feat: include an 'api' option to customize the slack api method url (#409)
This commit is contained in:
@@ -452,6 +452,26 @@ Behind the scenes, [automatic retries][retries] are handled with the
|
|||||||
[`@slack/web-api`][slack-web-api] package for Slack API methods, and
|
[`@slack/web-api`][slack-web-api] package for Slack API methods, and
|
||||||
[`axios-retry`][axios-retry] when sending with a webhook.
|
[`axios-retry`][axios-retry] when sending with a webhook.
|
||||||
|
|
||||||
|
### Sending to a custom API URL
|
||||||
|
|
||||||
|
In certain circumstances, such as testing the sent payload, a
|
||||||
|
[custom API URL][custom-api-url] can be used to change where `method` requests
|
||||||
|
are sent:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Send to a custom API URL
|
||||||
|
uses: slackapi/[email protected]
|
||||||
|
with:
|
||||||
|
api: http://localhost:8080
|
||||||
|
method: chat.postMessage
|
||||||
|
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||||
|
payload: |
|
||||||
|
channel: ${{ secrets.SLACK_CHANNEL_ID }}
|
||||||
|
text: "What's happening on localhost?"
|
||||||
|
```
|
||||||
|
|
||||||
|
The default value of `api` is "https://slack.com/api/" for steps using `method`.
|
||||||
|
|
||||||
## Expected outputs
|
## Expected outputs
|
||||||
|
|
||||||
Each technique above [outputs values][github-outputs] that can be used as inputs
|
Each technique above [outputs values][github-outputs] that can be used as inputs
|
||||||
@@ -517,6 +537,7 @@ All contributions are encouraged! Check out the
|
|||||||
[config-tokens]: https://api.slack.com/reference/manifests#config-tokens
|
[config-tokens]: https://api.slack.com/reference/manifests#config-tokens
|
||||||
[contributing]: .github/contributing.md
|
[contributing]: .github/contributing.md
|
||||||
[conversation]: https://api.slack.com/types/conversation
|
[conversation]: https://api.slack.com/types/conversation
|
||||||
|
[custom-api-url]: https://tools.slack.dev/node-slack-sdk/web-api/#custom-api-url
|
||||||
[epoch]: https://en.wikipedia.org/wiki/Unix_time
|
[epoch]: https://en.wikipedia.org/wiki/Unix_time
|
||||||
[event-context]: https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts#L6
|
[event-context]: https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts#L6
|
||||||
[event-payload]: https://docs.github.com/en/webhooks/webhook-events-and-payloads
|
[event-payload]: https://docs.github.com/en/webhooks/webhook-events-and-payloads
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ name: "Slack: Send to Slack"
|
|||||||
author: "slackapi"
|
author: "slackapi"
|
||||||
description: "Send data to Slack to start a Slack workflow in Workflow Builder, call a Slack API method, or post a message into a channel"
|
description: "Send data to Slack to start a Slack workflow in Workflow Builder, call a Slack API method, or post a message into a channel"
|
||||||
inputs:
|
inputs:
|
||||||
|
api:
|
||||||
|
description: "A custom API URL to send Slack API method requests to."
|
||||||
|
required: false
|
||||||
errors:
|
errors:
|
||||||
default: "false"
|
default: "false"
|
||||||
description: "If the step exits with an error on errors or continues."
|
description: "If the step exits with an error on errors or continues."
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export default class Client {
|
|||||||
agent: this.proxies(config)?.httpsAgent,
|
agent: this.proxies(config)?.httpsAgent,
|
||||||
logger: config.logger,
|
logger: config.logger,
|
||||||
retryConfig: this.retries(config.inputs.retries),
|
retryConfig: this.retries(config.inputs.retries),
|
||||||
|
slackApiUrl: config.inputs.api || undefined,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default class Config {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Inputs - Values provided to this job.
|
* @typedef Inputs - Values provided to this job.
|
||||||
|
* @property {string?} api - A custom API URL to send method requests to.
|
||||||
* @property {boolean} errors - If the job should exit after errors or succeed.
|
* @property {boolean} errors - If the job should exit after errors or succeed.
|
||||||
* @property {string?} method - The Slack API method to call.
|
* @property {string?} method - The Slack API method to call.
|
||||||
* @property {string?} payload - Request contents from the provided input.
|
* @property {string?} payload - Request contents from the provided input.
|
||||||
@@ -101,6 +102,7 @@ export default class Config {
|
|||||||
this.logger = new Logger(core).logger;
|
this.logger = new Logger(core).logger;
|
||||||
this.webapi = webapi;
|
this.webapi = webapi;
|
||||||
this.inputs = {
|
this.inputs = {
|
||||||
|
api: core.getInput("api"),
|
||||||
errors: core.getBooleanInput("errors"),
|
errors: core.getBooleanInput("errors"),
|
||||||
method: core.getInput("method"),
|
method: core.getInput("method"),
|
||||||
payload: core.getInput("payload"),
|
payload: core.getInput("payload"),
|
||||||
|
|||||||
+15
-13
@@ -73,6 +73,7 @@ describe("client", () => {
|
|||||||
core: core,
|
core: core,
|
||||||
logger: new Logger(core).logger,
|
logger: new Logger(core).logger,
|
||||||
inputs: {
|
inputs: {
|
||||||
|
api: "http://localhost:8080/api",
|
||||||
method: "pins.add",
|
method: "pins.add",
|
||||||
retries: "10",
|
retries: "10",
|
||||||
token: "xoxb-example-002",
|
token: "xoxb-example-002",
|
||||||
@@ -86,6 +87,7 @@ describe("client", () => {
|
|||||||
agent: undefined,
|
agent: undefined,
|
||||||
logger: config.logger,
|
logger: config.logger,
|
||||||
retryConfig: webapi.retryPolicies.tenRetriesInAboutThirtyMinutes,
|
retryConfig: webapi.retryPolicies.tenRetriesInAboutThirtyMinutes,
|
||||||
|
slackApiUrl: "http://localhost:8080/api",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -110,10 +112,10 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
||||||
mocks.api.resolves(response);
|
mocks.calls.resolves(response);
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.deepEqual(mocks.api.getCall(0).firstArg, "chat.postMessage");
|
assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage");
|
||||||
assert.deepEqual(mocks.api.getCall(0).lastArg, args);
|
assert.deepEqual(mocks.calls.getCall(0).lastArg, args);
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
||||||
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
||||||
@@ -158,10 +160,10 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
||||||
mocks.api.resolves(response);
|
mocks.calls.resolves(response);
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.deepEqual(mocks.api.getCall(0).firstArg, "chat.postMessage");
|
assert.deepEqual(mocks.calls.getCall(0).firstArg, "chat.postMessage");
|
||||||
assert.deepEqual(mocks.api.getCall(0).lastArg, args);
|
assert.deepEqual(mocks.calls.getCall(0).lastArg, args);
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
||||||
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
||||||
@@ -194,9 +196,9 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("files.uploadV2");
|
mocks.core.getInput.withArgs("method").returns("files.uploadV2");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxp-example");
|
mocks.core.getInput.withArgs("token").returns("xoxp-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
mocks.core.getInput.withArgs("payload").returns(JSON.stringify(args));
|
||||||
mocks.api.resolves(response);
|
mocks.calls.resolves(response);
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.deepEqual(mocks.api.getCall(0).lastArg, args);
|
assert.deepEqual(mocks.calls.getCall(0).lastArg, args);
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
assert.equal(mocks.core.setOutput.getCall(0).firstArg, "ok");
|
||||||
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
assert.equal(mocks.core.setOutput.getCall(0).lastArg, true);
|
||||||
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
assert.equal(mocks.core.setOutput.getCall(1).firstArg, "response");
|
||||||
@@ -231,7 +233,7 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
||||||
mocks.api.rejects(errors.requestErrorWithOriginal(response, true));
|
mocks.calls.rejects(errors.requestErrorWithOriginal(response, true));
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.fail("Expected an error but none was found");
|
assert.fail("Expected an error but none was found");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -266,7 +268,7 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
||||||
mocks.api.rejects(errors.httpErrorFromResponse(response));
|
mocks.calls.rejects(errors.httpErrorFromResponse(response));
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.fail("Expected an error but none was found");
|
assert.fail("Expected an error but none was found");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -302,7 +304,7 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
||||||
mocks.api.rejects(errors.platformErrorFromResult(response));
|
mocks.calls.rejects(errors.platformErrorFromResult(response));
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.fail("Expected an error but none was found");
|
assert.fail("Expected an error but none was found");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -331,7 +333,7 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
||||||
mocks.api.rejects(errors.platformErrorFromResult(response));
|
mocks.calls.rejects(errors.platformErrorFromResult(response));
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.fail("Expected an error but none was found");
|
assert.fail("Expected an error but none was found");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -357,7 +359,7 @@ describe("client", () => {
|
|||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
mocks.core.getInput.withArgs("payload").returns(`"text": "hello"`);
|
||||||
mocks.api.rejects(errors.rateLimitedErrorWithDelay(12));
|
mocks.calls.rejects(errors.rateLimitedErrorWithDelay(12));
|
||||||
await send(mocks.core);
|
await send(mocks.core);
|
||||||
assert.fail("Expected an error but none was found");
|
assert.fail("Expected an error but none was found");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ describe("config", () => {
|
|||||||
|
|
||||||
describe("inputs", () => {
|
describe("inputs", () => {
|
||||||
it("valid values are collected from the action inputs", async () => {
|
it("valid values are collected from the action inputs", async () => {
|
||||||
|
mocks.core.getInput.withArgs("api").returns("http://localhost:8080");
|
||||||
mocks.core.getBooleanInput.withArgs("errors").returns(true);
|
mocks.core.getBooleanInput.withArgs("errors").returns(true);
|
||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("payload").returns('"hello": "world"');
|
mocks.core.getInput.withArgs("payload").returns('"hello": "world"');
|
||||||
@@ -26,6 +27,7 @@ describe("config", () => {
|
|||||||
mocks.core.getInput.withArgs("retries").returns("0");
|
mocks.core.getInput.withArgs("retries").returns("0");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
const config = new Config(mocks.core);
|
const config = new Config(mocks.core);
|
||||||
|
assert.equal(config.inputs.api, "http://localhost:8080");
|
||||||
assert.equal(config.inputs.errors, true);
|
assert.equal(config.inputs.errors, true);
|
||||||
assert.equal(config.inputs.method, "chat.postMessage");
|
assert.equal(config.inputs.method, "chat.postMessage");
|
||||||
assert.equal(config.inputs.payload, '"hello": "world"');
|
assert.equal(config.inputs.payload, '"hello": "world"');
|
||||||
|
|||||||
+2
-2
@@ -43,8 +43,8 @@ export class Mock {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.sandbox = sinon.createSandbox();
|
this.sandbox = sinon.createSandbox();
|
||||||
this.api = sinon.stub(webapi.WebClient.prototype, "apiCall");
|
|
||||||
this.axios = this.sandbox.stub(axios);
|
this.axios = this.sandbox.stub(axios);
|
||||||
|
this.calls = sinon.stub(webapi.WebClient.prototype, "apiCall");
|
||||||
this.core = this.sandbox.stub(core);
|
this.core = this.sandbox.stub(core);
|
||||||
this.fs = this.sandbox.stub(fs);
|
this.fs = this.sandbox.stub(fs);
|
||||||
this.webapi = {
|
this.webapi = {
|
||||||
@@ -63,8 +63,8 @@ export class Mock {
|
|||||||
*/
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
this.sandbox.reset();
|
this.sandbox.reset();
|
||||||
this.api.resetHistory();
|
|
||||||
this.axios.post.resetHistory();
|
this.axios.post.resetHistory();
|
||||||
|
this.calls.resetHistory();
|
||||||
this.core.getInput.reset();
|
this.core.getInput.reset();
|
||||||
this.core.getInput.withArgs("errors").returns("false");
|
this.core.getInput.withArgs("errors").returns("false");
|
||||||
this.core.getInput.withArgs("retries").returns("5");
|
this.core.getInput.withArgs("retries").returns("5");
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ describe("send", () => {
|
|||||||
|
|
||||||
it("token", async () => {
|
it("token", async () => {
|
||||||
process.env.SLACK_WEBHOOK_URL = "https://example.com"; // https://github.com/slackapi/slack-github-action/issues/373
|
process.env.SLACK_WEBHOOK_URL = "https://example.com"; // https://github.com/slackapi/slack-github-action/issues/373
|
||||||
mocks.api.resolves({ ok: true });
|
mocks.calls.resolves({ ok: true });
|
||||||
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
mocks.core.getInput.withArgs("method").returns("chat.postMessage");
|
||||||
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
mocks.core.getInput.withArgs("token").returns("xoxb-example");
|
||||||
mocks.core.getInput.withArgs("payload").returns('"text": "hello"');
|
mocks.core.getInput.withArgs("payload").returns('"text": "hello"');
|
||||||
|
|||||||
Reference in New Issue
Block a user