Merge pull request #20622 from mondragonda/sentry-scaffolder-action-examples
Added examples for `sentry:project:create` action and unit tests.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-sentry': patch
|
||||
---
|
||||
|
||||
Added examples for `sentry:project:create` scaffolder action and unit tests.
|
||||
@@ -30,10 +30,14 @@
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-node": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-node": "workspace:^",
|
||||
"yaml": "^2.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"msw": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Creates a Sentry project with the specified parameters.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'create-sentry-project',
|
||||
action: 'sentry:project:create',
|
||||
name: 'Create a Sentry project with provided project slug.',
|
||||
input: {
|
||||
organizationSlug: 'my-org',
|
||||
teamSlug: 'team-a',
|
||||
name: 'Scaffolded project A',
|
||||
slug: 'scaff-proj-a',
|
||||
authToken:
|
||||
'a14711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d5df96',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'create-sentry-project',
|
||||
action: 'sentry:project:create',
|
||||
name: 'Create a Sentry project without providing a project slug.',
|
||||
input: {
|
||||
organizationSlug: 'my-org',
|
||||
teamSlug: 'team-b',
|
||||
name: 'Scaffolded project B',
|
||||
authToken:
|
||||
'b15711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d4gf93',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { setupRequestMockHandlers } from '@backstage/backend-test-utils';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { ActionContext } from '@backstage/plugin-scaffolder-node';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { createSentryCreateProjectAction } from './createProject';
|
||||
|
||||
describe('sentry:project:create action', () => {
|
||||
const worker = setupServer();
|
||||
setupRequestMockHandlers(worker);
|
||||
|
||||
const createScaffolderConfig = (configData: JsonObject = {}) => ({
|
||||
config: new ConfigReader({
|
||||
scaffolder: {
|
||||
...configData,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const getActionContext = (): ActionContext<{
|
||||
organizationSlug: string;
|
||||
teamSlug: string;
|
||||
name: string;
|
||||
slug?: string;
|
||||
authToken?: string;
|
||||
}> => ({
|
||||
workspacePath: './dev/proj',
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
logger: jest.createMockFromModule('winston'),
|
||||
logStream: jest.createMockFromModule('stream'),
|
||||
input: {
|
||||
organizationSlug: 'org',
|
||||
teamSlug: 'team',
|
||||
name: 'test project',
|
||||
authToken: randomBytes(5).toString('hex'),
|
||||
},
|
||||
output: jest.fn(),
|
||||
});
|
||||
|
||||
it('should request sentry project create with specified parameters.', async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
const action = createSentryCreateProjectAction(createScaffolderConfig());
|
||||
const actionContext = getActionContext();
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async ({ request }) => {
|
||||
expect(request.headers.get('Authorization')).toBe(
|
||||
`Bearer ${actionContext.input.authToken}`,
|
||||
);
|
||||
expect(request.headers.get('Content-Type')).toBe(`application/json`);
|
||||
await expect(request.json()).resolves.toEqual({
|
||||
name: actionContext.input.name,
|
||||
});
|
||||
return HttpResponse.json(
|
||||
{ detail: 'project creation mocked result' },
|
||||
{ status: 201 },
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler(actionContext);
|
||||
});
|
||||
|
||||
it('should request sentry project create with added optional specified project slug', async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
const action = createSentryCreateProjectAction(createScaffolderConfig());
|
||||
const actionContext = getActionContext();
|
||||
actionContext.input = { ...actionContext.input, slug: 'project-slug' };
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async ({ request }) => {
|
||||
expect(request.headers.get('Authorization')).toBe(
|
||||
`Bearer ${actionContext.input.authToken}`,
|
||||
);
|
||||
expect(request.headers.get('Content-Type')).toBe(`application/json`);
|
||||
await expect(request.json()).resolves.toEqual({
|
||||
name: actionContext.input.name,
|
||||
slug: actionContext.input.slug,
|
||||
});
|
||||
return HttpResponse.json(
|
||||
{ detail: 'project creation mocked result' },
|
||||
{ status: 201 },
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler(actionContext);
|
||||
});
|
||||
|
||||
it('should take Sentry auth token from scaffolder config when input authToken is missing.', async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
const sentryScaffolderConfigToken = randomBytes(5).toString('hex');
|
||||
const action = createSentryCreateProjectAction(
|
||||
createScaffolderConfig({
|
||||
sentry: {
|
||||
token: sentryScaffolderConfigToken,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const actionContext = getActionContext();
|
||||
actionContext.input.authToken = undefined;
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async ({ request }) => {
|
||||
expect(request.headers.get('Authorization')).toBe(
|
||||
`Bearer ${sentryScaffolderConfigToken}`,
|
||||
);
|
||||
expect(request.headers.get('Content-Type')).toBe(`application/json`);
|
||||
await expect(request.json()).resolves.toEqual({
|
||||
name: actionContext.input.name,
|
||||
});
|
||||
return HttpResponse.json(
|
||||
{ detail: 'project creation mocked result' },
|
||||
{ status: 201 },
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler(actionContext);
|
||||
});
|
||||
|
||||
it('should throw InputError when auth token is missing from input parameters and scaffolder config.', async () => {
|
||||
const action = createSentryCreateProjectAction(createScaffolderConfig());
|
||||
const actionContext = getActionContext();
|
||||
actionContext.input.authToken = undefined;
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async ({ request }) => {
|
||||
expect(request.headers.get('Authorization')).toBe(
|
||||
`Bearer ${actionContext.input.authToken}`,
|
||||
);
|
||||
expect(request.headers.get('Content-Type')).toBe(`application/json`);
|
||||
await expect(request.json()).resolves.toEqual({
|
||||
name: actionContext.input.name,
|
||||
});
|
||||
return HttpResponse.json(
|
||||
{ detail: 'project creation mocked result' },
|
||||
{ status: 201 },
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(() => action.handler(actionContext)).rejects.toThrow(
|
||||
new InputError('No valid sentry token given'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw InputError when sentry API returns unexpected content-type.', async () => {
|
||||
const action = createSentryCreateProjectAction(createScaffolderConfig());
|
||||
const actionContext = getActionContext();
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async () => {
|
||||
return HttpResponse.text('Bad response', { status: 201 });
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(() => action.handler(actionContext)).rejects.toThrow(
|
||||
new InputError(`Unexpected Sentry Response Type: Bad response`),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw InputError when sentry API returns unexpected status code.', async () => {
|
||||
const action = createSentryCreateProjectAction(createScaffolderConfig());
|
||||
const actionContext = getActionContext();
|
||||
|
||||
worker.use(
|
||||
http.post(
|
||||
`https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`,
|
||||
async () => {
|
||||
return HttpResponse.json({ detail: 'OUCH' }, { status: 400 });
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(() => action.handler(actionContext)).rejects.toThrow(
|
||||
new InputError(`Sentry Response was: OUCH`),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -19,7 +19,7 @@ import { InputError } from '@backstage/errors';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* Creates the `sentry:craete-project` Scaffolder action.
|
||||
* Creates the `sentry:project:create` Scaffolder action.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
|
||||
@@ -8658,10 +8658,14 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-scaffolder-backend-module-sentry@workspace:plugins/scaffolder-backend-module-sentry"
|
||||
dependencies:
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-node": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
msw: ^2.0.0
|
||||
yaml: ^2.3.3
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@@ -34764,7 +34768,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"msw@npm:^2.0.8":
|
||||
"msw@npm:^2.0.0, msw@npm:^2.0.8":
|
||||
version: 2.0.9
|
||||
resolution: "msw@npm:2.0.9"
|
||||
dependencies:
|
||||
@@ -45031,7 +45035,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yaml@npm:2.3.4, yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2":
|
||||
"yaml@npm:2.3.4, yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3":
|
||||
version: 2.3.4
|
||||
resolution: "yaml@npm:2.3.4"
|
||||
checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8
|
||||
|
||||
Reference in New Issue
Block a user