Skip to content

Commit bb058b3

Browse files
authored
feat: migrate to Zod schema and drop legacy support (#11)
* feat: migrate to Zod schema and drop legacy support Signed-off-by: Gaurav Pandey <grvpandey11@gmail.com> * chore: update workflow Signed-off-by: Gaurav Pandey <grvpandey11@gmail.com> * chore: release 3.0.0 Signed-off-by: Gaurav Pandey <grvpandey11@gmail.com> --------- Signed-off-by: Gaurav Pandey <grvpandey11@gmail.com>
1 parent 17a6d50 commit bb058b3

File tree

7 files changed

+23249
-16752
lines changed

7 files changed

+23249
-16752
lines changed

.github/workflows/npm-publish.yaml

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,35 @@ on:
44
release:
55
types: [created]
66

7+
permissions:
8+
contents: read
9+
710
jobs:
8-
build:
11+
build-and-publish:
912
runs-on: ubuntu-latest
1013
steps:
11-
- uses: actions/checkout@v4
12-
- uses: actions/setup-node@v4
13-
with:
14-
node-version: 20
15-
- run: yarn install --frozen-lockfile
16-
- run: yarn test
14+
- name: Checkout code
15+
uses: actions/checkout@v4
1716

18-
publish-npm:
19-
needs: build
20-
runs-on: ubuntu-latest
21-
steps:
22-
- uses: actions/checkout@v4
23-
- uses: actions/setup-node@v4
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
2419
with:
2520
node-version: 20
2621
registry-url: https://registry.npmjs.org/
27-
- run: yarn install --frozen-lockfile
28-
- run: yarn tsc
29-
- run: yarn build
30-
- run: yarn publish
22+
23+
- name: Install dependencies
24+
run: yarn install
25+
26+
- name: Compile TypeScript
27+
run: yarn tsc
28+
29+
- name: Run tests
30+
run: yarn test
31+
32+
- name: Build project
33+
run: yarn build
34+
35+
- name: Publish to NPM
36+
run: yarn publish
3137
env:
32-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
38+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

package.json

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
{
22
"name": "@grvpandey11/backstage-plugin-scaffolder-backend-module-ms-teams",
33
"description": "The Microsoft Teams module for @backstage/plugin-scaffolder-backend",
4-
"version": "2.0.0",
4+
"version": "3.0.0",
55
"main": "src/index.ts",
66
"types": "src/index.ts",
77
"license": "Apache-2.0",
8-
"private": false,
98
"repository": {
109
"type": "git",
1110
"url": "https://github.com/grvpandey11/backstage-plugin-scaffolder-backend-module-ms-teams.git",
@@ -31,17 +30,17 @@
3130
"postpack": "backstage-cli package postpack"
3231
},
3332
"dependencies": {
34-
"@backstage/backend-plugin-api": "^1.1.0",
35-
"@backstage/config": "^1.3.1",
36-
"@backstage/errors": "^1.2.6",
37-
"@backstage/plugin-scaffolder-node": "^0.6.2",
38-
"axios": "^1.7.9",
39-
"yaml": "^2.6.1"
33+
"@backstage/backend-plugin-api": "^1.4.0",
34+
"@backstage/config": "^1.3.2",
35+
"@backstage/errors": "^1.2.7",
36+
"@backstage/plugin-scaffolder-node": "^0.9.0",
37+
"axios": "^1.10.0",
38+
"yaml": "^2.8.0",
39+
"zod": "^3.25.67"
4040
},
4141
"devDependencies": {
42-
"@backstage/cli": "^0.29.4",
43-
"@backstage/plugin-scaffolder-node-test-utils": "^0.1.17",
44-
"winston": "^3.17.0"
42+
"@backstage/cli": "^0.33.0",
43+
"@backstage/plugin-scaffolder-node-test-utils": "^0.3.0"
4544
},
4645
"files": [
4746
"dist"

src/actions/ms-teams/send-ms-teams-message.examples.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
2-
import yaml from 'yaml';
2+
import * as yaml from 'yaml';
33

44
export const examples: TemplateExample[] = [
55
{
6-
description: 'Send a simple message to Microsoft Teams',
6+
description: 'Sends a simple message to Microsoft Teams using the configured webhook URL.',
77
example: yaml.stringify({
88
steps: [
99
{
10-
id: 'ms-teams:sendMessage',
11-
action: 'ms-teams:sendMessage',
10+
id: 'send-message',
1211
name: 'Send a message to Teams',
12+
action: 'ms-teams:sendMessage',
1313
input: {
1414
message: 'Hello, Teams!',
1515
},
@@ -18,13 +18,13 @@ export const examples: TemplateExample[] = [
1818
}),
1919
},
2020
{
21-
description: 'Send a message to Microsoft Teams with a specific webhook URL',
21+
description: 'Sends a message to Microsoft Teams using a specific webhook URL provided in input.',
2222
example: yaml.stringify({
2323
steps: [
2424
{
25-
id: 'ms-teams:sendMessage',
25+
id: 'send-message',
26+
name: 'Send a message to Teams with explicit webhook',
2627
action: 'ms-teams:sendMessage',
27-
name: 'Send a message to Teams',
2828
input: {
2929
message: 'Hello, Teams!',
3030
webhookUrl: 'https://example-teams-webhook.com',
Lines changed: 71 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,102 @@
1-
import { PassThrough } from 'stream';
21
import { createSendTeamsMessageViaWebhookAction } from './send-ms-teams-message';
3-
import * as winston from 'winston';
4-
import { Config } from '@backstage/config';
2+
import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';
53
import axios from 'axios';
4+
import { Config } from '@backstage/config';
65

76
jest.mock('axios');
8-
97
const mockedAxios = axios as jest.Mocked<typeof axios>;
108

11-
const defaultHandlerOptions = {
12-
workspacePath: '/tmp',
13-
logStream: new PassThrough(),
14-
output: jest.fn(),
15-
createTemporaryDirectory() {
16-
throw new Error('Not implemented');
17-
},
18-
checkpoint: jest.fn(),
19-
getInitiatorCredentials: jest.fn(),
20-
};
21-
229
describe('ms-teams:sendMessage', () => {
23-
beforeEach(() => {});
10+
afterEach(() => {
11+
jest.resetAllMocks();
12+
});
2413

2514
it('should throw error if webhookUrl is not defined', async () => {
2615
const action = createSendTeamsMessageViaWebhookAction({
2716
config: {
28-
getOptionalString: (_key: string): string | undefined => undefined,
17+
getOptionalString: (_key: string) => undefined,
2918
} as Config,
3019
});
3120

32-
const logger = {} as winston.Logger;
33-
34-
try {
35-
await action.handler({
36-
...defaultHandlerOptions,
37-
input: {
38-
message: 'Hello, Teams!',
39-
},
40-
logger,
41-
});
42-
throw new Error('This should not succeed');
43-
} catch (err: any) {
44-
// eslint-disable-next-line jest/no-conditional-expect
45-
expect(err.message).toContain(
46-
'Webhook URL is not specified in either the app-config or the action input. This must be specified in at least one place in order to send a message',
47-
);
48-
}
21+
await expect(
22+
action.handler(
23+
createMockActionContext({
24+
input: {
25+
message: 'Hello, Teams!',
26+
},
27+
}),
28+
),
29+
).rejects.toThrow(
30+
'Webhook URL is not specified in either the app-config or the action input. This must be specified in at least one place in order to send a message',
31+
);
4932
});
5033

5134
it('should send to config webhook URL if provided', async () => {
5235
const action = createSendTeamsMessageViaWebhookAction({
5336
config: {
54-
getOptionalString: (_key: string): string | undefined =>
55-
'https://example-teams.com',
37+
getOptionalString: (_key: string) => 'https://example-teams.com',
5638
} as Config,
5739
});
5840

5941
mockedAxios.post.mockResolvedValue({ status: 200 });
6042

61-
const logger = {} as winston.Logger;
62-
63-
await action.handler({
64-
...defaultHandlerOptions,
65-
input: {
66-
message: 'Hello, Teams!',
67-
},
68-
logger,
69-
});
43+
await action.handler(
44+
createMockActionContext({
45+
input: {
46+
message: 'Hello, Teams!',
47+
},
48+
}),
49+
);
7050

71-
expect(axios.post).toHaveBeenCalledWith(
51+
expect(mockedAxios.post).toHaveBeenCalledWith(
7252
'https://example-teams.com',
7353
expect.anything(),
7454
);
7555
});
7656

77-
it('should prefer webhook url from config if provided in input', async () => {
57+
it('should prefer webhook url from config even if provided in input', async () => {
7858
const action = createSendTeamsMessageViaWebhookAction({
7959
config: {
80-
getOptionalString: (_key: string): string | undefined =>
81-
'https://example-teams.com',
60+
getOptionalString: (_key: string) => 'https://example-teams.com',
8261
} as Config,
8362
});
8463

8564
mockedAxios.post.mockResolvedValue({ status: 200 });
8665

87-
const logger = {} as winston.Logger;
88-
89-
await action.handler({
90-
...defaultHandlerOptions,
91-
input: {
92-
message: 'Hello, Teams!',
93-
webhookUrl: 'https://dontusethis-teams.com',
94-
},
95-
logger,
96-
});
66+
await action.handler(
67+
createMockActionContext({
68+
input: {
69+
message: 'Hello, Teams!',
70+
webhookUrl: 'https://input-teams.com',
71+
},
72+
}),
73+
);
9774

98-
expect(axios.post).toHaveBeenCalledWith(
75+
expect(mockedAxios.post).toHaveBeenCalledWith(
9976
'https://example-teams.com',
10077
expect.anything(),
10178
);
10279
});
10380

104-
it('should use the webhook url on input if config value is not present', async () => {
81+
it('should use webhookUrl from input if config value is not present', async () => {
10582
const action = createSendTeamsMessageViaWebhookAction({
10683
config: {
107-
getOptionalString: (_key: string): string | undefined => undefined,
84+
getOptionalString: (_key: string) => undefined,
10885
} as Config,
10986
});
11087

11188
mockedAxios.post.mockResolvedValue({ status: 200 });
11289

113-
const logger = {} as winston.Logger;
114-
115-
await action.handler({
116-
...defaultHandlerOptions,
117-
input: {
118-
message: 'Hello, Teams!',
119-
webhookUrl: 'https://nevergonnagiveyouup-teams.com',
120-
},
121-
logger,
122-
});
90+
await action.handler(
91+
createMockActionContext({
92+
input: {
93+
message: 'Hello, Teams!',
94+
webhookUrl: 'https://nevergonnagiveyouup-teams.com',
95+
},
96+
}),
97+
);
12398

124-
expect(axios.post).toHaveBeenCalledWith(
99+
expect(mockedAxios.post).toHaveBeenCalledWith(
125100
'https://nevergonnagiveyouup-teams.com',
126101
expect.anything(),
127102
);
@@ -130,25 +105,22 @@ describe('ms-teams:sendMessage', () => {
130105
it('should send message in proper format to webhook URL', async () => {
131106
const action = createSendTeamsMessageViaWebhookAction({
132107
config: {
133-
getOptionalString: (_key: string): string | undefined =>
134-
'https://example-teams.com',
108+
getOptionalString: (_key: string) => 'https://example-teams.com',
135109
} as Config,
136110
});
137111

138112
mockedAxios.post.mockResolvedValue({ status: 200 });
139113

140-
const logger = {} as winston.Logger;
141-
142-
await action.handler({
143-
...defaultHandlerOptions,
144-
input: {
145-
message: 'Hello, Teams!',
146-
webhookUrl: 'https://dontusethis-teams.com',
147-
},
148-
logger,
149-
});
114+
await action.handler(
115+
createMockActionContext({
116+
input: {
117+
message: 'Hello, Teams!',
118+
webhookUrl: 'https://should-not-be-used.com',
119+
},
120+
}),
121+
);
150122

151-
expect(axios.post).toHaveBeenCalledWith(
123+
expect(mockedAxios.post).toHaveBeenCalledWith(
152124
'https://example-teams.com',
153125
expect.objectContaining({ text: 'Hello, Teams!' }),
154126
);
@@ -157,31 +129,23 @@ describe('ms-teams:sendMessage', () => {
157129
it('should throw an error if result.status is not 200', async () => {
158130
const action = createSendTeamsMessageViaWebhookAction({
159131
config: {
160-
getOptionalString: (_key: string): string | undefined =>
161-
'https://example-teams.com',
132+
getOptionalString: (_key: string) => 'https://example-teams.com',
162133
} as Config,
163134
});
164135

165136
mockedAxios.post.mockResolvedValue({ status: 400 });
166-
const logger = jest.fn() as unknown as winston.Logger;
167-
logger.error = jest.fn();
168-
logger.debug = jest.fn();
169137

170-
try {
171-
await action.handler({
172-
...defaultHandlerOptions,
173-
input: {
174-
message: 'Hello, Teams!',
175-
webhookUrl: 'https://dontusethis-teams.com',
176-
},
177-
logger,
178-
});
179-
expect(true).toBeFalsy();
180-
} catch (err: any) {
181-
// eslint-disable-next-line jest/no-conditional-expect
182-
expect(err.message).toContain(
183-
`Something went wrong while trying to send a request to the Teams webhook URL - StatusCode 400`,
184-
);
185-
}
138+
await expect(
139+
action.handler(
140+
createMockActionContext({
141+
input: {
142+
message: 'Hello, Teams!',
143+
webhookUrl: 'https://ignored.com',
144+
},
145+
}),
146+
),
147+
).rejects.toThrow(
148+
'Something went wrong while trying to send a request to the Teams webhook URL - StatusCode 400',
149+
);
186150
});
187151
});

0 commit comments

Comments
 (0)