From 698c55f1820cad93ad157959ac0c720c5886e764 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 31 May 2022 16:42:51 +0200 Subject: [PATCH] chore: code review changes Signed-off-by: blam --- .../examples/acme/team-a-group.yaml | 70 ------------ .../src/scaffolder/tasks/DatabaseTaskStore.ts | 8 +- .../tasks/StorageTaskBroker.test.ts | 8 +- .../src/scaffolder/tasks/StorageTaskBroker.ts | 9 +- .../src/scaffolder/tasks/types.ts | 4 +- .../src/service/router.test.ts | 84 ++++++++------ .../scaffolder-backend/src/service/router.ts | 15 ++- plugins/scaffolder/src/api.test.ts | 36 +++--- plugins/scaffolder/src/api.ts | 20 ++-- .../ListTasksPage/ListTaskPage.test.tsx | 104 ++++++++++-------- .../ListTasksPage/ListTasksPage.tsx | 10 +- .../ListTasksPage/OwnerListPicker.tsx | 6 +- plugins/scaffolder/src/index.ts | 1 - plugins/scaffolder/src/types.ts | 13 +-- yarn.lock | 83 ++++++++++++-- 15 files changed, 256 insertions(+), 215 deletions(-) delete mode 100644 packages/catalog-model/examples/acme/team-a-group.yaml diff --git a/packages/catalog-model/examples/acme/team-a-group.yaml b/packages/catalog-model/examples/acme/team-a-group.yaml deleted file mode 100644 index 2b3e2999d2..0000000000 --- a/packages/catalog-model/examples/acme/team-a-group.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Group -metadata: - name: team-a - description: Team A -spec: - type: team - profile: - # Intentional no displayName for testing - email: team-a@example.com - picture: https://avatars.dicebear.com/api/identicon/team-a@example.com.svg?background=%23fff&margin=25 - parent: backstage - children: [] ---- -apiVersion: backstage.io/v1alpha1 -kind: User -metadata: - name: breanna.davison -spec: - profile: - # Intentional no displayName for testing - email: breanna-davison@example.com - picture: https://avatars.dicebear.com/api/avataaars/breanna-davison@example.com.svg?background=%23fff - memberOf: [team-a] ---- -apiVersion: backstage.io/v1alpha1 -kind: User -metadata: - name: janelle.dawe -spec: - profile: - displayName: Janelle Dawe - email: janelle-dawe@example.com - picture: https://avatars.dicebear.com/api/avataaars/janelle-dawe@example.com.svg?background=%23fff - memberOf: [team-a] ---- -apiVersion: backstage.io/v1alpha1 -kind: User -metadata: - name: nigel.manning -spec: - profile: - displayName: Nigel Manning - email: nigel-manning@example.com - picture: https://avatars.dicebear.com/api/avataaars/nigel-manning@example.com.svg?background=%23fff - memberOf: [team-a] ---- -# This user is added as an example, to make it more easy for the "Guest" -# sign-in option to demonstrate some entities being owned. In a regular org, -# a guest user would probably not be registered like this. -apiVersion: backstage.io/v1alpha1 -kind: User -metadata: - name: guest -spec: - profile: - displayName: Guest User - email: guest@example.com - picture: https://avatars.dicebear.com/api/avataaars/guest@example.com.svg?background=%23fff - memberOf: [team-a] - ---- -apiVersion: backstage.io/v1alpha1 -kind: User -metadata: - name: benjdlambert -spec: - profile: - picture: https://avatars.dicebear.com/api/avataaars/breanna-davison@example.com.svg?background=%23fff - memberOf: [] diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 145747124a..d62e59744f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -93,7 +93,9 @@ export class DatabaseTaskStore implements TaskStore { this.db = options.database; } - async list(options: { createdBy?: string }): Promise { + async list(options: { + createdBy?: string; + }): Promise<{ tasks: SerializedTask[] }> { const queryBuilder = this.db('tasks'); if (options.createdBy) { @@ -104,7 +106,7 @@ export class DatabaseTaskStore implements TaskStore { const results = await queryBuilder.orderBy('created_at', 'desc').select(); - return results.map(result => ({ + const tasks = results.map(result => ({ id: result.id, spec: JSON.parse(result.spec), status: result.status, @@ -112,6 +114,8 @@ export class DatabaseTaskStore implements TaskStore { lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at), createdAt: parseSqlDateToIsoString(result.created_at), })); + + return { tasks }; } async getTask(taskId: string): Promise { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts index caca1580ad..2350072806 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts @@ -208,13 +208,13 @@ describe('StorageTaskBroker', () => { const { taskId } = await broker.dispatch({ spec: {} as TaskSpec }); const promise = broker.list(); - await expect(promise).resolves.toEqual( - expect.arrayContaining([ + await expect(promise).resolves.toEqual({ + tasks: expect.arrayContaining([ expect.objectContaining({ id: taskId, }), ]), - ); + }); }); it('should list only tasks createdBy a specific user', async () => { @@ -227,6 +227,6 @@ describe('StorageTaskBroker', () => { const task = await storage.getTask(taskId); const promise = broker.list({ createdBy: 'user:default/foo' }); - await expect(promise).resolves.toEqual([task]); + await expect(promise).resolves.toEqual({ tasks: [task] }); }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index fb0d673083..9e60bc7727 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -151,7 +151,14 @@ export class StorageTaskBroker implements TaskBroker { private readonly logger: Logger, ) {} - async list(options?: { createdBy?: string }): Promise { + async list(options?: { + createdBy?: string; + }): Promise<{ tasks: SerializedTask[] }> { + if (!this.storage.list) { + throw new Error( + 'TaskStore does not implement the list method. Please implement the list method to be able to list tasks', + ); + } return await this.storage.list({ createdBy: options?.createdBy }); } diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index a9d3e3ae5b..4b63a0cdd3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -133,7 +133,7 @@ export interface TaskBroker { after: number | undefined; }): Observable<{ events: SerializedTaskEvent[] }>; get(taskId: string): Promise; - list(options?: { createdBy?: string }): Promise; + list?(options?: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; } /** @@ -194,7 +194,7 @@ export interface TaskStore { listStaleTasks(options: { timeoutS: number }): Promise<{ tasks: { taskId: string }[]; }>; - list(options: { createdBy?: string }): Promise; + list?(options: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise; listEvents({ diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index b65c26949c..aea33f1158 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -292,42 +292,52 @@ describe('createRouter', () => { describe('GET /v2/tasks', () => { it('return all tasks', async () => { - (taskBroker.list as jest.Mocked['list']).mockResolvedValue([ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ]); + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + }); const response = await request(app).get(`/v2/tasks`); expect(taskBroker.list).toBeCalledWith({ createdBy: undefined, }); expect(response.status).toEqual(200); - expect(response.body).toStrictEqual([ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ]); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + }); }); it('return filtered tasks', async () => { - (taskBroker.list as jest.Mocked['list']).mockResolvedValue([ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ]); + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + }); const response = await request(app).get( `/v2/tasks?createdBy=user:default/foo`, @@ -337,15 +347,17 @@ describe('createRouter', () => { }); expect(response.status).toEqual(200); - expect(response.body).toStrictEqual([ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ]); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + }); }); }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 553ff2b8bc..4919d2e5f9 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -253,7 +253,20 @@ export async function createRouter( res.status(201).json({ id: result.taskId }); }) .get('/v2/tasks', async (req, res) => { - const userEntityRef = req.query.createdBy?.toString(); + const [userEntityRef] = [req.query.createdBy].flat(); + + if ( + typeof userEntityRef !== 'string' && + typeof userEntityRef !== 'undefined' + ) { + throw new InputError('createdBy query parameter must be a string'); + } + + if (!taskBroker.list) { + throw new Error( + 'TaskBroker does not support listing tasks, please implement the list method on the TaskBroker.', + ); + } const tasks = await taskBroker.list({ createdBy: userEntityRef, diff --git a/plugins/scaffolder/src/api.test.ts b/plugins/scaffolder/src/api.test.ts index 7dc840ee96..583258c069 100644 --- a/plugins/scaffolder/src/api.test.ts +++ b/plugins/scaffolder/src/api.test.ts @@ -347,7 +347,7 @@ describe('api', () => { }), ); - const result = await apiClient.listTasks({ createdBy: 'all' }); + const result = await apiClient.listTasks({ filterByOwnership: 'all' }); expect(result).toHaveLength(2); }); it('should list task using the current user as owner', async () => { @@ -357,23 +357,27 @@ describe('api', () => { if (createdBy) { return res( - ctx.json([ - { - createdBy, - }, - ]), + ctx.json({ + tasks: [ + { + createdBy, + }, + ], + }), ); } return res( - ctx.json([ - { - createdBy: null, - }, - { - createdBy: null, - }, - ]), + ctx.json({ + tasks: [ + { + createdBy: null, + }, + { + createdBy: null, + }, + ], + }), ); }), ); @@ -382,9 +386,9 @@ describe('api', () => { userEntityRef: 'user:default/foo', }); - const result = await apiClient.listTasks({ createdBy: 'owned' }); + const result = await apiClient.listTasks({ filterByOwnership: 'owned' }); expect(identityApi.getBackstageIdentity).toBeCalled(); - expect(result).toHaveLength(1); + expect(result.tasks).toHaveLength(1); }); }); }); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 6d236bb8fd..64141dd83d 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -37,7 +37,6 @@ import { ScaffolderGetIntegrationsListOptions, ScaffolderGetIntegrationsListResponse, ScaffolderTask, - TasksOwnerFilterKind, ScaffolderDryRunOptions, ScaffolderDryRunResponse, } from './types'; @@ -61,13 +60,13 @@ export class ScaffolderClient implements ScaffolderApi { private readonly discoveryApi: DiscoveryApi; private readonly scmIntegrationsApi: ScmIntegrationRegistry; private readonly fetchApi: FetchApi; - private readonly identityApi: IdentityApi; + private readonly identityApi?: IdentityApi; private readonly useLongPollingLogs: boolean; constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi; - identityApi: IdentityApi; + identityApi?: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; useLongPollingLogs?: boolean; }) { @@ -79,18 +78,21 @@ export class ScaffolderClient implements ScaffolderApi { } async listTasks(options: { - createdBy: TasksOwnerFilterKind; - }): Promise { + filterByOwnership: 'owned' | 'all'; + }): Promise<{ tasks: ScaffolderTask[] }> { + if (!this.identityApi) { + throw new Error( + 'IdentityApi is not available in the ScaffolderClient, please pass through the IdentityApi to the ScaffolderClient constructor in order to use the listTasks method', + ); + } const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); const { userEntityRef } = await this.identityApi.getBackstageIdentity(); const query = queryString.stringify( - options.createdBy === 'owned' ? { createdBy: userEntityRef } : {}, + options.filterByOwnership === 'owned' ? { createdBy: userEntityRef } : {}, ); - const url = `${baseUrl}/v2/tasks?${query}`; - - const response = await this.fetchApi.fetch(url); + const response = await this.fetchApi.fetch(`${baseUrl}/v2/tasks?${query}`); if (!response.ok) { throw await ResponseError.fromResponse(response); } diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx index 3d20fd1fd8..6dc26abd68 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx @@ -62,7 +62,7 @@ describe('', () => { }; catalogApi.getEntityByRef.mockResolvedValue(entity); - scaffolderApiMock.listTasks.mockResolvedValue([]); + scaffolderApiMock.listTasks.mockResolvedValue({ tasks: [] }); const { getByText } = await renderInTestApp( ', () => { }, }; catalogApi.getEntityByRef.mockResolvedValue(entity); - scaffolderApiMock.listTasks.mockResolvedValue([ - { - id: 'a-random-id', - spec: { - user: { ref: 'user:default/foo' }, - templateInfo: { - entityRef: 'template:default/test', - }, - } as any, - status: 'completed', - createdAt: '', - lastHeartbeatAt: '', - }, - ]); + scaffolderApiMock.listTasks.mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: { + user: { ref: 'user:default/foo' }, + templateInfo: { + entityRef: 'template:default/test', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }); scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ title: 'One Template', @@ -139,7 +141,9 @@ describe('', () => { }, ); - expect(scaffolderApiMock.listTasks).toBeCalledWith({ createdBy: 'owned' }); + expect(scaffolderApiMock.listTasks).toBeCalledWith({ + filterByOwnership: 'owned', + }); expect(getByText('List template tasks')).toBeInTheDocument(); expect(getByText('All tasks that have been started')).toBeInTheDocument(); expect(getByText('Tasks')).toBeInTheDocument(); @@ -173,36 +177,40 @@ describe('', () => { }); scaffolderApiMock.listTasks - .mockResolvedValue([ - { - id: 'a-random-id', - spec: { - user: { ref: 'user:default/foo' }, - templateInfo: { - entityRef: 'template:default/mock', - }, - } as any, - status: 'completed', - createdAt: '', - lastHeartbeatAt: '', - }, - ]) - .mockResolvedValue([ - { - id: 'b-random-id', - spec: { - templateInfo: { - entityRef: 'template:default/mock', - }, - user: { - ref: 'user:default/boo', - }, - } as any, - status: 'completed', - createdAt: '', - lastHeartbeatAt: '', - }, - ]); + .mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: { + user: { ref: 'user:default/foo' }, + templateInfo: { + entityRef: 'template:default/mock', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }) + .mockResolvedValue({ + tasks: [ + { + id: 'b-random-id', + spec: { + templateInfo: { + entityRef: 'template:default/mock', + }, + user: { + ref: 'user:default/boo', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }); scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ title: 'One Template', @@ -232,7 +240,9 @@ describe('', () => { fireEvent.click(allButton); }); - expect(scaffolderApiMock.listTasks).toBeCalledWith({ createdBy: 'all' }); + expect(scaffolderApiMock.listTasks).toBeCalledWith({ + filterByOwnership: 'all', + }); expect(await findByText('One Template')).toBeInTheDocument(); expect(await findByText('OtherUser')).toBeInTheDocument(); }); diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index 5f099ed331..d45d6f1ae7 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -31,7 +31,6 @@ import React, { useState } from 'react'; import { scaffolderApiRef } from '../../api'; import { rootRouteRef } from '../../routes'; import { OwnerListPicker } from './OwnerListPicker'; -import { TasksOwnerFilterKind } from '../../types'; import { CreatedAtColumn, OwnerEntityColumn, @@ -40,7 +39,7 @@ import { } from './columns'; export interface MyTaskPageProps { - initiallySelectedFilter?: TasksOwnerFilterKind; + initiallySelectedFilter?: 'owned' | 'all'; } const ListTaskPageContent = (props: MyTaskPageProps) => { @@ -52,14 +51,15 @@ const ListTaskPageContent = (props: MyTaskPageProps) => { const [ownerFilter, setOwnerFilter] = useState(initiallySelectedFilter); const { value, loading, error } = useAsync(() => { if (scaffolderApi.listTasks) { - return scaffolderApi.listTasks?.({ createdBy: ownerFilter }); + return scaffolderApi.listTasks?.({ filterByOwnership: ownerFilter }); } // eslint-disable-next-line no-console console.warn( 'listTasks is not implemented in the scaffolderApi, please make sure to implement this method.', ); - return Promise.resolve([]); + + return Promise.resolve({ tasks: [] }); }, [scaffolderApi, ownerFilter]); if (loading) { @@ -89,7 +89,7 @@ const ListTaskPageContent = (props: MyTaskPageProps) => { ( theme => ({ root: { @@ -91,7 +89,7 @@ function getFilterGroups(): ButtonGroup[] { export const OwnerListPicker = (props: { filter: string; - onSelectOwner: (id: TasksOwnerFilterKind) => void; + onSelectOwner: (id: 'owned' | 'all') => void; }) => { const { filter, onSelectOwner } = props; const classes = useStyles(); @@ -111,7 +109,7 @@ export const OwnerListPicker = (props: { key={item.id} button divider - onClick={() => onSelectOwner(item.id as TasksOwnerFilterKind)} + onClick={() => onSelectOwner(item.id as 'owned' | 'all')} selected={item.id === filter} className={classes.menuItem} data-testid={`owner-picker-${item.id}`} diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index d98fd96e55..c42dc95f9e 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -37,7 +37,6 @@ export type { ScaffolderTaskOutput, ScaffolderTaskStatus, TemplateParameterSchema, - TasksOwnerFilterKind, } from './types'; export { createScaffolderFieldExtension, diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index df7afe065f..710af6ad23 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -102,13 +102,6 @@ export type LogEvent = { taskId: string; }; -/** - * The status of each task you can filter from `ScaffolderClient` - * - * @public - */ -export type TasksOwnerFilterKind = 'owned' | 'all'; - /** * The input options to the `scaffold` method of the `ScaffolderClient`. * @@ -200,10 +193,10 @@ export interface ScaffolderApi { getTask(taskId: string): Promise; listTasks?({ - createdBy, + filterByOwnership, }: { - createdBy: TasksOwnerFilterKind; - }): Promise; + filterByOwnership: 'owned' | 'all'; + }): Promise<{ tasks: ScaffolderTask[] }>; getIntegrationsList( options: ScaffolderGetIntegrationsListOptions, diff --git a/yarn.lock b/yarn.lock index aeac084923..e7d10f183d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4176,6 +4176,14 @@ resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd" integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw== +"@mswjs/cookies@^0.1.6": + version "0.1.7" + resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.1.7.tgz#d334081b2c51057a61c1dd7b76ca3cac02251651" + integrity sha512-bDg1ReMBx+PYDB4Pk7y1Q07Zz1iKIEUWQpkEXiA2lEWg9gvOZ8UBmGXilCEUvyYoRFlmr/9iXTRR69TrgSwX/Q== + dependencies: + "@types/set-cookie-parser" "^2.4.0" + set-cookie-parser "^2.4.6" + "@mswjs/cookies@^0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.0.tgz#7ef2b5d7e444498bb27cf57720e61f76a4ce9f23" @@ -4184,6 +4192,18 @@ "@types/set-cookie-parser" "^2.4.0" set-cookie-parser "^2.4.6" +"@mswjs/interceptors@^0.12.6": + version "0.12.7" + resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.12.7.tgz#0d1cd4cd31a0f663e0455993951201faa09d0909" + integrity sha512-eGjZ3JRAt0Fzi5FgXiV/P3bJGj0NqsN7vBS0J0FO2AQRQ0jCKQS4lEFm4wvlSgKQNfeuc/Vz6d81VtU3Gkx/zg== + dependencies: + "@open-draft/until" "^1.0.3" + "@xmldom/xmldom" "^0.7.2" + debug "^4.3.2" + headers-utils "^3.0.2" + outvariant "^1.2.0" + strict-event-emitter "^0.2.0" + "@mswjs/interceptors@^0.15.1": version "0.15.3" resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.15.3.tgz#bcd17b5d7558d4f598007a4bb383b42dc9264f8d" @@ -5967,6 +5987,14 @@ resolved "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.27.1.tgz#f14740d1f585a0a8e3f46359b62fda8b0eaa31e7" integrity sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w== +"@types/inquirer@^7.3.3": + version "7.3.3" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz#92e6676efb67fa6925c69a2ee638f67a822952ac" + integrity sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ== + dependencies: + "@types/through" "*" + rxjs "^6.4.0" + "@types/inquirer@^8.1.3": version "8.2.1" resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" @@ -6043,7 +6071,7 @@ resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== -"@types/js-levenshtein@^1.1.1": +"@types/js-levenshtein@^1.1.0", "@types/js-levenshtein@^1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5" integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== @@ -7163,7 +7191,7 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.5": +"@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.2", "@xmldom/xmldom@^0.7.5": version "0.7.5" resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A== @@ -9827,7 +9855,7 @@ cookie@0.4.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -cookie@0.4.2, cookie@^0.4.2, cookie@~0.4.1: +cookie@0.4.2, cookie@^0.4.1, cookie@^0.4.2, cookie@~0.4.1: version "0.4.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -13556,6 +13584,11 @@ graphql-ws@^5.4.1: resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.5.5.tgz#f375486d3f196e2a2527b503644693ae3a8670a9" integrity sha512-hvyIS71vs4Tu/yUYHPvGXsTgo0t3arU820+lT5VjZS2go0ewp2LqyCgxEN56CzOG7Iys52eRhHBiD1gGRdiQtw== +graphql@^15.5.1: + version "15.8.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" + integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== + graphql@^16.0.0, graphql@^16.3.0: version "16.5.0" resolved "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz#41b5c1182eaac7f3d47164fb247f61e4dfb69c85" @@ -13782,6 +13815,11 @@ headers-polyfill@^3.0.4: resolved "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.0.7.tgz#725c4f591e6748f46b036197eae102c92b959ff4" integrity sha512-JoLCAdCEab58+2/yEmSnOlficyHFpIl0XJqwu3l+Unkm1gXpFUYsThz6Yha3D6tNhocWkCPfyW0YVIGWFqTi7w== +headers-utils@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-3.0.2.tgz#dfc65feae4b0e34357308aefbcafa99c895e59ef" + integrity sha512-xAxZkM1dRyGV2Ou5bzMxBPNLoRCjcX+ya7KSWybQD2KwLphxsapUVK6x/02o7f4VU6GPSXch9vNY2+gkU8tYWQ== + helmet@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/helmet/-/helmet-5.0.2.tgz#3264ec6bab96c82deaf65e3403c369424cb2366c" @@ -14361,7 +14399,7 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -inquirer@^8.0.0, inquirer@^8.2.0: +inquirer@^8.0.0, inquirer@^8.1.1, inquirer@^8.2.0: version "8.2.4" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== @@ -18146,6 +18184,32 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msw@^0.35.0: + version "0.35.0" + resolved "https://registry.npmjs.org/msw/-/msw-0.35.0.tgz#18a4ceb6c822ef226a30421d434413bc45030d38" + integrity sha512-V7A6PqaS31F1k//fPS0OnO7vllfaqBUFsMEu3IpYixyWpiUInfyglodnbXhhtDyytkQikpkPZv8TZi/CvZzv/w== + dependencies: + "@mswjs/cookies" "^0.1.6" + "@mswjs/interceptors" "^0.12.6" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/inquirer" "^7.3.3" + "@types/js-levenshtein" "^1.1.0" + chalk "^4.1.1" + chokidar "^3.4.2" + cookie "^0.4.1" + graphql "^15.5.1" + headers-utils "^3.0.2" + inquirer "^8.1.1" + is-node-process "^1.0.1" + js-levenshtein "^1.1.6" + node-fetch "^2.6.1" + node-match-path "^0.6.3" + statuses "^2.0.0" + strict-event-emitter "^0.2.0" + type-fest "^1.2.2" + yargs "^17.0.1" + msw@^0.39.2: version "0.39.2" resolved "https://registry.npmjs.org/msw/-/msw-0.39.2.tgz#832e9274db62c43cb79854d5a69dce031c700de8" @@ -18490,6 +18554,11 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" +node-match-path@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.6.3.tgz#55dd8443d547f066937a0752dce462ea7dc27551" + integrity sha512-fB1reOHKLRZCJMAka28hIxCwQLxGmd7WewOCBDYKpyA1KXi68A7vaGgdZAPhY2E6SXoYt3KqYCCvXLJ+O0Fu/Q== + node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -19099,7 +19168,7 @@ outdent@^0.5.0: resolved "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== -outvariant@^1.2.1, outvariant@^1.3.0: +outvariant@^1.2.0, outvariant@^1.2.1, outvariant@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.3.0.tgz#c39723b1d2cba729c930b74bf962317a81b9b1c9" integrity sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ== @@ -22133,7 +22202,7 @@ rxjs@7.5.5, rxjs@^7.1.0, rxjs@^7.2.0, rxjs@^7.5.1, rxjs@^7.5.5: dependencies: tslib "^2.1.0" -rxjs@^6.3.3, rxjs@^6.6.0, rxjs@^6.6.3: +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3: version "6.6.7" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -25768,7 +25837,7 @@ yargs@^17.0.0, yargs@^17.1.1, yargs@^17.2.1: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^17.3.1: +yargs@^17.0.1, yargs@^17.3.1: version "17.5.1" resolved "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==