Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e97c7e2e68 | ||
![]() |
5fb48ed54b | ||
![]() |
903e9e6093 | ||
![]() |
d30e491817 | ||
![]() |
aa727e3260 | ||
![]() |
1cad3a7149 | ||
![]() |
3b7f6740bb | ||
![]() |
2febc5efad | ||
![]() |
903616bef6 | ||
![]() |
c944193fb4 | ||
![]() |
4f2155ea63 | ||
![]() |
4bda1edda7 | ||
![]() |
1a55cc8604 | ||
![]() |
bf7ab475ee | ||
![]() |
2f39efb935 | ||
![]() |
9f8eb985e4 | ||
![]() |
3549fef71c | ||
![]() |
2cfa64c2a3 | ||
![]() |
7245a0a599 | ||
![]() |
0633da3244 | ||
![]() |
96341976f5 | ||
![]() |
9abfaec4d5 | ||
![]() |
945c52dd6b | ||
![]() |
6567d24760 | ||
![]() |
ffaf9b6e0c | ||
![]() |
463e6908b1 | ||
![]() |
e185ceb385 | ||
![]() |
1b21bbe5b7 | ||
![]() |
14b7053ed8 | ||
![]() |
2760526def | ||
![]() |
d851db22d0 | ||
![]() |
2fa360e400 | ||
![]() |
e4eb146169 | ||
![]() |
86611453b5 | ||
![]() |
65f9d1b6b9 |
@@ -7,4 +7,12 @@ module.exports = {
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.test.ts', '**/test/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': ['off'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -4,4 +4,6 @@ module.exports = {
|
||||
testEnvironment: 'node',
|
||||
setupFilesAfterEnv: ['./test/setup/global-hooks.ts'],
|
||||
globalTeardown: './test/setup/global-teardown.ts',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/graphql/queries/*.ts'],
|
||||
};
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if (requestConfig.additionalProperties?.skipAddingAuthHeader) return requestConfig;
|
||||
if (requestConfig.additionalProperties?.skipAddingAuthHeader)
|
||||
return requestConfig;
|
||||
|
||||
if ($.auth.data?.accessToken) {
|
||||
const authorizationHeader = `Bearer ${$.auth.data.accessToken}`;
|
||||
|
189
packages/backend/src/apps/trello/actions/create-card/index.ts
Normal file
189
packages/backend/src/apps/trello/actions/create-card/index.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { IJSONArray, IJSONObject } from '@automatisch/types';
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create card',
|
||||
key: 'createCard',
|
||||
description: 'Creates a new card within a specified board and list.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Board',
|
||||
key: 'boardId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listBoards',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'List',
|
||||
key: 'listId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
dependsOn: ['parameters.boardId'],
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listBoardLists',
|
||||
},
|
||||
{
|
||||
name: 'parameters.boardId',
|
||||
value: '{parameters.boardId}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
key: 'name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
key: 'description',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Label',
|
||||
key: 'label',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
dependsOn: ['parameters.boardId'],
|
||||
description: 'Select a color tag to attach to the card.',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listBoardLabels',
|
||||
},
|
||||
{
|
||||
name: 'parameters.boardId',
|
||||
value: '{parameters.boardId}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Card Position',
|
||||
key: 'cardPosition',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
description: '',
|
||||
variables: true,
|
||||
options: [
|
||||
{
|
||||
label: 'top',
|
||||
value: 'top',
|
||||
},
|
||||
{
|
||||
label: 'bottom',
|
||||
value: 'bottom',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Members',
|
||||
key: 'memberIds',
|
||||
type: 'dynamic' as const,
|
||||
required: false,
|
||||
description: '',
|
||||
fields: [
|
||||
{
|
||||
label: 'Member',
|
||||
key: 'memberId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
dependsOn: ['parameters.boardId'],
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listMembers',
|
||||
},
|
||||
{
|
||||
name: 'parameters.boardId',
|
||||
value: '{parameters.boardId}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Due Date',
|
||||
key: 'dueDate',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Format: mm-dd-yyyy HH:mm:ss or yyyy-MM-dd HH:mm:ss.',
|
||||
},
|
||||
{
|
||||
label: 'URL Attachment',
|
||||
key: 'urlSource',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'A URL to attach to the card.',
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const {
|
||||
listId,
|
||||
name,
|
||||
description,
|
||||
cardPosition,
|
||||
dueDate,
|
||||
label,
|
||||
urlSource,
|
||||
} = $.step.parameters;
|
||||
|
||||
const memberIds = $.step.parameters.memberIds as IJSONArray;
|
||||
const idMembers = memberIds.map(
|
||||
(memberId: IJSONObject) => memberId.memberId
|
||||
);
|
||||
|
||||
const fields = {
|
||||
name,
|
||||
desc: description,
|
||||
idList: listId,
|
||||
pos: cardPosition,
|
||||
due: dueDate,
|
||||
idMembers: idMembers.join(','),
|
||||
idLabels: label,
|
||||
urlSource,
|
||||
};
|
||||
|
||||
const response = await $.http.post('/1/cards', fields);
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
3
packages/backend/src/apps/trello/actions/index.ts
Normal file
3
packages/backend/src/apps/trello/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createCard from './create-card';
|
||||
|
||||
export default [createCard];
|
@@ -5,6 +5,8 @@ const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
requestConfig.headers.Authorization = `OAuth oauth_consumer_key="${$.auth.data.apiKey}", oauth_token="${$.auth.data.token}"`;
|
||||
}
|
||||
|
||||
requestConfig.headers.Accept = 'application/json';
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
|
6
packages/backend/src/apps/trello/dynamic-data/index.ts
Normal file
6
packages/backend/src/apps/trello/dynamic-data/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import listBoardLabels from './list-board-labels';
|
||||
import listBoardLists from './list-board-lists';
|
||||
import listBoards from './list-boards';
|
||||
import listMembers from './listMembers';
|
||||
|
||||
export default [listBoardLabels, listBoardLists, listBoards, listMembers];
|
@@ -0,0 +1,39 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List board labels',
|
||||
key: 'listBoardLabels',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const boardLabels: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const boardId = $.step.parameters.boardId;
|
||||
|
||||
if (!boardId) {
|
||||
return boardLabels;
|
||||
}
|
||||
|
||||
const params = {
|
||||
fields: 'color',
|
||||
};
|
||||
|
||||
const { data } = await $.http.get(`/1/boards/${boardId}/labels`, {
|
||||
params,
|
||||
});
|
||||
|
||||
if (data?.length) {
|
||||
for (const boardLabel of data) {
|
||||
boardLabels.data.push({
|
||||
value: boardLabel.id,
|
||||
name: boardLabel.color,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return boardLabels;
|
||||
},
|
||||
};
|
@@ -0,0 +1,33 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List board lists',
|
||||
key: 'listBoardLists',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const boards: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const boardId = $.step.parameters.boardId;
|
||||
|
||||
if (!boardId) {
|
||||
return boards;
|
||||
}
|
||||
|
||||
const { data } = await $.http.get(`/1/boards/${boardId}/lists`);
|
||||
|
||||
if (data?.length) {
|
||||
for (const list of data) {
|
||||
boards.data.push({
|
||||
value: list.id,
|
||||
name: list.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return boards;
|
||||
},
|
||||
};
|
@@ -0,0 +1,27 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List boards',
|
||||
key: 'listBoards',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const boards: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const { data } = await $.http.get(`/1/members/me/boards`);
|
||||
|
||||
if (data?.length) {
|
||||
for (const board of data) {
|
||||
boards.data.push({
|
||||
value: board.id,
|
||||
name: board.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return boards;
|
||||
},
|
||||
};
|
@@ -0,0 +1,33 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List members',
|
||||
key: 'listMembers',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const members: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const boardId = $.step.parameters.boardId;
|
||||
|
||||
if (!boardId) {
|
||||
return members;
|
||||
}
|
||||
|
||||
const { data } = await $.http.get(`/1/boards/${boardId}/members`);
|
||||
|
||||
if (data?.length) {
|
||||
for (const member of data) {
|
||||
members.data.push({
|
||||
value: member.id,
|
||||
name: member.fullName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return members;
|
||||
},
|
||||
};
|
@@ -1,6 +1,8 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
import dynamicData from './dynamic-data';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Trello',
|
||||
@@ -13,4 +15,6 @@ export default defineApp({
|
||||
primaryColor: '0079bf',
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
dynamicData,
|
||||
});
|
||||
|
1
packages/backend/src/apps/twitch/assets/favicon.svg
Normal file
1
packages/backend/src/apps/twitch/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" overflow="visible" width="40" height="40" version="1.1" viewBox="0 0 40 40" x="0px" y="0px" class="ScSvg-sc-mx5axi-2 iAAiAK"><g fill="#5C16C5"><polygon points="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" class="ScBody-sc-mx5axi-3 dosCbL" fill="#9147FF"><animate dur="150ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" from="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" to="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5"></animate><animate dur="250ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" from="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5" to="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8"></animate><animate dur="50ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" to="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" from="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5"></animate><animate dur="75ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" to="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5" from="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8"></animate></polygon><polygon points="26 25 30 21 30 10 14 10 14 25 18 25 18 29 22 25" class="ScFace-sc-mx5axi-4 fDFkyX" fill="#FFFFFF"><animateTransform dur="150ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform><animateTransform dur="250ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="50ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="75ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform></polygon><g class="ScEyes-sc-mx5axi-5 fAMMxB" fill="#5C16C5"><path d="M20,14 L22,14 L22,20 L20,20 L20,14 Z M27,14 L27,20 L25,20 L25,14 L27,14 Z" class="ScBody-sc-mx5axi-3 dosCbL" fill="#9147FF"><animateTransform dur="150ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform><animateTransform dur="250ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="50ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="75ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform></path></g></g></svg>
|
After Width: | Height: | Size: 3.3 KiB |
22
packages/backend/src/apps/twitch/auth/generate-auth-url.ts
Normal file
22
packages/backend/src/apps/twitch/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
import authScope from '../common/auth-scope';
|
||||
|
||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value as string;
|
||||
const searchParams = new URLSearchParams({
|
||||
client_id: $.auth.data.clientId as string,
|
||||
redirect_uri: redirectUri,
|
||||
response_type: 'code',
|
||||
scope: authScope.join(' '),
|
||||
});
|
||||
|
||||
const url = `https://id.twitch.tv/oauth2/authorize?${searchParams.toString()}`;
|
||||
|
||||
await $.auth.set({
|
||||
url,
|
||||
});
|
||||
}
|
48
packages/backend/src/apps/twitch/auth/index.ts
Normal file
48
packages/backend/src/apps/twitch/auth/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import generateAuthUrl from './generate-auth-url';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import refreshToken from './refresh-token';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'oAuthRedirectUrl',
|
||||
label: 'OAuth Redirect URL',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: '{WEB_APP_URL}/app/twitch/connections/add',
|
||||
placeholder: null,
|
||||
description:
|
||||
'When asked to input a redirect URL in Twitch, enter the URL above.',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client ID',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client Secret',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
refreshToken,
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
const { data } = await $.http.get('https://id.twitch.tv/oauth2/validate');
|
||||
return !!data.login;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
27
packages/backend/src/apps/twitch/auth/refresh-token.ts
Normal file
27
packages/backend/src/apps/twitch/auth/refresh-token.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { URLSearchParams } from 'node:url';
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import authScope from '../common/auth-scope';
|
||||
|
||||
const refreshToken = async ($: IGlobalVariable) => {
|
||||
const params = new URLSearchParams({
|
||||
client_id: $.auth.data.clientId as string,
|
||||
client_secret: $.auth.data.clientSecret as string,
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: $.auth.data.refreshToken as string,
|
||||
});
|
||||
|
||||
const { data } = await $.http.post(
|
||||
'https://id.twitch.tv/oauth2/token',
|
||||
params.toString()
|
||||
);
|
||||
|
||||
await $.auth.set({
|
||||
accessToken: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
expiresIn: data.expires_in,
|
||||
scope: authScope.join(' '),
|
||||
tokenType: data.token_type,
|
||||
});
|
||||
};
|
||||
|
||||
export default refreshToken;
|
63
packages/backend/src/apps/twitch/auth/verify-credentials.ts
Normal file
63
packages/backend/src/apps/twitch/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value as string;
|
||||
const headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
const userParams = {
|
||||
client_id: $.auth.data.clientId,
|
||||
client_secret: $.auth.data.clientSecret,
|
||||
code: $.auth.data.code,
|
||||
grant_type: 'authorization_code',
|
||||
redirect_uri: redirectUri,
|
||||
};
|
||||
|
||||
const { data } = await $.http.post(
|
||||
`https://id.twitch.tv/oauth2/token`,
|
||||
null,
|
||||
{ headers, params: userParams }
|
||||
);
|
||||
|
||||
await $.auth.set({
|
||||
userAccessToken: data.access_token,
|
||||
});
|
||||
|
||||
const currentUser = await getCurrentUser($);
|
||||
|
||||
const screenName = [currentUser.display_name, currentUser.email]
|
||||
.filter(Boolean)
|
||||
.join(' @ ');
|
||||
|
||||
await $.auth.set({
|
||||
clientId: $.auth.data.clientId,
|
||||
clientSecret: $.auth.data.clientSecret,
|
||||
scope: $.auth.data.scope,
|
||||
userExpiresIn: data.expires_in,
|
||||
userRefreshToken: data.refresh_token,
|
||||
screenName,
|
||||
});
|
||||
|
||||
const appParams = {
|
||||
client_id: $.auth.data.clientId,
|
||||
client_secret: $.auth.data.clientSecret,
|
||||
grant_type: 'client_credentials',
|
||||
};
|
||||
|
||||
const response = await $.http.post(
|
||||
`https://id.twitch.tv/oauth2/token`,
|
||||
null,
|
||||
{ headers, params: appParams }
|
||||
);
|
||||
|
||||
await $.auth.set({
|
||||
appAccessToken: response.data.access_token,
|
||||
appExpiresIn: response.data.expires_in,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
20
packages/backend/src/apps/twitch/common/add-auth-header.ts
Normal file
20
packages/backend/src/apps/twitch/common/add-auth-header.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
const clientId = $.auth.data.clientId as string;
|
||||
let token;
|
||||
if (requestConfig.additionalProperties?.appAccessToken) {
|
||||
token = $.auth.data.appAccessToken;
|
||||
} else {
|
||||
token = $.auth.data.userAccessToken;
|
||||
}
|
||||
|
||||
if (token && clientId) {
|
||||
requestConfig.headers.Authorization = `Bearer ${token}`;
|
||||
requestConfig.headers['Client-Id'] = clientId;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
3
packages/backend/src/apps/twitch/common/auth-scope.ts
Normal file
3
packages/backend/src/apps/twitch/common/auth-scope.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const authScope: string[] = ['user:read:email'];
|
||||
|
||||
export default authScope;
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const getCurrentUser = async ($: IGlobalVariable) => {
|
||||
const { data: currentUser } = await $.http.get('/helix/users');
|
||||
return currentUser.data[0];
|
||||
};
|
||||
|
||||
export default getCurrentUser;
|
0
packages/backend/src/apps/twitch/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/twitch/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/twitch/index.ts
Normal file
16
packages/backend/src/apps/twitch/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import auth from './auth';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Twitch',
|
||||
key: 'twitch',
|
||||
baseUrl: 'https://www.twitch.tv',
|
||||
apiBaseUrl: 'https://api.twitch.tv',
|
||||
iconUrl: '{BASE_URL}/apps/twitch/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/twitch/connection',
|
||||
primaryColor: '5C16C5',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
});
|
1
packages/backend/src/apps/zendesk/assets/favicon.svg
Normal file
1
packages/backend/src/apps/zendesk/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="363" height="259" fill="#03363d"><path d="M173.82 40.5v112.86H80.34L173.82 40.5zm0-40.5a46.74 46.74 0 1 1-93.48 0h93.48zm15.4 153.37a46.74 46.74 0 0 1 93.48 0h-93.48zm0-40.5V0h93.5l-93.5 112.86zm52.28 137.06a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H227c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06h25.68zM0 249.4l28.3-28.76H.67v-9.02h40.76v9.2l-28.3 28.75h28.7v9.03H0v-9.2zm73.6.52a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H59.12c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06H84.8zm72.23 4.03c0-15 11.23-24.44 23.6-24.44a20.34 20.34 0 0 1 15.67 7.05v-27.72h10v68.6h-10V252a20.1 20.1 0 0 1-15.76 7.42c-12 0-23.5-9.5-23.5-24.43m39.82-.1a14.92 14.92 0 1 0-14.91 15.32c8.6 0 14.9-6.86 14.9-15.32m73.48 13.6l9.06-4.7a13.44 13.44 0 0 0 12.08 6.86c5.66 0 8.6-2.9 8.6-6.2 0-3.76-5.47-4.6-11.42-5.83-8-1.7-16.33-4.33-16.33-14 0-7.43 7.07-14.3 18.2-14.2 8.77 0 15.3 3.48 19 9.1l-8.4 4.6a12.19 12.19 0 0 0-10.57-5.36c-5.38 0-8.12 2.63-8.12 5.64 0 3.38 4.34 4.32 11.14 5.83 7.74 1.7 16.5 4.23 16.5 14 0 6.48-5.66 15.22-19.06 15.13-9.8 0-16.7-3.95-20.67-10.9m66.9-10.87l-7.93 8.65v12.2h-10v-68.6h10v44.93l21.23-23.3h12.18l-18.4 20.1 18.88 26.88h-11.32l-14.63-20.86zM126.8 210.53c-11.9 0-21.85 7.7-21.85 20.5v27.45h10.2V232.3c0-7.7 4.43-12.32 12-12.32s11.33 4.6 11.33 12.32v26.18h10.14v-27.45c0-12.78-10-20.5-21.85-20.5"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
23
packages/backend/src/apps/zendesk/auth/generate-auth-url.ts
Normal file
23
packages/backend/src/apps/zendesk/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
import authScope from '../common/auth-scope';
|
||||
|
||||
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
|
||||
const redirectUri = oauthRedirectUrlField.value as string;
|
||||
const searchParams = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
redirect_uri: redirectUri,
|
||||
client_id: $.auth.data.clientId as string,
|
||||
scope: authScope.join(' '),
|
||||
});
|
||||
|
||||
await $.auth.set({
|
||||
url: `${
|
||||
$.auth.data.instanceUrl
|
||||
}/oauth/authorizations/new?${searchParams.toString()}`,
|
||||
});
|
||||
}
|
55
packages/backend/src/apps/zendesk/auth/index.ts
Normal file
55
packages/backend/src/apps/zendesk/auth/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import generateAuthUrl from './generate-auth-url';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'oAuthRedirectUrl',
|
||||
label: 'OAuth Redirect URL',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: '{WEB_APP_URL}/app/zendesk/connections/add',
|
||||
placeholder: null,
|
||||
description: '',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'instanceUrl',
|
||||
label: 'Zendesk Subdomain Url',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: 'https://{{subdomain}}.zendesk.com',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client ID',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client Secret',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await getCurrentUser($);
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
56
packages/backend/src/apps/zendesk/auth/verify-credentials.ts
Normal file
56
packages/backend/src/apps/zendesk/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { IGlobalVariable, IJSONValue, IField } from '@automatisch/types';
|
||||
import getCurrentUser from '../common/get-current-user';
|
||||
import scopes from '../common/auth-scope';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
await getAccessToken($);
|
||||
|
||||
const user = await getCurrentUser($);
|
||||
const subdomain = extractSubdomain($.auth.data.instanceUrl);
|
||||
const name = user.name as string;
|
||||
const screenName = [name, subdomain].filter(Boolean).join(' @ ');
|
||||
|
||||
await $.auth.set({
|
||||
screenName,
|
||||
apiToken: $.auth.data.apiToken,
|
||||
instanceUrl: $.auth.data.instanceUrl,
|
||||
email: $.auth.data.email,
|
||||
});
|
||||
};
|
||||
|
||||
const getAccessToken = async ($: IGlobalVariable) => {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value as string;
|
||||
|
||||
const response = await $.http.post(`/oauth/tokens`, {
|
||||
redirect_uri: redirectUri,
|
||||
code: $.auth.data.code,
|
||||
grant_type: 'authorization_code',
|
||||
scope: scopes.join(' '),
|
||||
client_id: $.auth.data.clientId as string,
|
||||
client_secret: $.auth.data.clientSecret as string,
|
||||
});
|
||||
|
||||
const data = response.data;
|
||||
|
||||
$.auth.data.accessToken = data.access_token;
|
||||
|
||||
await $.auth.set({
|
||||
clientId: $.auth.data.clientId,
|
||||
clientSecret: $.auth.data.clientSecret,
|
||||
accessToken: data.access_token,
|
||||
tokenType: data.token_type,
|
||||
});
|
||||
};
|
||||
|
||||
function extractSubdomain(url: IJSONValue) {
|
||||
const match = (url as string).match(/https:\/\/(.*?)\.zendesk\.com/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default verifyCredentials;
|
17
packages/backend/src/apps/zendesk/common/add-auth-headers.ts
Normal file
17
packages/backend/src/apps/zendesk/common/add-auth-headers.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
const { instanceUrl, tokenType, accessToken } = $.auth.data;
|
||||
|
||||
if (instanceUrl) {
|
||||
requestConfig.baseURL = instanceUrl as string;
|
||||
}
|
||||
|
||||
if (tokenType && accessToken) {
|
||||
requestConfig.headers.Authorization = `${tokenType} ${$.auth.data.accessToken}`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
3
packages/backend/src/apps/zendesk/common/auth-scope.ts
Normal file
3
packages/backend/src/apps/zendesk/common/auth-scope.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const authScope: string[] = ['read', 'write'];
|
||||
|
||||
export default authScope;
|
10
packages/backend/src/apps/zendesk/common/get-current-user.ts
Normal file
10
packages/backend/src/apps/zendesk/common/get-current-user.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||
const response = await $.http.get('/api/v2/users/me');
|
||||
const currentUser = response.data.user;
|
||||
|
||||
return currentUser;
|
||||
};
|
||||
|
||||
export default getCurrentUser;
|
0
packages/backend/src/apps/zendesk/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/zendesk/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/zendesk/index.ts
Normal file
16
packages/backend/src/apps/zendesk/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-headers';
|
||||
import auth from './auth';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Zendesk',
|
||||
key: 'zendesk',
|
||||
baseUrl: 'https://zendesk.com/',
|
||||
apiBaseUrl: '',
|
||||
iconUrl: '{BASE_URL}/apps/zendesk/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/zendesk/connection',
|
||||
primaryColor: '17494d',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
});
|
@@ -0,0 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.table('executions', (table) => {
|
||||
table.index('flow_id');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.table('executions', (table) => {
|
||||
table.dropIndex('flow_id');
|
||||
});
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.table('executions', (table) => {
|
||||
table.index('updated_at');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.table('executions', (table) => {
|
||||
table.dropIndex('updated_at');
|
||||
});
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Execution from '../../models/execution';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
import logger from '../../helpers/logger';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
@@ -22,6 +24,25 @@ const deleteFlow = async (
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
const triggerStep = await flow.getTriggerStep();
|
||||
const trigger = await triggerStep?.getTriggerCommand();
|
||||
|
||||
if (trigger?.type === 'webhook' && trigger.unregisterHook) {
|
||||
const $ = await globalVariable({
|
||||
flow,
|
||||
connection: await triggerStep.$relatedQuery('connection'),
|
||||
app: await triggerStep.getApp(),
|
||||
step: triggerStep,
|
||||
});
|
||||
|
||||
try {
|
||||
await trigger.unregisterHook($);
|
||||
} catch (error) {
|
||||
// suppress error as the remote resource might have been already deleted
|
||||
logger.debug(`Failed to unregister webhook for flow ${flow.id}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const executionIds = (
|
||||
await flow.$relatedQuery('executions').select('executions.id')
|
||||
).map((execution: Execution) => execution.id);
|
||||
|
@@ -81,6 +81,10 @@ const duplicateFlow = async (
|
||||
parameters: updateStepVariables(step.parameters, newStepIds),
|
||||
});
|
||||
|
||||
if (duplicatedStep.isTrigger) {
|
||||
await duplicatedStep.updateWebhookUrl();
|
||||
}
|
||||
|
||||
newStepIds[step.id] = duplicatedStep.id;
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import * as license from '../../helpers/license.ee';
|
||||
@@ -22,7 +23,7 @@ describe('graphQL getAutomatischInfo query', () => {
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(license, 'getLicense').mockResolvedValue(false);
|
||||
|
||||
jest.replaceProperty(appConfig, 'isCloud', false)
|
||||
jest.replaceProperty(appConfig, 'isCloud', false);
|
||||
});
|
||||
|
||||
it('should return empty license data', async () => {
|
||||
@@ -36,9 +37,9 @@ describe('graphQL getAutomatischInfo query', () => {
|
||||
getAutomatischInfo: {
|
||||
isCloud: false,
|
||||
license: {
|
||||
id: null as string,
|
||||
name: null as string,
|
||||
expireAt: null as string,
|
||||
id: null,
|
||||
name: null,
|
||||
expireAt: null,
|
||||
verified: false,
|
||||
},
|
||||
},
|
||||
@@ -63,7 +64,7 @@ describe('graphQL getAutomatischInfo query', () => {
|
||||
|
||||
describe('and with cloud flag enabled', () => {
|
||||
beforeEach(async () => {
|
||||
jest.replaceProperty(appConfig, 'isCloud', true)
|
||||
jest.replaceProperty(appConfig, 'isCloud', true);
|
||||
});
|
||||
|
||||
it('should return all license data', async () => {
|
||||
@@ -92,7 +93,7 @@ describe('graphQL getAutomatischInfo query', () => {
|
||||
|
||||
describe('and with cloud flag disabled', () => {
|
||||
beforeEach(async () => {
|
||||
jest.replaceProperty(appConfig, 'isCloud', false)
|
||||
jest.replaceProperty(appConfig, 'isCloud', false);
|
||||
});
|
||||
|
||||
it('should return all license data', async () => {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import request, { Test } from 'supertest';
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { IRole, IUser } from '@automatisch/types';
|
||||
|
||||
describe('graphQL getCurrentUser query', () => {
|
||||
describe('with unauthenticated user', () => {
|
||||
@@ -31,7 +31,7 @@ describe('graphQL getCurrentUser query', () => {
|
||||
});
|
||||
|
||||
describe('with authenticated user', () => {
|
||||
let role: IRole, currentUser: IUser, token: string, requestObject: Test;
|
||||
let role, currentUser, token, requestObject;
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await createRole({
|
||||
@@ -70,12 +70,12 @@ describe('graphQL getCurrentUser query', () => {
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getCurrentUser: {
|
||||
createdAt: (currentUser.createdAt as Date).getTime().toString(),
|
||||
createdAt: currentUser.createdAt.getTime().toString(),
|
||||
email: currentUser.email,
|
||||
fullName: currentUser.fullName,
|
||||
id: currentUser.id,
|
||||
role: { id: role.id, name: role.name },
|
||||
updatedAt: (currentUser.updatedAt as Date).getTime().toString(),
|
||||
updatedAt: currentUser.updatedAt.getTime().toString(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
489
packages/backend/src/graphql/queries/get-executions.test.ts
Normal file
489
packages/backend/src/graphql/queries/get-executions.test.ts
Normal file
@@ -0,0 +1,489 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import appConfig from '../../config/app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { createFlow } from '../../../test/factories/flow';
|
||||
import { createStep } from '../../../test/factories/step';
|
||||
import { createExecution } from '../../../test/factories/execution';
|
||||
import { createExecutionStep } from '../../../test/factories/execution-step';
|
||||
|
||||
describe('graphQL getExecutions query', () => {
|
||||
const query = `
|
||||
query {
|
||||
getExecutions(limit: 10, offset: 0) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
testRun
|
||||
createdAt
|
||||
updatedAt
|
||||
status
|
||||
flow {
|
||||
id
|
||||
name
|
||||
active
|
||||
steps {
|
||||
iconUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const invalidToken = 'invalid-token';
|
||||
|
||||
describe('with unauthenticated user', () => {
|
||||
it('should throw not authorized error', async () => {
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', invalidToken)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.errors).toBeDefined();
|
||||
expect(response.body.errors[0].message).toEqual('Not Authorised!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with authenticated user', () => {
|
||||
describe('and without permissions', () => {
|
||||
it('should throw not authorized error', async () => {
|
||||
const userWithoutPermissions = await createUser();
|
||||
const token = createAuthTokenByUserId(userWithoutPermissions.id);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.errors).toBeDefined();
|
||||
expect(response.body.errors[0].message).toEqual('Not authorized!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with correct permission', () => {
|
||||
let role,
|
||||
currentUser,
|
||||
anotherUser,
|
||||
token,
|
||||
flowOne,
|
||||
stepOneForFlowOne,
|
||||
stepTwoForFlowOne,
|
||||
executionOne,
|
||||
flowTwo,
|
||||
stepOneForFlowTwo,
|
||||
stepTwoForFlowTwo,
|
||||
executionTwo,
|
||||
flowThree,
|
||||
stepOneForFlowThree,
|
||||
stepTwoForFlowThree,
|
||||
executionThree,
|
||||
expectedResponseForExecutionOne,
|
||||
expectedResponseForExecutionTwo,
|
||||
expectedResponseForExecutionThree;
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await createRole({
|
||||
key: 'sample',
|
||||
name: 'sample',
|
||||
});
|
||||
|
||||
currentUser = await createUser({
|
||||
roleId: role.id,
|
||||
fullName: 'Current User',
|
||||
});
|
||||
|
||||
anotherUser = await createUser();
|
||||
|
||||
token = createAuthTokenByUserId(currentUser.id);
|
||||
|
||||
flowOne = await createFlow({
|
||||
userId: currentUser.id,
|
||||
});
|
||||
|
||||
stepOneForFlowOne = await createStep({
|
||||
flowId: flowOne.id,
|
||||
});
|
||||
|
||||
stepTwoForFlowOne = await createStep({
|
||||
flowId: flowOne.id,
|
||||
});
|
||||
|
||||
executionOne = await createExecution({
|
||||
flowId: flowOne.id,
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionOne.id,
|
||||
stepId: stepOneForFlowOne.id,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionOne.id,
|
||||
stepId: stepTwoForFlowOne.id,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
flowTwo = await createFlow({
|
||||
userId: currentUser.id,
|
||||
});
|
||||
|
||||
stepOneForFlowTwo = await createStep({
|
||||
flowId: flowTwo.id,
|
||||
});
|
||||
|
||||
stepTwoForFlowTwo = await createStep({
|
||||
flowId: flowTwo.id,
|
||||
});
|
||||
|
||||
executionTwo = await createExecution({
|
||||
flowId: flowTwo.id,
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionTwo.id,
|
||||
stepId: stepOneForFlowTwo.id,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionTwo.id,
|
||||
stepId: stepTwoForFlowTwo.id,
|
||||
status: 'failure',
|
||||
});
|
||||
|
||||
flowThree = await createFlow({
|
||||
userId: anotherUser.id,
|
||||
});
|
||||
|
||||
stepOneForFlowThree = await createStep({
|
||||
flowId: flowThree.id,
|
||||
});
|
||||
|
||||
stepTwoForFlowThree = await createStep({
|
||||
flowId: flowThree.id,
|
||||
});
|
||||
|
||||
executionThree = await createExecution({
|
||||
flowId: flowThree.id,
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionThree.id,
|
||||
stepId: stepOneForFlowThree.id,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
await createExecutionStep({
|
||||
executionId: executionThree.id,
|
||||
stepId: stepTwoForFlowThree.id,
|
||||
status: 'failure',
|
||||
});
|
||||
|
||||
expectedResponseForExecutionOne = {
|
||||
node: {
|
||||
createdAt: executionOne.createdAt.getTime().toString(),
|
||||
flow: {
|
||||
active: flowOne.active,
|
||||
id: flowOne.id,
|
||||
name: flowOne.name,
|
||||
steps: [
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowOne.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowOne.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
],
|
||||
},
|
||||
id: executionOne.id,
|
||||
status: 'success',
|
||||
testRun: executionOne.testRun,
|
||||
updatedAt: executionOne.updatedAt.getTime().toString(),
|
||||
},
|
||||
};
|
||||
|
||||
expectedResponseForExecutionTwo = {
|
||||
node: {
|
||||
createdAt: executionTwo.createdAt.getTime().toString(),
|
||||
flow: {
|
||||
active: flowTwo.active,
|
||||
id: flowTwo.id,
|
||||
name: flowTwo.name,
|
||||
steps: [
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
],
|
||||
},
|
||||
id: executionTwo.id,
|
||||
status: 'failure',
|
||||
testRun: executionTwo.testRun,
|
||||
updatedAt: executionTwo.updatedAt.getTime().toString(),
|
||||
},
|
||||
};
|
||||
|
||||
expectedResponseForExecutionThree = {
|
||||
node: {
|
||||
createdAt: executionThree.createdAt.getTime().toString(),
|
||||
flow: {
|
||||
active: flowThree.active,
|
||||
id: flowThree.id,
|
||||
name: flowThree.name,
|
||||
steps: [
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowThree.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
{
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowThree.appKey}/assets/favicon.svg`,
|
||||
},
|
||||
],
|
||||
},
|
||||
id: executionThree.id,
|
||||
status: 'failure',
|
||||
testRun: executionThree.testRun,
|
||||
updatedAt: executionThree.updatedAt.getTime().toString(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('and with isCreator condition', () => {
|
||||
beforeEach(async () => {
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'Execution',
|
||||
roleId: role.id,
|
||||
conditions: ['isCreator'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return executions data of the current user', async () => {
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getExecutions: {
|
||||
edges: [
|
||||
expectedResponseForExecutionTwo,
|
||||
expectedResponseForExecutionOne,
|
||||
],
|
||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and without isCreator condition', () => {
|
||||
beforeEach(async () => {
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'Execution',
|
||||
roleId: role.id,
|
||||
conditions: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return executions data of all users', async () => {
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getExecutions: {
|
||||
edges: [
|
||||
expectedResponseForExecutionThree,
|
||||
expectedResponseForExecutionTwo,
|
||||
expectedResponseForExecutionOne,
|
||||
],
|
||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with filters', () => {
|
||||
beforeEach(async () => {
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'Execution',
|
||||
roleId: role.id,
|
||||
conditions: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return executions data for the specified flow', async () => {
|
||||
const query = `
|
||||
query {
|
||||
getExecutions(limit: 10, offset: 0, filters: { flowId: "${flowOne.id}" }) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
testRun
|
||||
createdAt
|
||||
updatedAt
|
||||
status
|
||||
flow {
|
||||
id
|
||||
name
|
||||
active
|
||||
steps {
|
||||
iconUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getExecutions: {
|
||||
edges: [expectedResponseForExecutionOne],
|
||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
|
||||
it('should return only executions data with success status', async () => {
|
||||
const query = `
|
||||
query {
|
||||
getExecutions(limit: 10, offset: 0, filters: { status: "success" }) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
testRun
|
||||
createdAt
|
||||
updatedAt
|
||||
status
|
||||
flow {
|
||||
id
|
||||
name
|
||||
active
|
||||
steps {
|
||||
iconUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getExecutions: {
|
||||
edges: [expectedResponseForExecutionOne],
|
||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
|
||||
it('should return only executions data within date range', async () => {
|
||||
const createdAtFrom = executionOne.createdAt.getTime().toString();
|
||||
|
||||
const createdAtTo = executionOne.createdAt.getTime().toString();
|
||||
|
||||
const query = `
|
||||
query {
|
||||
getExecutions(limit: 10, offset: 0, filters: { createdAt: { from: "${createdAtFrom}", to: "${createdAtTo}" }}) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
testRun
|
||||
createdAt
|
||||
updatedAt
|
||||
status
|
||||
flow {
|
||||
id
|
||||
name
|
||||
active
|
||||
steps {
|
||||
iconUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getExecutions: {
|
||||
edges: [expectedResponseForExecutionOne],
|
||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,11 +1,22 @@
|
||||
import { raw } from 'objection';
|
||||
import { DateTime } from 'luxon';
|
||||
import Context from '../../types/express/context';
|
||||
import Execution from '../../models/execution';
|
||||
import paginate from '../../helpers/pagination';
|
||||
|
||||
type Filters = {
|
||||
flowId?: string;
|
||||
status?: string;
|
||||
createdAt?: {
|
||||
from?: string;
|
||||
to?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type Params = {
|
||||
limit: number;
|
||||
offset: number;
|
||||
filters?: Filters;
|
||||
};
|
||||
|
||||
const getExecutions = async (
|
||||
@@ -15,6 +26,8 @@ const getExecutions = async (
|
||||
) => {
|
||||
const conditions = context.currentUser.can('read', 'Execution');
|
||||
|
||||
const filters = params.filters;
|
||||
|
||||
const userExecutions = context.currentUser.$relatedQuery('executions');
|
||||
const allExecutions = Execution.query();
|
||||
const executionBaseQuery = conditions.isCreator ? userExecutions : allExecutions;
|
||||
@@ -32,16 +45,49 @@ const getExecutions = async (
|
||||
.clone()
|
||||
.joinRelated('executionSteps as execution_steps')
|
||||
.select('executions.*', raw(selectStatusStatement))
|
||||
.groupBy('executions.id')
|
||||
.orderBy('created_at', 'desc');
|
||||
|
||||
const computedExecutions = Execution
|
||||
.query()
|
||||
.with('executions', executions)
|
||||
.withSoftDeleted()
|
||||
.withGraphFetched({
|
||||
flow: {
|
||||
steps: true,
|
||||
},
|
||||
})
|
||||
.groupBy('executions.id')
|
||||
.orderBy('updated_at', 'desc');
|
||||
});
|
||||
|
||||
return paginate(executions, params.limit, params.offset);
|
||||
if (filters?.flowId) {
|
||||
computedExecutions.where('executions.flow_id', filters.flowId);
|
||||
}
|
||||
|
||||
if (filters?.status) {
|
||||
computedExecutions.where('executions.status', filters.status);
|
||||
}
|
||||
|
||||
if (filters?.createdAt) {
|
||||
const createdAtFilter = filters.createdAt;
|
||||
if (createdAtFilter.from) {
|
||||
const isoFromDateTime = DateTime
|
||||
.fromMillis(
|
||||
parseInt(createdAtFilter.from, 10)
|
||||
)
|
||||
.toISO();
|
||||
computedExecutions.where('executions.created_at', '>=', isoFromDateTime);
|
||||
}
|
||||
|
||||
if (createdAtFilter.to) {
|
||||
const isoToDateTime = DateTime
|
||||
.fromMillis(
|
||||
parseInt(createdAtFilter.to, 10)
|
||||
)
|
||||
.toISO();
|
||||
computedExecutions.where('executions.created_at', '<=', isoToDateTime);
|
||||
}
|
||||
}
|
||||
|
||||
return paginate(computedExecutions, params.limit, params.offset);
|
||||
};
|
||||
|
||||
export default getExecutions;
|
||||
|
262
packages/backend/src/graphql/queries/get-flow.test.ts
Normal file
262
packages/backend/src/graphql/queries/get-flow.test.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import appConfig from '../../config/app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { createFlow } from '../../../test/factories/flow';
|
||||
import { createStep } from '../../../test/factories/step';
|
||||
import { createConnection } from '../../../test/factories/connection';
|
||||
|
||||
describe('graphQL getFlow query', () => {
|
||||
const query = (flowId) => {
|
||||
return `
|
||||
query {
|
||||
getFlow(id: "${flowId}") {
|
||||
id
|
||||
name
|
||||
active
|
||||
status
|
||||
steps {
|
||||
id
|
||||
type
|
||||
key
|
||||
appKey
|
||||
iconUrl
|
||||
webhookUrl
|
||||
status
|
||||
position
|
||||
connection {
|
||||
id
|
||||
verified
|
||||
createdAt
|
||||
}
|
||||
parameters
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
describe('with unauthenticated user', () => {
|
||||
it('should throw not authorized error', async () => {
|
||||
const invalidToken = 'invalid-token';
|
||||
const flow = await createFlow();
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', invalidToken)
|
||||
.send({ query: query(flow.id) })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.errors).toBeDefined();
|
||||
expect(response.body.errors[0].message).toEqual('Not Authorised!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with authenticated user', () => {
|
||||
describe('and without permissions', () => {
|
||||
it('should throw not authorized error', async () => {
|
||||
const userWithoutPermissions = await createUser();
|
||||
const token = createAuthTokenByUserId(userWithoutPermissions.id);
|
||||
const flow = await createFlow();
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query: query(flow.id) })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.errors).toBeDefined();
|
||||
expect(response.body.errors[0].message).toEqual('Not authorized!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with correct permission', () => {
|
||||
let currentUser, currentUserRole, currentUserFlow;
|
||||
|
||||
beforeEach(async () => {
|
||||
currentUserRole = await createRole();
|
||||
currentUser = await createUser({ roleId: currentUserRole.id });
|
||||
currentUserFlow = await createFlow({ userId: currentUser.id });
|
||||
});
|
||||
|
||||
describe('and with isCreator condition', () => {
|
||||
it('should return executions data of the current user', async () => {
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'Flow',
|
||||
roleId: currentUserRole.id,
|
||||
conditions: ['isCreator'],
|
||||
});
|
||||
|
||||
const triggerStep = await createStep({
|
||||
flowId: currentUserFlow.id,
|
||||
type: 'trigger',
|
||||
key: 'catchRawWebhook',
|
||||
webhookPath: `/webhooks/flows/${currentUserFlow.id}`,
|
||||
});
|
||||
|
||||
const actionConnection = await createConnection({
|
||||
userId: currentUser.id,
|
||||
formattedData: {
|
||||
screenName: 'Test',
|
||||
authenticationKey: 'test key',
|
||||
},
|
||||
});
|
||||
|
||||
const actionStep = await createStep({
|
||||
flowId: currentUserFlow.id,
|
||||
type: 'action',
|
||||
connectionId: actionConnection.id,
|
||||
key: 'translateText',
|
||||
});
|
||||
|
||||
const token = createAuthTokenByUserId(currentUser.id);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query: query(currentUserFlow.id) })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getFlow: {
|
||||
active: currentUserFlow.active,
|
||||
id: currentUserFlow.id,
|
||||
name: currentUserFlow.name,
|
||||
status: 'draft',
|
||||
steps: [
|
||||
{
|
||||
appKey: triggerStep.appKey,
|
||||
connection: null,
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
|
||||
id: triggerStep.id,
|
||||
key: 'catchRawWebhook',
|
||||
parameters: {},
|
||||
position: 1,
|
||||
status: triggerStep.status,
|
||||
type: 'trigger',
|
||||
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${currentUserFlow.id}`,
|
||||
},
|
||||
{
|
||||
appKey: actionStep.appKey,
|
||||
connection: {
|
||||
createdAt: actionConnection.createdAt
|
||||
.getTime()
|
||||
.toString(),
|
||||
id: actionConnection.id,
|
||||
verified: actionConnection.verified,
|
||||
},
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
|
||||
id: actionStep.id,
|
||||
key: 'translateText',
|
||||
parameters: {},
|
||||
position: 1,
|
||||
status: actionStep.status,
|
||||
type: 'action',
|
||||
webhookUrl: 'http://localhost:3000/null',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and without isCreator condition', () => {
|
||||
it('should return executions data of all users', async () => {
|
||||
await createPermission({
|
||||
action: 'read',
|
||||
subject: 'Flow',
|
||||
roleId: currentUserRole.id,
|
||||
conditions: [],
|
||||
});
|
||||
|
||||
const anotherUser = await createUser();
|
||||
const anotherUserFlow = await createFlow({ userId: anotherUser.id });
|
||||
|
||||
const triggerStep = await createStep({
|
||||
flowId: anotherUserFlow.id,
|
||||
type: 'trigger',
|
||||
key: 'catchRawWebhook',
|
||||
webhookPath: `/webhooks/flows/${anotherUserFlow.id}`,
|
||||
});
|
||||
|
||||
const actionConnection = await createConnection({
|
||||
userId: anotherUser.id,
|
||||
formattedData: {
|
||||
screenName: 'Test',
|
||||
authenticationKey: 'test key',
|
||||
},
|
||||
});
|
||||
|
||||
const actionStep = await createStep({
|
||||
flowId: anotherUserFlow.id,
|
||||
type: 'action',
|
||||
connectionId: actionConnection.id,
|
||||
key: 'translateText',
|
||||
});
|
||||
|
||||
const token = createAuthTokenByUserId(currentUser.id);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/graphql')
|
||||
.set('Authorization', token)
|
||||
.send({ query: query(anotherUserFlow.id) })
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getFlow: {
|
||||
active: anotherUserFlow.active,
|
||||
id: anotherUserFlow.id,
|
||||
name: anotherUserFlow.name,
|
||||
status: 'draft',
|
||||
steps: [
|
||||
{
|
||||
appKey: triggerStep.appKey,
|
||||
connection: null,
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
|
||||
id: triggerStep.id,
|
||||
key: 'catchRawWebhook',
|
||||
parameters: {},
|
||||
position: 1,
|
||||
status: triggerStep.status,
|
||||
type: 'trigger',
|
||||
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${anotherUserFlow.id}`,
|
||||
},
|
||||
{
|
||||
appKey: actionStep.appKey,
|
||||
connection: {
|
||||
createdAt: actionConnection.createdAt
|
||||
.getTime()
|
||||
.toString(),
|
||||
id: actionConnection.id,
|
||||
verified: actionConnection.verified,
|
||||
},
|
||||
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
|
||||
id: actionStep.id,
|
||||
key: 'translateText',
|
||||
parameters: {},
|
||||
position: 1,
|
||||
status: actionStep.status,
|
||||
type: 'action',
|
||||
webhookUrl: 'http://localhost:3000/null',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
@@ -5,21 +6,20 @@ import Crypto from 'crypto';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { IRole, IUser, IPermission } from '@automatisch/types';
|
||||
import * as license from '../../helpers/license.ee';
|
||||
|
||||
describe('graphQL getRole query', () => {
|
||||
let validRole: IRole,
|
||||
invalidRoleId: string,
|
||||
queryWithValidRole: string,
|
||||
queryWithInvalidRole: string,
|
||||
userWithPermissions: IUser,
|
||||
userWithoutPermissions: IUser,
|
||||
tokenWithPermissions: string,
|
||||
tokenWithoutPermissions: string,
|
||||
invalidToken: string,
|
||||
permissionOne: IPermission,
|
||||
permissionTwo: IPermission;
|
||||
let validRole,
|
||||
invalidRoleId,
|
||||
queryWithValidRole,
|
||||
queryWithInvalidRole,
|
||||
userWithPermissions,
|
||||
userWithoutPermissions,
|
||||
tokenWithPermissions,
|
||||
tokenWithoutPermissions,
|
||||
invalidToken,
|
||||
permissionOne,
|
||||
permissionTwo;
|
||||
|
||||
beforeEach(async () => {
|
||||
validRole = await createRole();
|
||||
|
@@ -1,22 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { IRole, IUser } from '@automatisch/types';
|
||||
import * as license from '../../helpers/license.ee';
|
||||
|
||||
describe('graphQL getRoles query', () => {
|
||||
let currentUserRole: IRole,
|
||||
roleOne: IRole,
|
||||
roleSecond: IRole,
|
||||
query: string,
|
||||
userWithPermissions: IUser,
|
||||
userWithoutPermissions: IUser,
|
||||
tokenWithPermissions: string,
|
||||
tokenWithoutPermissions: string,
|
||||
invalidToken: string;
|
||||
let currentUserRole,
|
||||
roleOne,
|
||||
roleSecond,
|
||||
query,
|
||||
userWithPermissions,
|
||||
userWithoutPermissions,
|
||||
tokenWithPermissions,
|
||||
tokenWithoutPermissions,
|
||||
invalidToken;
|
||||
|
||||
beforeEach(async () => {
|
||||
currentUserRole = await createRole({ name: 'Current user role' });
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import { IUser } from '@automatisch/types';
|
||||
import User from '../../models/user';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
@@ -32,7 +32,7 @@ describe('graphQL getTrialStatus query', () => {
|
||||
});
|
||||
|
||||
describe('with authenticated user', () => {
|
||||
let user: IUser, userToken: string;
|
||||
let user, userToken;
|
||||
|
||||
beforeEach(async () => {
|
||||
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
|
||||
@@ -54,7 +54,7 @@ describe('graphQL getTrialStatus query', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: { getTrialStatus: null as string },
|
||||
data: { getTrialStatus: null },
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
@@ -82,7 +82,7 @@ describe('graphQL getTrialStatus query', () => {
|
||||
.expect(200);
|
||||
|
||||
const expectedResponsePayload = {
|
||||
data: { getTrialStatus: null as string },
|
||||
data: { getTrialStatus: null },
|
||||
};
|
||||
|
||||
expect(response.body).toEqual(expectedResponsePayload);
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import request, { Test } from 'supertest';
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import Crypto from 'crypto';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { IRole, IUser } from '@automatisch/types';
|
||||
|
||||
describe('graphQL getUser query', () => {
|
||||
describe('with unauthenticated user', () => {
|
||||
@@ -61,11 +61,7 @@ describe('graphQL getUser query', () => {
|
||||
});
|
||||
|
||||
describe('and correct permissions', () => {
|
||||
let role: IRole,
|
||||
currentUser: IUser,
|
||||
anotherUser: IUser,
|
||||
token: string,
|
||||
requestObject: Test;
|
||||
let role, currentUser, anotherUser, token, requestObject;
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await createRole({
|
||||
@@ -116,12 +112,12 @@ describe('graphQL getUser query', () => {
|
||||
const expectedResponsePayload = {
|
||||
data: {
|
||||
getUser: {
|
||||
createdAt: (anotherUser.createdAt as Date).getTime().toString(),
|
||||
createdAt: anotherUser.createdAt.getTime().toString(),
|
||||
email: anotherUser.email,
|
||||
fullName: anotherUser.fullName,
|
||||
id: anotherUser.id,
|
||||
role: { id: role.id, name: role.name },
|
||||
updatedAt: (anotherUser.updatedAt as Date).getTime().toString(),
|
||||
updatedAt: anotherUser.updatedAt.getTime().toString(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import request, { Test } from 'supertest';
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
import { createRole } from '../../../test/factories/role';
|
||||
import { createPermission } from '../../../test/factories/permission';
|
||||
import { createUser } from '../../../test/factories/user';
|
||||
import { IRole, IUser } from '@automatisch/types';
|
||||
|
||||
describe('graphQL getUsers query', () => {
|
||||
const query = `
|
||||
@@ -61,11 +61,7 @@ describe('graphQL getUsers query', () => {
|
||||
});
|
||||
|
||||
describe('and with correct permissions', () => {
|
||||
let role: IRole,
|
||||
currentUser: IUser,
|
||||
anotherUser: IUser,
|
||||
token: string,
|
||||
requestObject: Test;
|
||||
let role, currentUser, anotherUser, token, requestObject;
|
||||
|
||||
beforeEach(async () => {
|
||||
role = await createRole({
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import request from 'supertest';
|
||||
import app from '../../app';
|
||||
import appConfig from '../../config/app';
|
||||
|
@@ -20,7 +20,11 @@ type Query {
|
||||
): FlowConnection
|
||||
getStepWithTestExecutions(stepId: String!): [Step]
|
||||
getExecution(executionId: String!): Execution
|
||||
getExecutions(limit: Int!, offset: Int!): ExecutionConnection
|
||||
getExecutions(
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
filters: ExecutionFiltersInput
|
||||
): ExecutionConnection
|
||||
getExecutionSteps(
|
||||
executionId: String!
|
||||
limit: Int!
|
||||
@@ -795,6 +799,17 @@ type Notification {
|
||||
description: String
|
||||
}
|
||||
|
||||
input ExecutionCreatedAtFilterInput {
|
||||
from: String
|
||||
to: String
|
||||
}
|
||||
|
||||
input ExecutionFiltersInput {
|
||||
flowId: String
|
||||
createdAt: ExecutionCreatedAtFilterInput
|
||||
status: String
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
|
@@ -10,6 +10,8 @@ export const createConnection = async (params: Partial<Connection> = {}) => {
|
||||
authenticationKey: 'test key',
|
||||
};
|
||||
|
||||
delete params.formattedData;
|
||||
|
||||
params.data = AES.encrypt(
|
||||
JSON.stringify(formattedData),
|
||||
appConfig.encryptionKey
|
||||
|
@@ -4,6 +4,8 @@ import { createFlow } from './flow';
|
||||
export const createExecution = async (params: Partial<Execution> = {}) => {
|
||||
params.flowId = params?.flowId || (await createFlow()).id;
|
||||
params.testRun = params?.testRun || false;
|
||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||
|
||||
const [execution] = await global.knex
|
||||
.table('executions')
|
||||
|
@@ -4,6 +4,8 @@ import { createUser } from './user';
|
||||
export const createFlow = async (params: Partial<Flow> = {}) => {
|
||||
params.userId = params?.userId || (await createUser()).id;
|
||||
params.name = params?.name || 'Name your flow!';
|
||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||
|
||||
const [flow] = await global.knex.table('flows').insert(params).returning('*');
|
||||
|
||||
|
@@ -1,24 +1,15 @@
|
||||
import { IPermission } from '@automatisch/types';
|
||||
import Permission from '../../src/models/permission';
|
||||
import { createRole } from './role';
|
||||
|
||||
type PermissionParams = {
|
||||
roleId?: string;
|
||||
action?: string;
|
||||
subject?: string;
|
||||
};
|
||||
|
||||
export const createPermission = async (
|
||||
params: PermissionParams = {}
|
||||
): Promise<IPermission> => {
|
||||
const permissionData = {
|
||||
roleId: params?.roleId || (await createRole()).id,
|
||||
action: params?.action || 'read',
|
||||
subject: params?.subject || 'User',
|
||||
};
|
||||
export const createPermission = async (params: Partial<Permission> = {}) => {
|
||||
params.roleId = params?.roleId || (await createRole()).id;
|
||||
params.action = params?.action || 'read';
|
||||
params.subject = params?.subject || 'User';
|
||||
params.conditions = params?.conditions || ['isCreator'];
|
||||
|
||||
const [permission] = await global.knex
|
||||
.table('permissions')
|
||||
.insert(permissionData)
|
||||
.insert(params)
|
||||
.returning('*');
|
||||
|
||||
return permission;
|
||||
|
@@ -13,7 +13,9 @@ export const createStep = async (params: Partial<Step> = {}) => {
|
||||
.first();
|
||||
|
||||
params.position = params?.position || (lastStep?.position || 0) + 1;
|
||||
params.status = params?.status || 'incomplete';
|
||||
params.status = params?.status || 'completed';
|
||||
params.appKey =
|
||||
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
||||
|
||||
const [step] = await global.knex.table('steps').insert(params).returning('*');
|
||||
|
||||
|
@@ -377,7 +377,10 @@ export default defineConfig({
|
||||
text: 'Trello',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [{ text: 'Connection', link: '/apps/trello/connection' }],
|
||||
items: [
|
||||
{ text: 'Actions', link: '/apps/trello/actions' },
|
||||
{ text: 'Connection', link: '/apps/trello/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Twilio',
|
||||
@@ -389,6 +392,12 @@ export default defineConfig({
|
||||
{ text: 'Connection', link: '/apps/twilio/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Twitch',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [{ text: 'Connection', link: '/apps/twitch/connection' }],
|
||||
},
|
||||
{
|
||||
text: 'Twitter',
|
||||
collapsible: true,
|
||||
@@ -435,6 +444,12 @@ export default defineConfig({
|
||||
{ text: 'Connection', link: '/apps/youtube/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Zendesk',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [{ text: 'Connection', link: '/apps/zendesk/connection' }],
|
||||
},
|
||||
],
|
||||
'/': [
|
||||
{
|
||||
|
12
packages/docs/pages/apps/trello/actions.md
Normal file
12
packages/docs/pages/apps/trello/actions.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
favicon: /favicons/trello.svg
|
||||
items:
|
||||
- name: Create card
|
||||
desc: Creates a new card within a specified board and list.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import CustomListing from '../../components/CustomListing.vue'
|
||||
</script>
|
||||
|
||||
<CustomListing />
|
19
packages/docs/pages/apps/twitch/connection.md
Normal file
19
packages/docs/pages/apps/twitch/connection.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Twitch
|
||||
|
||||
:::info
|
||||
This page explains the steps you need to follow to set up the Twitch
|
||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||
:::
|
||||
|
||||
1. Go to the [developer console](https://dev.twitch.tv/console) to register an app.
|
||||
2. Select on the **Applications** tab and click on the **Register Your Application**.
|
||||
3. Enter a name for your app.
|
||||
4. Copy **OAuth Redirect URL** from Automatisch to **OAuth Redirect URLs** field.
|
||||
5. Select a **Category** and click on the **Create** button.
|
||||
6. Go back to **Applications** tab and choose your app under **Developer Applications**.
|
||||
7. Click the **Manage**.
|
||||
8. Copy the **Your Client ID** value from the following popup to the `Client ID` field on Automatisch.
|
||||
9. Click on the **New Secret** and generate your client secret key.
|
||||
10. Copy the **Your Client Secret** value from the following popup to the `Client Secret` field on Automatisch.
|
||||
11. Click **Submit** button on Automatisch.
|
||||
12. Congrats! Start using your new Twitch connection within the flows.
|
21
packages/docs/pages/apps/zendesk/connection.md
Normal file
21
packages/docs/pages/apps/zendesk/connection.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Zendesk
|
||||
|
||||
:::info
|
||||
This page explains the steps you need to follow to set up the Zendesk
|
||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||
:::
|
||||
|
||||
1. Fill `Zendesk Subdomain URL` with your dashboard URL, for example: `https://yourcompany.zendesk.com`.
|
||||
2. Go to your Zendesk dashboard.
|
||||
3. Click on **Zendesk Products** at the top right corner and click **Admin Center** from the dropdown.
|
||||
4. Enter **App and integrations** section.
|
||||
5. Click on **Zendesk API** from the sidebar.
|
||||
6. Click on **OAuth Clients** tab.
|
||||
7. Click on **Add OAuth Client** button.
|
||||
8. Enter necessary information in the form.
|
||||
9. Copy **OAuth Redirect URL** from Automatisch to **Redirect URLs** field in the form.
|
||||
10. Enter your preferred client ID value in **Unique Identifier** field.
|
||||
11. Save the form to complete creating the OAuth client.
|
||||
12. Copy the `Unique identifier` value from the page to the `Client ID` field on Automatisch.
|
||||
13. Copy the `Secret` value from the page to the `Client Secret` field on Automatisch.
|
||||
14. Now, you can start using the Zendesk connection with Automatisch.
|
@@ -39,6 +39,7 @@ The following integrations are currently supported by Automatisch.
|
||||
- [Stripe](/apps/stripe/triggers)
|
||||
- [Telegram](/apps/telegram-bot/actions)
|
||||
- [Todoist](/apps/todoist/triggers)
|
||||
- [Trello](/apps/trello/actions)
|
||||
- [Twilio](/apps/twilio/triggers)
|
||||
- [Twitter](/apps/twitter/triggers)
|
||||
- [Typeform](/apps/typeform/triggers)
|
||||
|
1
packages/docs/pages/public/favicons/twitch.svg
Normal file
1
packages/docs/pages/public/favicons/twitch.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" overflow="visible" width="40" height="40" version="1.1" viewBox="0 0 40 40" x="0px" y="0px" class="ScSvg-sc-mx5axi-2 iAAiAK"><g fill="#5C16C5"><polygon points="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" class="ScBody-sc-mx5axi-3 dosCbL" fill="#9147FF"><animate dur="150ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" from="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" to="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5"></animate><animate dur="250ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" from="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5" to="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8"></animate><animate dur="50ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" to="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" from="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5"></animate><animate dur="75ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="points" to="16 5 8 13 8 31 14 31 14 36 19 31 23 31 35 19 35 5" from="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8"></animate></polygon><polygon points="26 25 30 21 30 10 14 10 14 25 18 25 18 29 22 25" class="ScFace-sc-mx5axi-4 fDFkyX" fill="#FFFFFF"><animateTransform dur="150ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform><animateTransform dur="250ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="50ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="75ms" begin="indefinite" fill="#FFFFFF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform></polygon><g class="ScEyes-sc-mx5axi-5 fAMMxB" fill="#5C16C5"><path d="M20,14 L22,14 L22,20 L20,20 L20,14 Z M27,14 L27,20 L25,20 L25,14 L27,14 Z" class="ScBody-sc-mx5axi-3 dosCbL" fill="#9147FF"><animateTransform dur="150ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform><animateTransform dur="250ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="50ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="3 -3" to="0 0"></animateTransform><animateTransform dur="75ms" begin="indefinite" fill="#9147FF" calcMode="spline" keyTimes="0; 1" keySplines="0.25 0.1 0.25 1" attributeName="transform" type="translate" from="0 0" to="3 -3"></animateTransform></path></g></g></svg>
|
After Width: | Height: | Size: 3.3 KiB |
1
packages/docs/pages/public/favicons/zendesk.svg
Normal file
1
packages/docs/pages/public/favicons/zendesk.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="363" height="259" fill="#03363d"><path d="M173.82 40.5v112.86H80.34L173.82 40.5zm0-40.5a46.74 46.74 0 1 1-93.48 0h93.48zm15.4 153.37a46.74 46.74 0 0 1 93.48 0h-93.48zm0-40.5V0h93.5l-93.5 112.86zm52.28 137.06a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H227c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06h25.68zM0 249.4l28.3-28.76H.67v-9.02h40.76v9.2l-28.3 28.75h28.7v9.03H0v-9.2zm73.6.52a18.22 18.22 0 0 0 12.95-5l6.42 6.93c-4.24 4.36-10.12 7.6-19.26 7.6-15.67 0-25.8-10.4-25.8-24.46a24 24 0 0 1 24.37-24.47c15.56 0 24.38 11.84 23.6 28.26H59.12c1.3 6.82 6.1 11.17 14.47 11.17m11.2-19c-1-6.37-4.8-11.06-12.4-11.06-7.07 0-12 4-13.27 11.06H84.8zm72.23 4.03c0-15 11.23-24.44 23.6-24.44a20.34 20.34 0 0 1 15.67 7.05v-27.72h10v68.6h-10V252a20.1 20.1 0 0 1-15.76 7.42c-12 0-23.5-9.5-23.5-24.43m39.82-.1a14.92 14.92 0 1 0-14.91 15.32c8.6 0 14.9-6.86 14.9-15.32m73.48 13.6l9.06-4.7a13.44 13.44 0 0 0 12.08 6.86c5.66 0 8.6-2.9 8.6-6.2 0-3.76-5.47-4.6-11.42-5.83-8-1.7-16.33-4.33-16.33-14 0-7.43 7.07-14.3 18.2-14.2 8.77 0 15.3 3.48 19 9.1l-8.4 4.6a12.19 12.19 0 0 0-10.57-5.36c-5.38 0-8.12 2.63-8.12 5.64 0 3.38 4.34 4.32 11.14 5.83 7.74 1.7 16.5 4.23 16.5 14 0 6.48-5.66 15.22-19.06 15.13-9.8 0-16.7-3.95-20.67-10.9m66.9-10.87l-7.93 8.65v12.2h-10v-68.6h10v44.93l21.23-23.3h12.18l-18.4 20.1 18.88 26.88h-11.32l-14.63-20.86zM126.8 210.53c-11.9 0-21.85 7.7-21.85 20.5v27.45h10.2V232.3c0-7.7 4.43-12.32 12-12.32s11.33 4.6 11.33 12.32v26.18h10.14v-27.45c0-12.78-10-20.5-21.85-20.5"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
30
packages/e2e-tests/fixtures/admin/create-user-page.js
Normal file
30
packages/e2e-tests/fixtures/admin/create-user-page.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const { faker } = require('@faker-js/faker');
|
||||
const { AuthenticatedPage } = require('../authenticated-page');
|
||||
|
||||
export class AdminCreateUserPage extends AuthenticatedPage {
|
||||
screenshot = '/admin/create-user';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
constructor (page) {
|
||||
super(page);
|
||||
this.fullNameInput = page.getByTestId('full-name-input');
|
||||
this.emailInput = page.getByTestId('email-input');
|
||||
this.passwordInput = page.getByTestId('password-input');
|
||||
this.roleInput = page.getByTestId('role.id-autocomplete');
|
||||
this.createButton = page.getByTestId('create-button');
|
||||
}
|
||||
|
||||
seed (seed) {
|
||||
faker.seed(seed || 0);
|
||||
}
|
||||
|
||||
generateUser () {
|
||||
return {
|
||||
fullName: faker.person.fullName(),
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: faker.internet.password()
|
||||
}
|
||||
}
|
||||
}
|
19
packages/e2e-tests/fixtures/admin/delete-user-modal.js
Normal file
19
packages/e2e-tests/fixtures/admin/delete-user-modal.js
Normal file
@@ -0,0 +1,19 @@
|
||||
export class DeleteUserModal {
|
||||
screenshotPath = '/admin/delete-modal';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
constructor (page) {
|
||||
this.page = page;
|
||||
this.modal = page.getByTestId('delete-user-modal');
|
||||
this.cancelButton = this.modal.getByTestId('confirmation-cancel-button');
|
||||
this.deleteButton = this.modal.getByTestId('confirmation-confirm-button');
|
||||
}
|
||||
|
||||
async close () {
|
||||
await this.page.click('body', {
|
||||
position: { x: 10, y: 10 }
|
||||
})
|
||||
}
|
||||
}
|
25
packages/e2e-tests/fixtures/admin/edit-user-page.js
Normal file
25
packages/e2e-tests/fixtures/admin/edit-user-page.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { faker } = require('@faker-js/faker');
|
||||
const { AuthenticatedPage } = require('../authenticated-page');
|
||||
|
||||
faker.seed(9002);
|
||||
|
||||
export class AdminEditUserPage extends AuthenticatedPage {
|
||||
screenshot = '/admin/edit-user';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
constructor (page) {
|
||||
super(page);
|
||||
this.fullNameInput = page.getByTestId('full-name-input');
|
||||
this.emailInput = page.getByTestId('email-input');
|
||||
this.updateButton = page.getByTestId('update-button');
|
||||
}
|
||||
|
||||
generateUser () {
|
||||
return {
|
||||
fullName: faker.person.fullName(),
|
||||
email: faker.internet.email(),
|
||||
}
|
||||
}
|
||||
}
|
15
packages/e2e-tests/fixtures/admin/index.js
Normal file
15
packages/e2e-tests/fixtures/admin/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const { AdminCreateUserPage } = require('./create-user-page');
|
||||
const { AdminEditUserPage } = require('./edit-user-page');
|
||||
const { AdminUsersPage } = require('./users-page');
|
||||
|
||||
export const adminFixtures = {
|
||||
adminUsersPage: async ({ page }, use) => {
|
||||
await use(new AdminUsersPage(page));
|
||||
},
|
||||
adminCreateUserPage: async ({ page }, use) => {
|
||||
await use(new AdminCreateUserPage(page));
|
||||
},
|
||||
adminEditUserPage: async ({page}, use) => {
|
||||
await use(new AdminEditUserPage(page));
|
||||
}
|
||||
}
|
115
packages/e2e-tests/fixtures/admin/users-page.js
Normal file
115
packages/e2e-tests/fixtures/admin/users-page.js
Normal file
@@ -0,0 +1,115 @@
|
||||
const { faker } = require('@faker-js/faker');
|
||||
const { AuthenticatedPage } = require('../authenticated-page');
|
||||
const { DeleteUserModal } = require('./delete-user-modal');
|
||||
|
||||
faker.seed(9001);
|
||||
|
||||
export class AdminUsersPage extends AuthenticatedPage {
|
||||
screenshotPath = '/admin';
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
constructor (page) {
|
||||
super(page);
|
||||
this.createUserButton = page.getByTestId('create-user');
|
||||
this.userRow = page.getByTestId('user-row');
|
||||
this.deleteUserModal = new DeleteUserModal(page);
|
||||
this.firstPageButton = page.getByTestId('first-page-button');
|
||||
this.previousPageButton = page.getByTestId('previous-page-button');
|
||||
this.nextPageButton = page.getByTestId('next-page-button');
|
||||
this.lastPageButton = page.getByTestId('last-page-button');
|
||||
this.usersLoader = page.getByTestId('users-list-loader');
|
||||
}
|
||||
|
||||
async navigateTo () {
|
||||
await this.profileMenuButton.click();
|
||||
await this.adminMenuItem.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
*/
|
||||
async getUserRowByEmail (email) {
|
||||
return this.userRow.filter({
|
||||
has: this.page.getByTestId('user-email').filter({
|
||||
hasText: email
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Locator} row
|
||||
*/
|
||||
async getRowData (row) {
|
||||
return {
|
||||
fullName: await row.getByTestId('user-full-name').textContent(),
|
||||
email: await row.getByTestId('user-email').textContent(),
|
||||
role: await row.getByTestId('user-role').textContent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Locator} row
|
||||
*/
|
||||
async clickEditUser (row) {
|
||||
await row.getByTestId('user-edit').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Locator} row
|
||||
*/
|
||||
async clickDeleteUser (row) {
|
||||
await row.getByTestId('delete-button').click();
|
||||
return this.deleteUserModal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
*/
|
||||
async findUserPageWithEmail (email) {
|
||||
// start at the first page
|
||||
const firstPageDisabled = await this.firstPageButton.isDisabled();
|
||||
if (!firstPageDisabled) {
|
||||
await this.firstPageButton.click();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const rowLocator = await this.getUserRowByEmail(email);
|
||||
if ((await rowLocator.count()) === 1) {
|
||||
return rowLocator;
|
||||
}
|
||||
if (await this.nextPageButton.isDisabled()) {
|
||||
return null;
|
||||
} else {
|
||||
await this.nextPageButton.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getTotalRows () {
|
||||
return await this.page.evaluate(() => {
|
||||
const node = document.querySelector('[data-total-count]');
|
||||
if (node) {
|
||||
const count = Number(node.dataset.totalCount);
|
||||
if (!isNaN(count)) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
async getRowsPerPage () {
|
||||
return await this.page.evaluate(() => {
|
||||
const node = document.querySelector('[data-rows-per-page]');
|
||||
if (node) {
|
||||
const count = Number(node.dataset.rowsPerPage);
|
||||
if (!isNaN(count)) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,5 +1,11 @@
|
||||
const path = require('node:path');
|
||||
|
||||
/**
|
||||
* @typedef {(
|
||||
* 'default' | 'success' | 'warning' | 'error' | 'info'
|
||||
* )} SnackbarVariant - Snackbar variant types in notistack/v3, see https://notistack.com/api-reference
|
||||
*/
|
||||
|
||||
export class BasePage {
|
||||
screenshotPath = '/';
|
||||
|
||||
@@ -8,7 +14,53 @@ export class BasePage {
|
||||
*/
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
this.snackbar = this.page.locator('#notistack-snackbar');
|
||||
this.snackbar = page.locator('*[data-test^="snackbar"]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the latest snackbar message and extracts relevant data
|
||||
* @param {string | undefined} testId
|
||||
* @returns {(
|
||||
* null | {
|
||||
* variant: SnackbarVariant,
|
||||
* text: string,
|
||||
* dataset: { [key: string]: string }
|
||||
* }
|
||||
* )}
|
||||
*/
|
||||
async getSnackbarData (testId) {
|
||||
if (!testId) {
|
||||
testId = 'snackbar';
|
||||
}
|
||||
const snack = this.page.getByTestId(testId);
|
||||
return {
|
||||
variant: await snack.getAttribute('data-snackbar-variant'),
|
||||
text: await snack.evaluate(node => node.innerText),
|
||||
dataset: await snack.evaluate(node => {
|
||||
function getChildren (n) {
|
||||
return [n].concat(
|
||||
...Array.from(n.children).map(c => getChildren(c))
|
||||
);
|
||||
}
|
||||
const datasets = getChildren(node).map(
|
||||
n => Object.assign({}, n.dataset)
|
||||
);
|
||||
return Object.assign({}, ...datasets);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all snackbars, should be replaced later
|
||||
*/
|
||||
async closeSnackbar () {
|
||||
const snackbars = await this.snackbar.all();
|
||||
for (const snackbar of snackbars) {
|
||||
await snackbar.click();
|
||||
}
|
||||
for (const snackbar of snackbars) {
|
||||
await snackbar.waitFor({ state: 'detached' });
|
||||
}
|
||||
}
|
||||
|
||||
async clickAway() {
|
||||
|
@@ -5,6 +5,7 @@ const { ExecutionsPage } = require('./executions-page');
|
||||
const { FlowEditorPage } = require('./flow-editor-page');
|
||||
const { UserInterfacePage } = require('./user-interface-page');
|
||||
const { LoginPage } = require('./login-page');
|
||||
const { adminFixtures } = require('./admin');
|
||||
|
||||
exports.test = test.extend({
|
||||
page: async ({ page }, use) => {
|
||||
@@ -31,6 +32,7 @@ exports.test = test.extend({
|
||||
userInterfacePage: async ({ page }, use) => {
|
||||
await use(new UserInterfacePage(page));
|
||||
},
|
||||
...adminFixtures
|
||||
});
|
||||
|
||||
exports.publicTest = test.extend({
|
||||
|
@@ -24,10 +24,17 @@
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.2.0",
|
||||
"@playwright/test": "^1.36.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"micro": "^10.0.1"
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"micro": "^10.0.1",
|
||||
"prettier": "^2.5.1"
|
||||
}
|
||||
}
|
||||
|
287
packages/e2e-tests/tests/admin/manage-users.spec.js
Normal file
287
packages/e2e-tests/tests/admin/manage-users.spec.js
Normal file
@@ -0,0 +1,287 @@
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
/**
|
||||
* NOTE: Make sure to delete all users generated between test runs,
|
||||
* otherwise tests will fail since users are only *soft*-deleted
|
||||
*/
|
||||
test.describe('User management page', () => {
|
||||
|
||||
test.beforeEach(async ({ adminUsersPage }) => {
|
||||
await adminUsersPage.navigateTo();
|
||||
await adminUsersPage.closeSnackbar();
|
||||
});
|
||||
|
||||
test(
|
||||
'User creation and deletion process',
|
||||
async ({ adminCreateUserPage, adminEditUserPage, adminUsersPage }) => {
|
||||
adminCreateUserPage.seed(9000);
|
||||
const user = adminCreateUserPage.generateUser();
|
||||
await adminUsersPage.usersLoader.waitFor({
|
||||
state: 'detached' /* Note: state: 'visible' introduces flakiness
|
||||
because visibility: hidden is used as part of the state transition in
|
||||
notistack, see
|
||||
https://github.com/iamhosseindhv/notistack/blob/122f47057eb7ce5a1abfe923316cf8475303e99a/src/transitions/Collapse/Collapse.tsx#L110
|
||||
*/
|
||||
});
|
||||
await test.step(
|
||||
'Create a user',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(user.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(user.email);
|
||||
await adminCreateUserPage.passwordInput.fill(user.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-create-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
await test.step(
|
||||
'Check the user exists with the expected properties',
|
||||
async () => {
|
||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||
const data = await adminUsersPage.getRowData(userRow);
|
||||
await expect(data.email).toBe(user.email);
|
||||
await expect(data.fullName).toBe(user.fullName);
|
||||
await expect(data.role).toBe('Admin');
|
||||
}
|
||||
);
|
||||
await test.step(
|
||||
'Edit user info and make sure the edit works correctly',
|
||||
async () => {
|
||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||
|
||||
let userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||
await adminUsersPage.clickEditUser(userRow);
|
||||
const newUserInfo = adminEditUserPage.generateUser();
|
||||
await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName);
|
||||
await adminEditUserPage.updateButton.click();
|
||||
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-edit-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
|
||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||
userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||
const rowData = await adminUsersPage.getRowData(userRow);
|
||||
await expect(rowData.fullName).toBe(newUserInfo.fullName);
|
||||
}
|
||||
);
|
||||
await test.step(
|
||||
'Delete user and check the page confirms this deletion',
|
||||
async () => {
|
||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||
await adminUsersPage.clickDeleteUser(userRow);
|
||||
const modal = adminUsersPage.deleteUserModal;
|
||||
await modal.deleteButton.click();
|
||||
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-delete-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
await expect(userRow).not.toBeVisible(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'Creating a user which has been deleted',
|
||||
async ({ adminCreateUserPage, adminUsersPage }) => {
|
||||
adminCreateUserPage.seed(9100);
|
||||
const testUser = adminCreateUserPage.generateUser();
|
||||
|
||||
await test.step(
|
||||
'Create the test user',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||
await adminCreateUserPage.passwordInput.fill(testUser.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-create-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Delete the created user',
|
||||
async () => {
|
||||
await adminUsersPage.findUserPageWithEmail(testUser.email);
|
||||
const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
|
||||
await adminUsersPage.clickDeleteUser(userRow);
|
||||
const modal = adminUsersPage.deleteUserModal;
|
||||
await modal.deleteButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-delete-user-success'
|
||||
);
|
||||
await expect(snackbar).not.toBeNull();
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
await expect(userRow).not.toBeVisible(false);
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Create the user again',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||
await adminCreateUserPage.passwordInput.fill(testUser.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
await adminUsersPage.snackbar.waitFor({
|
||||
state: 'attached'
|
||||
});
|
||||
/*
|
||||
TODO: assert snackbar behavior after deciding what should
|
||||
happen here, i.e. if this should create a new user, stay the
|
||||
same, un-delete the user, or something else
|
||||
*/
|
||||
// await adminUsersPage.getSnackbarData('snackbar-error');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
'Creating a user which already exists',
|
||||
async ({ adminCreateUserPage, adminUsersPage, page }) => {
|
||||
adminCreateUserPage.seed(9200);
|
||||
const testUser = adminCreateUserPage.generateUser();
|
||||
|
||||
await test.step(
|
||||
'Create the test user',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||
await adminCreateUserPage.passwordInput.fill(testUser.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-create-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Create the user again',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||
await adminCreateUserPage.passwordInput.fill(testUser.password);
|
||||
const createUserPageUrl = page.url();
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
|
||||
await expect(page.url()).toBe(createUserPageUrl);
|
||||
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
||||
await expect(snackbar.variant).toBe('error');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
'Editing a user to have the same email as another user should not be allowed',
|
||||
async ({
|
||||
adminCreateUserPage, adminEditUserPage, adminUsersPage, page
|
||||
}) => {
|
||||
adminCreateUserPage.seed(9300);
|
||||
const user1 = adminCreateUserPage.generateUser();
|
||||
const user2 = adminCreateUserPage.generateUser();
|
||||
await test.step(
|
||||
'Create the first user',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(user1.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(user1.email);
|
||||
await adminCreateUserPage.passwordInput.fill(user1.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-create-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Create the second user',
|
||||
async () => {
|
||||
await adminUsersPage.createUserButton.click();
|
||||
await adminCreateUserPage.fullNameInput.fill(user2.fullName);
|
||||
await adminCreateUserPage.emailInput.fill(user2.email);
|
||||
await adminCreateUserPage.passwordInput.fill(user2.password);
|
||||
await adminCreateUserPage.roleInput.click();
|
||||
await adminCreateUserPage.page.getByRole(
|
||||
'option', { name: 'Admin' }
|
||||
).click();
|
||||
await adminCreateUserPage.createButton.click();
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-create-user-success'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
}
|
||||
);
|
||||
|
||||
await test.step(
|
||||
'Try editing the second user to have the email of the first user',
|
||||
async () => {
|
||||
await adminUsersPage.findUserPageWithEmail(user2.email);
|
||||
let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
|
||||
await adminUsersPage.clickEditUser(userRow);
|
||||
|
||||
await adminEditUserPage.emailInput.fill(user1.email);
|
||||
const editPageUrl = page.url();
|
||||
await adminEditUserPage.updateButton.click();
|
||||
|
||||
const snackbar = await adminUsersPage.getSnackbarData(
|
||||
'snackbar-error'
|
||||
);
|
||||
await expect(snackbar.variant).toBe('error');
|
||||
await adminUsersPage.closeSnackbar();
|
||||
await expect(page.url()).toBe(editPageUrl);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
8
packages/types/index.d.ts
vendored
8
packages/types/index.d.ts
vendored
@@ -51,8 +51,8 @@ export interface IExecution {
|
||||
testRun: boolean;
|
||||
status: 'success' | 'failure';
|
||||
executionSteps: IExecutionStep[];
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
updatedAt: string | Date;
|
||||
createdAt: string | Date;
|
||||
}
|
||||
|
||||
export interface IStep {
|
||||
@@ -83,8 +83,8 @@ export interface IFlow {
|
||||
active: boolean;
|
||||
status: 'paused' | 'published' | 'draft';
|
||||
steps: IStep[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
createdAt: string | Date;
|
||||
updatedAt: string | Date;
|
||||
remoteWebhookId: string;
|
||||
lastInternalId: () => Promise<string>;
|
||||
}
|
||||
|
@@ -6,13 +6,13 @@ import Paper from '@mui/material/Paper';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import LoadingButton from '@mui/lab/LoadingButton';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useSnackbar } from 'notistack';
|
||||
|
||||
import { CREATE_APP_CONFIG } from 'graphql/mutations/create-app-config';
|
||||
import { UPDATE_APP_CONFIG } from 'graphql/mutations/update-app-config';
|
||||
|
||||
import Form from 'components/Form';
|
||||
import { Switch } from './style';
|
||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||
|
||||
type AdminApplicationSettingsProps = {
|
||||
appKey: string;
|
||||
@@ -36,7 +36,7 @@ function AdminApplicationSettings(
|
||||
);
|
||||
|
||||
const formatMessage = useFormatMessage();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const enqueueSnackbar = useEnqueueSnackbar();
|
||||
|
||||
const handleSubmit = async (values: any) => {
|
||||
try {
|
||||
@@ -55,6 +55,9 @@ function AdminApplicationSettings(
|
||||
}
|
||||
enqueueSnackbar(formatMessage('adminAppsSettings.successfullySaved'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-save-admin-apps-settings-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while saving!');
|
||||
|
@@ -15,7 +15,12 @@ const ApolloProvider = (props: ApolloProviderProps): React.ReactElement => {
|
||||
|
||||
const onError = React.useCallback(
|
||||
(message) => {
|
||||
enqueueSnackbar(message, { variant: 'error' });
|
||||
enqueueSnackbar(message, {
|
||||
variant: 'error',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-error'
|
||||
}
|
||||
});
|
||||
},
|
||||
[enqueueSnackbar]
|
||||
);
|
||||
|
@@ -82,6 +82,9 @@ function AppConnectionRow(props: AppConnectionRowProps): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('connection.deletedMessage'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-delete-connection-success'
|
||||
}
|
||||
});
|
||||
} else if (action.type === 'test') {
|
||||
setVerificationVisible(true);
|
||||
|
@@ -14,6 +14,7 @@ type ConfirmationDialogProps = {
|
||||
cancelButtonChildren: React.ReactNode;
|
||||
confirmButtionChildren: React.ReactNode;
|
||||
open?: boolean;
|
||||
'data-test'?: string;
|
||||
}
|
||||
|
||||
export default function ConfirmationDialog(props: ConfirmationDialogProps) {
|
||||
@@ -26,9 +27,9 @@ export default function ConfirmationDialog(props: ConfirmationDialogProps) {
|
||||
confirmButtionChildren,
|
||||
open = true,
|
||||
} = props;
|
||||
|
||||
const dataTest = props['data-test'];
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<Dialog open={open} onClose={onClose} data-test={dataTest}>
|
||||
{title && (
|
||||
<DialogTitle>
|
||||
{title}
|
||||
@@ -44,11 +45,16 @@ export default function ConfirmationDialog(props: ConfirmationDialogProps) {
|
||||
|
||||
<DialogActions>
|
||||
{(cancelButtonChildren && onClose) && (
|
||||
<Button onClick={onClose}>{cancelButtonChildren}</Button>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
data-test="confirmation-cancel-button">{cancelButtonChildren}</Button>
|
||||
)}
|
||||
|
||||
{(confirmButtionChildren && onConfirm) && (
|
||||
<Button onClick={onConfirm} color="error">
|
||||
<Button
|
||||
onClick={onConfirm}
|
||||
color="error"
|
||||
data-test="confirmation-confirm-button">
|
||||
{confirmButtionChildren}
|
||||
</Button>
|
||||
)}
|
||||
|
@@ -31,6 +31,9 @@ export default function DeleteRoleButton(props: DeleteRoleButtonProps) {
|
||||
setShowConfirmation(false);
|
||||
enqueueSnackbar(formatMessage('deleteRoleButton.successfullyDeleted'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-delete-role-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while deleting!');
|
||||
|
@@ -29,6 +29,9 @@ export default function DeleteUserButton(props: DeleteUserButtonProps) {
|
||||
setShowConfirmation(false);
|
||||
enqueueSnackbar(formatMessage('deleteUserButton.successfullyDeleted'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-delete-user-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while deleting!');
|
||||
@@ -37,7 +40,7 @@ export default function DeleteUserButton(props: DeleteUserButtonProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={() => setShowConfirmation(true)} size="small">
|
||||
<IconButton data-test="delete-button" onClick={() => setShowConfirmation(true)} size="small">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -49,6 +52,7 @@ export default function DeleteUserButton(props: DeleteUserButtonProps) {
|
||||
onConfirm={handleConfirm}
|
||||
cancelButtonChildren={formatMessage('deleteUserButton.cancel')}
|
||||
confirmButtionChildren={formatMessage('deleteUserButton.confirm')}
|
||||
data-test="delete-user-modal"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@@ -39,11 +39,15 @@ function ExecutionId(props: Pick<IExecution, 'id'>) {
|
||||
}
|
||||
|
||||
function ExecutionDate(props: Pick<IExecution, 'createdAt'>) {
|
||||
const createdAt = DateTime.fromMillis(parseInt(props.createdAt, 10));
|
||||
const createdAt = DateTime.fromMillis(
|
||||
parseInt(props.createdAt as string, 10)
|
||||
);
|
||||
const relativeCreatedAt = createdAt.toRelative();
|
||||
|
||||
return (
|
||||
<Tooltip title={createdAt.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}>
|
||||
<Tooltip
|
||||
title={createdAt.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
|
||||
>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{relativeCreatedAt}
|
||||
</Typography>
|
||||
|
@@ -23,8 +23,10 @@ export default function ExecutionRow(
|
||||
const { execution } = props;
|
||||
const { flow } = execution;
|
||||
|
||||
const updatedAt = DateTime.fromMillis(parseInt(execution.updatedAt, 10));
|
||||
const relativeUpdatedAt = updatedAt.toRelative();
|
||||
const createdAt = DateTime.fromMillis(
|
||||
parseInt(execution.createdAt as string, 10)
|
||||
);
|
||||
const relativeCreatedAt = createdAt.toRelative();
|
||||
|
||||
return (
|
||||
<Link to={URLS.EXECUTION(execution.id)} data-test="execution-row">
|
||||
@@ -41,8 +43,8 @@ export default function ExecutionRow(
|
||||
</Typography>
|
||||
|
||||
<Typography variant="caption" noWrap>
|
||||
{formatMessage('execution.updatedAt', {
|
||||
datetime: relativeUpdatedAt,
|
||||
{formatMessage('execution.createdAt', {
|
||||
datetime: relativeCreatedAt,
|
||||
})}
|
||||
</Typography>
|
||||
</Title>
|
||||
|
@@ -36,6 +36,9 @@ export default function ContextMenu(
|
||||
|
||||
enqueueSnackbar(formatMessage('flow.successfullyDuplicated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-duplicate-flow-success'
|
||||
}
|
||||
});
|
||||
|
||||
onClose();
|
||||
|
@@ -65,8 +65,8 @@ export default function FlowRow(props: FlowRowProps): React.ReactElement {
|
||||
setAnchorEl(contextButtonRef.current);
|
||||
};
|
||||
|
||||
const createdAt = DateTime.fromMillis(parseInt(flow.createdAt, 10));
|
||||
const updatedAt = DateTime.fromMillis(parseInt(flow.updatedAt, 10));
|
||||
const createdAt = DateTime.fromMillis(parseInt(flow.createdAt as string, 10));
|
||||
const updatedAt = DateTime.fromMillis(parseInt(flow.updatedAt as string, 10));
|
||||
const isUpdated = updatedAt > createdAt;
|
||||
const relativeCreatedAt = createdAt.toRelative();
|
||||
const relativeUpdatedAt = updatedAt.toRelative();
|
||||
|
@@ -43,6 +43,9 @@ export default function ResetPasswordForm() {
|
||||
|
||||
enqueueSnackbar(formatMessage('resetPasswordForm.passwordUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-reset-password-success'
|
||||
}
|
||||
});
|
||||
|
||||
navigate(URLS.LOGIN);
|
||||
|
@@ -52,6 +52,7 @@ export default function TablePaginationActions(
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="first page"
|
||||
data-test="first-page-button"
|
||||
>
|
||||
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
|
||||
</IconButton>
|
||||
@@ -59,6 +60,7 @@ export default function TablePaginationActions(
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="previous page"
|
||||
data-test="previous-page-button"
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowRight />
|
||||
@@ -70,6 +72,7 @@ export default function TablePaginationActions(
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="next page"
|
||||
data-test="next-page-button"
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowLeft />
|
||||
@@ -81,6 +84,7 @@ export default function TablePaginationActions(
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="last page"
|
||||
data-test="last-page-button"
|
||||
>
|
||||
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
|
||||
</IconButton>
|
||||
|
@@ -83,23 +83,34 @@ export default function UserList(): React.ReactElement {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading && <ListLoader rowsNumber={3} columnsNumber={2} />}
|
||||
{loading && <ListLoader
|
||||
data-test="users-list-loader"
|
||||
rowsNumber={3}
|
||||
columnsNumber={2} />}
|
||||
{!loading &&
|
||||
users.map((user) => (
|
||||
<TableRow
|
||||
key={user.id}
|
||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||
data-test="user-row"
|
||||
>
|
||||
<TableCell scope="row">
|
||||
<Typography variant="subtitle2">{user.fullName}</Typography>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
data-test="user-full-name">{user.fullName}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2">{user.email}</Typography>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
data-test="user-email">{user.email}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography variant="subtitle2">
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
data-test="user-role"
|
||||
>
|
||||
{user.role.name}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
@@ -110,11 +121,14 @@ export default function UserList(): React.ReactElement {
|
||||
size="small"
|
||||
component={Link}
|
||||
to={URLS.USER(user.id)}
|
||||
data-test="user-edit"
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
|
||||
<DeleteUserButton userId={user.id} />
|
||||
<DeleteUserButton
|
||||
data-test="user-delete"
|
||||
userId={user.id} />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -124,6 +138,8 @@ export default function UserList(): React.ReactElement {
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
data-total-count={totalCount}
|
||||
data-rows-per-page={rowsPerPage}
|
||||
rowsPerPageOptions={[10, 25, 50, 100]}
|
||||
page={page}
|
||||
count={totalCount}
|
||||
|
@@ -20,7 +20,11 @@ export default function useEnqueueSnackbar() {
|
||||
...(options || {}) as Record<string, unknown>,
|
||||
SnackbarProps: {
|
||||
onClick: () => closeSnackbar(key),
|
||||
...(options.SnackbarProps || {}) as Record<string, unknown>
|
||||
...({
|
||||
'data-test': 'snackbar', // keep above options.snackbarProps
|
||||
'data-snackbar-variant': `${options.variant}` || 'default',
|
||||
}) as Record<string, string>,
|
||||
...(options.SnackbarProps || {}) as Record<string, unknown>,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -12,6 +12,7 @@ import LiveChat from 'components/LiveChat/index.ee';
|
||||
import routes from 'routes';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<SnackbarProvider>
|
||||
|
@@ -89,7 +89,7 @@
|
||||
"executions.title": "Executions",
|
||||
"executions.noExecutions": "There is no execution data point to show.",
|
||||
"execution.id": "Execution ID: {id}",
|
||||
"execution.updatedAt": "updated {datetime}",
|
||||
"execution.createdAt": "created {datetime}",
|
||||
"execution.test": "Test run",
|
||||
"execution.statusSuccess": "Success",
|
||||
"execution.statusFailure": "Failure",
|
||||
|
@@ -64,6 +64,9 @@ function RoleMappings({ provider, providerLoading }: RoleMappingsProps) {
|
||||
});
|
||||
enqueueSnackbar(formatMessage('roleMappingsForm.successfullySaved'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-update-role-mappings-success'
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
@@ -93,6 +93,9 @@ function SamlConfiguration({
|
||||
|
||||
enqueueSnackbar(formatMessage('authenticationForm.successfullySaved'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-save-saml-provider-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while saving!');
|
||||
|
@@ -43,6 +43,9 @@ export default function CreateRole(): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('createRole.successfullyCreated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-create-role-success'
|
||||
}
|
||||
});
|
||||
|
||||
navigate(URLS.ROLES);
|
||||
|
@@ -47,6 +47,10 @@ export default function CreateUser(): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('createUser.successfullyCreated'), {
|
||||
variant: 'success',
|
||||
persist: true,
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-create-user-success',
|
||||
}
|
||||
});
|
||||
|
||||
navigate(URLS.USERS);
|
||||
@@ -69,6 +73,7 @@ export default function CreateUser(): React.ReactElement {
|
||||
required={true}
|
||||
name="fullName"
|
||||
label={formatMessage('userForm.fullName')}
|
||||
data-test="full-name-input"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
@@ -76,6 +81,7 @@ export default function CreateUser(): React.ReactElement {
|
||||
required={true}
|
||||
name="email"
|
||||
label={formatMessage('userForm.email')}
|
||||
data-test="email-input"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
@@ -84,6 +90,7 @@ export default function CreateUser(): React.ReactElement {
|
||||
name="password"
|
||||
label={formatMessage('userForm.password')}
|
||||
type="password"
|
||||
data-test="password-input"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
@@ -110,6 +117,7 @@ export default function CreateUser(): React.ReactElement {
|
||||
color="primary"
|
||||
sx={{ boxShadow: 2 }}
|
||||
loading={loading}
|
||||
data-test="create-button"
|
||||
>
|
||||
{formatMessage('createUser.submit')}
|
||||
</LoadingButton>
|
||||
|
@@ -53,6 +53,9 @@ export default function EditRole(): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('editRole.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-edit-role-success'
|
||||
}
|
||||
});
|
||||
|
||||
navigate(URLS.ROLES);
|
||||
|
@@ -55,6 +55,10 @@ export default function EditUser(): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('editUser.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-edit-user-success',
|
||||
persist: true
|
||||
}
|
||||
});
|
||||
|
||||
navigate(URLS.USERS);
|
||||
@@ -87,6 +91,7 @@ export default function EditUser(): React.ReactElement {
|
||||
required={true}
|
||||
name="fullName"
|
||||
label={formatMessage('userForm.fullName')}
|
||||
data-test="full-name-input"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
@@ -94,6 +99,7 @@ export default function EditUser(): React.ReactElement {
|
||||
required={true}
|
||||
name="email"
|
||||
label={formatMessage('userForm.email')}
|
||||
data-test="email-input"
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
@@ -120,6 +126,7 @@ export default function EditUser(): React.ReactElement {
|
||||
color="primary"
|
||||
sx={{ boxShadow: 2 }}
|
||||
loading={loading}
|
||||
data-test="update-button"
|
||||
>
|
||||
{formatMessage('editUser.submit')}
|
||||
</LoadingButton>
|
||||
|
@@ -78,6 +78,9 @@ function ProfileSettings() {
|
||||
|
||||
enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-update-profile-settings-success'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -128,6 +128,9 @@ export default function UserInterface(): React.ReactElement {
|
||||
|
||||
enqueueSnackbar(formatMessage('userInterfacePage.successfullyUpdated'), {
|
||||
variant: 'success',
|
||||
SnackbarProps: {
|
||||
'data-test': 'snackbar-update-user-interface-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Failed while updating!');
|
||||
|
372
yarn.lock
372
yarn.lock
@@ -125,6 +125,14 @@
|
||||
"@algolia/logger-common" "4.12.0"
|
||||
"@algolia/requester-common" "4.12.0"
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
|
||||
integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@apideck/better-ajv-errors@^0.3.1":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.2.tgz"
|
||||
@@ -190,7 +198,12 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz"
|
||||
integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==
|
||||
|
||||
"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.8.0":
|
||||
"@babel/compat-data@^7.22.9":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.2.tgz#6a12ced93455827037bfb5ed8492820d60fc32cc"
|
||||
integrity sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==
|
||||
|
||||
"@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.8.0":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz"
|
||||
integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==
|
||||
@@ -232,6 +245,27 @@
|
||||
semver "^6.3.0"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/core@^7.11.6":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.2.tgz#ed10df0d580fff67c5f3ee70fd22e2e4c90a9f94"
|
||||
integrity sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.22.13"
|
||||
"@babel/generator" "^7.23.0"
|
||||
"@babel/helper-compilation-targets" "^7.22.15"
|
||||
"@babel/helper-module-transforms" "^7.23.0"
|
||||
"@babel/helpers" "^7.23.2"
|
||||
"@babel/parser" "^7.23.0"
|
||||
"@babel/template" "^7.22.15"
|
||||
"@babel/traverse" "^7.23.2"
|
||||
"@babel/types" "^7.23.0"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/eslint-parser@^7.12.16":
|
||||
version "7.17.0"
|
||||
resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz"
|
||||
@@ -294,6 +328,17 @@
|
||||
browserslist "^4.17.5"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.22.15":
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52"
|
||||
integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.22.9"
|
||||
"@babel/helper-validator-option" "^7.22.15"
|
||||
browserslist "^4.21.9"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.16.10":
|
||||
version "7.16.10"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz"
|
||||
@@ -413,6 +458,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.16.7"
|
||||
|
||||
"@babel/helper-module-imports@^7.22.15":
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
|
||||
integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.15"
|
||||
|
||||
"@babel/helper-module-transforms@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz"
|
||||
@@ -427,6 +479,17 @@
|
||||
"@babel/traverse" "^7.16.7"
|
||||
"@babel/types" "^7.16.7"
|
||||
|
||||
"@babel/helper-module-transforms@^7.23.0":
|
||||
version "7.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e"
|
||||
integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==
|
||||
dependencies:
|
||||
"@babel/helper-environment-visitor" "^7.22.20"
|
||||
"@babel/helper-module-imports" "^7.22.15"
|
||||
"@babel/helper-simple-access" "^7.22.5"
|
||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz"
|
||||
@@ -439,6 +502,11 @@
|
||||
resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz"
|
||||
integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
|
||||
|
||||
"@babel/helper-plugin-utils@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
|
||||
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
|
||||
|
||||
"@babel/helper-remap-async-to-generator@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz"
|
||||
@@ -466,6 +534,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.16.7"
|
||||
|
||||
"@babel/helper-simple-access@^7.22.5":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
|
||||
integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
|
||||
version "7.16.0"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz"
|
||||
@@ -507,6 +582,11 @@
|
||||
resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz"
|
||||
integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
|
||||
|
||||
"@babel/helper-validator-option@^7.22.15":
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040"
|
||||
integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==
|
||||
|
||||
"@babel/helper-wrap-function@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz"
|
||||
@@ -526,6 +606,15 @@
|
||||
"@babel/traverse" "^7.16.7"
|
||||
"@babel/types" "^7.16.7"
|
||||
|
||||
"@babel/helpers@^7.23.2":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767"
|
||||
integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==
|
||||
dependencies:
|
||||
"@babel/template" "^7.22.15"
|
||||
"@babel/traverse" "^7.23.2"
|
||||
"@babel/types" "^7.23.0"
|
||||
|
||||
"@babel/highlight@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz"
|
||||
@@ -544,12 +633,12 @@
|
||||
chalk "^2.4.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7":
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz"
|
||||
integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==
|
||||
|
||||
"@babel/parser@^7.16.12":
|
||||
"@babel/parser@^7.16.10", "@babel/parser@^7.16.12":
|
||||
version "7.16.12"
|
||||
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz"
|
||||
integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==
|
||||
@@ -795,13 +884,20 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.8.0"
|
||||
|
||||
"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.16.7", "@babel/plugin-syntax-jsx@^7.7.2":
|
||||
"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz"
|
||||
integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
|
||||
"@babel/plugin-syntax-jsx@^7.7.2":
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918"
|
||||
integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.22.5"
|
||||
|
||||
"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz"
|
||||
@@ -1437,7 +1533,39 @@
|
||||
"@babel/parser" "^7.22.15"
|
||||
"@babel/types" "^7.22.15"
|
||||
|
||||
"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.7.2":
|
||||
"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz"
|
||||
integrity sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.16.7"
|
||||
"@babel/generator" "^7.16.8"
|
||||
"@babel/helper-environment-visitor" "^7.16.7"
|
||||
"@babel/helper-function-name" "^7.16.7"
|
||||
"@babel/helper-hoist-variables" "^7.16.7"
|
||||
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||
"@babel/parser" "^7.16.8"
|
||||
"@babel/types" "^7.16.8"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/traverse@^7.16.10", "@babel/traverse@^7.7.2":
|
||||
version "7.16.10"
|
||||
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz"
|
||||
integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.16.7"
|
||||
"@babel/generator" "^7.16.8"
|
||||
"@babel/helper-environment-visitor" "^7.16.7"
|
||||
"@babel/helper-function-name" "^7.16.7"
|
||||
"@babel/helper-hoist-variables" "^7.16.7"
|
||||
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||
"@babel/parser" "^7.16.10"
|
||||
"@babel/types" "^7.16.8"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/traverse@^7.23.2":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
|
||||
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
|
||||
@@ -1754,6 +1882,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.1.0.tgz#e14896f1c57af2495e341dc4c7bf04125c8aeafd"
|
||||
integrity sha512-38DT60rumHfBYynif3lmtxMqMqmsOQIxQgEuPZxCk2yUYN0eqWpTACgxi0VpidvsJB8CRxCpvP7B3anK85FjtQ==
|
||||
|
||||
"@faker-js/faker@^8.2.0":
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.2.0.tgz#d4656d2cb485fe6ec4e7b340da9f16fac2c36c4a"
|
||||
integrity sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==
|
||||
|
||||
"@formatjs/ecma402-abstract@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.1.tgz"
|
||||
@@ -1976,7 +2109,7 @@
|
||||
|
||||
"@jest/console@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
|
||||
integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -2022,7 +2155,7 @@
|
||||
|
||||
"@jest/core@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
|
||||
integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
|
||||
dependencies:
|
||||
"@jest/console" "^29.7.0"
|
||||
@@ -2066,7 +2199,7 @@
|
||||
|
||||
"@jest/environment@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
|
||||
integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
|
||||
dependencies:
|
||||
"@jest/fake-timers" "^29.7.0"
|
||||
@@ -2076,14 +2209,14 @@
|
||||
|
||||
"@jest/expect-utils@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
|
||||
integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
|
||||
dependencies:
|
||||
jest-get-type "^29.6.3"
|
||||
|
||||
"@jest/expect@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
|
||||
integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
|
||||
dependencies:
|
||||
expect "^29.7.0"
|
||||
@@ -2103,7 +2236,7 @@
|
||||
|
||||
"@jest/fake-timers@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
|
||||
integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -2124,7 +2257,7 @@
|
||||
|
||||
"@jest/globals@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
|
||||
integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
|
||||
dependencies:
|
||||
"@jest/environment" "^29.7.0"
|
||||
@@ -2165,7 +2298,7 @@
|
||||
|
||||
"@jest/reporters@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
|
||||
integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
|
||||
dependencies:
|
||||
"@bcoe/v8-coverage" "^0.2.3"
|
||||
@@ -2195,7 +2328,7 @@
|
||||
|
||||
"@jest/schemas@^29.6.3":
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
|
||||
integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
|
||||
dependencies:
|
||||
"@sinclair/typebox" "^0.27.8"
|
||||
@@ -2211,7 +2344,7 @@
|
||||
|
||||
"@jest/source-map@^29.6.3":
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
|
||||
integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.18"
|
||||
@@ -2230,7 +2363,7 @@
|
||||
|
||||
"@jest/test-result@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
|
||||
integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
|
||||
dependencies:
|
||||
"@jest/console" "^29.7.0"
|
||||
@@ -2250,7 +2383,7 @@
|
||||
|
||||
"@jest/test-sequencer@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
|
||||
integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
|
||||
dependencies:
|
||||
"@jest/test-result" "^29.7.0"
|
||||
@@ -2281,7 +2414,7 @@
|
||||
|
||||
"@jest/transform@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
|
||||
integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
|
||||
dependencies:
|
||||
"@babel/core" "^7.11.6"
|
||||
@@ -2324,7 +2457,7 @@
|
||||
|
||||
"@jest/types@^29.6.3":
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
|
||||
integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
|
||||
dependencies:
|
||||
"@jest/schemas" "^29.6.3"
|
||||
@@ -2375,7 +2508,15 @@
|
||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9":
|
||||
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18":
|
||||
version "0.3.20"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f"
|
||||
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.19"
|
||||
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz"
|
||||
integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
|
||||
@@ -3768,7 +3909,7 @@
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
@@ -3785,14 +3926,14 @@
|
||||
|
||||
"@sinonjs/commons@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72"
|
||||
integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==
|
||||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@sinonjs/fake-timers@^10.0.2":
|
||||
version "10.3.0"
|
||||
resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
|
||||
integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^3.0.0"
|
||||
@@ -4123,9 +4264,9 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cookiejar@*":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
|
||||
integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.3.tgz#c54976fb8f3a32ea8da844f59f0374dd39656e13"
|
||||
integrity sha512-LZ8SD3LpNmLMDLkG2oCBjZg+ETnx6XdCjydUE0HwojDmnDfDUnhMKKbtth1TZh+hzcqb03azrYWoXLS8sMXdqg==
|
||||
|
||||
"@types/cors@^2.8.12":
|
||||
version "2.8.12"
|
||||
@@ -4254,13 +4395,20 @@
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3":
|
||||
"@types/graceful-fs@^4.1.2":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz"
|
||||
integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/graceful-fs@^4.1.3":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.8.tgz#417e461e4dc79d957dc3107f45fe4973b09c2915"
|
||||
integrity sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
|
||||
@@ -4334,9 +4482,9 @@
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/jest@^29.5.5":
|
||||
version "29.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a"
|
||||
integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==
|
||||
version "29.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.6.tgz#f4cf7ef1b5b0bfc1aa744e41b24d9cc52533130b"
|
||||
integrity sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==
|
||||
dependencies:
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
@@ -4678,17 +4826,17 @@
|
||||
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
|
||||
|
||||
"@types/superagent@*":
|
||||
version "4.1.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.19.tgz#33f4fa460fb9e79e5e47a96731725141c667acd0"
|
||||
integrity sha512-McM1mlc7PBZpCaw0fw/36uFqo0YeA6m8JqoyE4OfqXsZCIg0hPP2xdE6FM7r6fdprDZHlJwDpydUj1R++93hCA==
|
||||
version "4.1.20"
|
||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.20.tgz#9248f55ac588794568f02fe9cac6d6ff2650b660"
|
||||
integrity sha512-GfpwJgYSr3yO+nArFkmyqv3i0vZavyEG5xPd/o95RwpKYpsOKJYI5XLdxLpdRbZI3YiGKKdIOFIf/jlP7A0Jxg==
|
||||
dependencies:
|
||||
"@types/cookiejar" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/supertest@^2.0.14":
|
||||
version "2.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.14.tgz#e8fb6f6feed58a0dd5c2036227865dfa6ff7411d"
|
||||
integrity sha512-Q900DeeHNFF3ZYYepf/EyJfZDA2JrnWLaSQ0YNV7+2GTo8IlJzauEnDGhya+hauncpBYTYGpVHwGdssJeAQ7eA==
|
||||
version "2.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.15.tgz#3d032865048c84c6a3bbbf1f949145b917d2ff65"
|
||||
integrity sha512-jUCZZ/TMcpGzoSaed9Gjr8HCf3HehExdibyw3OHHEL1als1KmyzcOZZH4MjbObI8TkWsEr7bc7gsW0WTDni+qQ==
|
||||
dependencies:
|
||||
"@types/superagent" "*"
|
||||
|
||||
@@ -4778,9 +4926,9 @@
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yargs@^17.0.8":
|
||||
version "17.0.26"
|
||||
resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz"
|
||||
integrity sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==
|
||||
version "17.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.29.tgz#06aabc72497b798c643c812a8b561537fea760cf"
|
||||
integrity sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
@@ -5941,7 +6089,7 @@ babel-jest@^27.4.2, babel-jest@^27.4.6:
|
||||
|
||||
babel-jest@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
|
||||
integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
|
||||
dependencies:
|
||||
"@jest/transform" "^29.7.0"
|
||||
@@ -5992,7 +6140,7 @@ babel-plugin-jest-hoist@^27.4.0:
|
||||
|
||||
babel-plugin-jest-hoist@^29.6.3:
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
|
||||
integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
|
||||
dependencies:
|
||||
"@babel/template" "^7.3.3"
|
||||
@@ -6080,7 +6228,7 @@ babel-preset-jest@^27.4.0:
|
||||
|
||||
babel-preset-jest@^29.6.3:
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
|
||||
integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
|
||||
dependencies:
|
||||
babel-plugin-jest-hoist "^29.6.3"
|
||||
@@ -6314,6 +6462,16 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
browserslist@^4.21.9:
|
||||
version "4.22.1"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619"
|
||||
integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001541"
|
||||
electron-to-chromium "^1.4.535"
|
||||
node-releases "^2.0.13"
|
||||
update-browserslist-db "^1.0.13"
|
||||
|
||||
bs-logger@0.x:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
|
||||
@@ -6543,6 +6701,11 @@ caniuse-lite@^1.0.30001299:
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz"
|
||||
integrity sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==
|
||||
|
||||
caniuse-lite@^1.0.30001541:
|
||||
version "1.0.30001554"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz#ba80d88dff9acbc0cd4b7535fc30e0191c5e2e2a"
|
||||
integrity sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==
|
||||
|
||||
cardinal@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz"
|
||||
@@ -7211,7 +7374,7 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0,
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
@@ -7307,7 +7470,7 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1:
|
||||
|
||||
create-jest@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
|
||||
integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -7363,9 +7526,9 @@ crypt@0.0.2:
|
||||
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||
|
||||
crypto-js@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz"
|
||||
integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
|
||||
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
|
||||
|
||||
crypto-random-string@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -7718,7 +7881,7 @@ dedent@^0.7.0:
|
||||
|
||||
dedent@^1.0.0:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
|
||||
integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
|
||||
|
||||
deep-equal@^1.0.1:
|
||||
@@ -7918,7 +8081,7 @@ diff-sequences@^27.4.0:
|
||||
|
||||
diff-sequences@^29.6.3:
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
|
||||
integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
|
||||
|
||||
diff@^4.0.1:
|
||||
@@ -8186,9 +8349,14 @@ electron-to-chromium@^1.4.17:
|
||||
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz"
|
||||
integrity sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA==
|
||||
|
||||
electron-to-chromium@^1.4.535:
|
||||
version "1.4.567"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz#c92e8fbc2bd15df3068d92571733a218a5413add"
|
||||
integrity sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w==
|
||||
|
||||
emittery@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
|
||||
integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
|
||||
|
||||
emittery@^0.8.1:
|
||||
@@ -9033,7 +9201,7 @@ expect@^27.4.6:
|
||||
|
||||
expect@^29.0.0, expect@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
|
||||
integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
|
||||
dependencies:
|
||||
"@jest/expect-utils" "^29.7.0"
|
||||
@@ -11001,9 +11169,9 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0:
|
||||
semver "^6.3.0"
|
||||
|
||||
istanbul-lib-instrument@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz"
|
||||
integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf"
|
||||
integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.12.3"
|
||||
"@babel/parser" "^7.14.7"
|
||||
@@ -11058,7 +11226,7 @@ jest-changed-files@^27.4.2:
|
||||
|
||||
jest-changed-files@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
|
||||
integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
|
||||
dependencies:
|
||||
execa "^5.0.0"
|
||||
@@ -11092,7 +11260,7 @@ jest-circus@^27.4.6:
|
||||
|
||||
jest-circus@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
|
||||
integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
|
||||
dependencies:
|
||||
"@jest/environment" "^29.7.0"
|
||||
@@ -11136,7 +11304,7 @@ jest-cli@^27.4.7:
|
||||
|
||||
jest-cli@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
|
||||
integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
|
||||
dependencies:
|
||||
"@jest/core" "^29.7.0"
|
||||
@@ -11181,7 +11349,7 @@ jest-config@^27.4.7:
|
||||
|
||||
jest-config@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
|
||||
integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
|
||||
dependencies:
|
||||
"@babel/core" "^7.11.6"
|
||||
@@ -11229,7 +11397,7 @@ jest-diff@^27.0.0, jest-diff@^27.4.6:
|
||||
|
||||
jest-diff@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
|
||||
integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
@@ -11246,7 +11414,7 @@ jest-docblock@^27.4.0:
|
||||
|
||||
jest-docblock@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
|
||||
integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
|
||||
dependencies:
|
||||
detect-newline "^3.0.0"
|
||||
@@ -11264,7 +11432,7 @@ jest-each@^27.4.6:
|
||||
|
||||
jest-each@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
|
||||
integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -11300,7 +11468,7 @@ jest-environment-node@^27.4.6:
|
||||
|
||||
jest-environment-node@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
|
||||
integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
|
||||
dependencies:
|
||||
"@jest/environment" "^29.7.0"
|
||||
@@ -11322,7 +11490,7 @@ jest-get-type@^27.4.0:
|
||||
|
||||
jest-get-type@^29.6.3:
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
|
||||
integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
|
||||
|
||||
jest-haste-map@^27.4.6:
|
||||
@@ -11347,7 +11515,7 @@ jest-haste-map@^27.4.6:
|
||||
|
||||
jest-haste-map@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
|
||||
integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -11397,7 +11565,7 @@ jest-leak-detector@^27.4.6:
|
||||
|
||||
jest-leak-detector@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
|
||||
integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
|
||||
dependencies:
|
||||
jest-get-type "^29.6.3"
|
||||
@@ -11415,7 +11583,7 @@ jest-matcher-utils@^27.4.6:
|
||||
|
||||
jest-matcher-utils@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
|
||||
integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
@@ -11440,7 +11608,7 @@ jest-message-util@^27.4.6:
|
||||
|
||||
jest-message-util@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
|
||||
integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.12.13"
|
||||
@@ -11463,7 +11631,7 @@ jest-mock@^27.4.6:
|
||||
|
||||
jest-mock@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
|
||||
integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -11482,7 +11650,7 @@ jest-regex-util@^27.0.0, jest-regex-util@^27.4.0:
|
||||
|
||||
jest-regex-util@^29.6.3:
|
||||
version "29.6.3"
|
||||
resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
|
||||
integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
|
||||
|
||||
jest-resolve-dependencies@^27.4.6:
|
||||
@@ -11496,7 +11664,7 @@ jest-resolve-dependencies@^27.4.6:
|
||||
|
||||
jest-resolve-dependencies@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
|
||||
integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
|
||||
dependencies:
|
||||
jest-regex-util "^29.6.3"
|
||||
@@ -11520,7 +11688,7 @@ jest-resolve@^27.4.2, jest-resolve@^27.4.6:
|
||||
|
||||
jest-resolve@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
|
||||
integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
@@ -11563,7 +11731,7 @@ jest-runner@^27.4.6:
|
||||
|
||||
jest-runner@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
|
||||
integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
|
||||
dependencies:
|
||||
"@jest/console" "^29.7.0"
|
||||
@@ -11618,7 +11786,7 @@ jest-runtime@^27.4.6:
|
||||
|
||||
jest-runtime@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
|
||||
integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
|
||||
dependencies:
|
||||
"@jest/environment" "^29.7.0"
|
||||
@@ -11682,7 +11850,7 @@ jest-snapshot@^27.4.6:
|
||||
|
||||
jest-snapshot@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
|
||||
integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
|
||||
dependencies:
|
||||
"@babel/core" "^7.11.6"
|
||||
@@ -11720,7 +11888,7 @@ jest-util@^27.4.2:
|
||||
|
||||
jest-util@^29.0.0, jest-util@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
|
||||
integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -11744,7 +11912,7 @@ jest-validate@^27.4.6:
|
||||
|
||||
jest-validate@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
|
||||
integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
@@ -11782,7 +11950,7 @@ jest-watcher@^27.0.0, jest-watcher@^27.4.6:
|
||||
|
||||
jest-watcher@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
|
||||
integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
|
||||
dependencies:
|
||||
"@jest/test-result" "^29.7.0"
|
||||
@@ -11814,7 +11982,7 @@ jest-worker@^27.0.2, jest-worker@^27.3.1, jest-worker@^27.4.1, jest-worker@^27.4
|
||||
|
||||
jest-worker@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
|
||||
integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
@@ -11833,7 +12001,7 @@ jest@^27.4.3:
|
||||
|
||||
jest@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
|
||||
integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
|
||||
dependencies:
|
||||
"@jest/core" "^29.7.0"
|
||||
@@ -12474,6 +12642,13 @@ lowercase-keys@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
|
||||
@@ -13296,6 +13471,11 @@ node-releases@^2.0.1:
|
||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz"
|
||||
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
|
||||
|
||||
node-releases@^2.0.13:
|
||||
version "2.0.13"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
|
||||
integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
|
||||
|
||||
nodemailer@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.0.tgz"
|
||||
@@ -15000,7 +15180,7 @@ pretty-format@^27.0.0, pretty-format@^27.4.6:
|
||||
|
||||
pretty-format@^29.0.0, pretty-format@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
|
||||
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
|
||||
dependencies:
|
||||
"@jest/schemas" "^29.6.3"
|
||||
@@ -15146,7 +15326,7 @@ pupa@^2.1.1:
|
||||
|
||||
pure-rand@^6.0.0:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz"
|
||||
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7"
|
||||
integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==
|
||||
|
||||
q@^1.1.2, q@^1.5.1:
|
||||
@@ -15931,7 +16111,7 @@ resolve.exports@^1.1.0:
|
||||
|
||||
resolve.exports@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz"
|
||||
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
|
||||
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
|
||||
|
||||
resolve@^1.0.0, resolve@^1.10.1, resolve@^1.21.0:
|
||||
@@ -16211,7 +16391,7 @@ semver@7.0.0:
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
|
||||
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
@@ -16581,7 +16761,7 @@ source-map-resolve@^0.6.0:
|
||||
|
||||
source-map-support@0.5.13:
|
||||
version "0.5.13"
|
||||
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
|
||||
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
@@ -17832,6 +18012,14 @@ upath@^2.0.1:
|
||||
resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz"
|
||||
integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
|
||||
|
||||
update-browserslist-db@^1.0.13:
|
||||
version "1.0.13"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
|
||||
integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
|
||||
dependencies:
|
||||
escalade "^3.1.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
update-notifier@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz"
|
||||
@@ -17941,13 +18129,13 @@ v8-to-istanbul@^8.1.0:
|
||||
source-map "^0.7.3"
|
||||
|
||||
v8-to-istanbul@^9.0.1:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz"
|
||||
integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==
|
||||
version "9.1.3"
|
||||
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz#ea456604101cd18005ac2cae3cdd1aa058a6306b"
|
||||
integrity sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.12"
|
||||
"@types/istanbul-lib-coverage" "^2.0.1"
|
||||
convert-source-map "^1.6.0"
|
||||
convert-source-map "^2.0.0"
|
||||
|
||||
validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4:
|
||||
version "3.0.4"
|
||||
@@ -18609,7 +18797,7 @@ write-file-atomic@^4.0.0:
|
||||
|
||||
write-file-atomic@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz"
|
||||
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
|
||||
integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
|
||||
dependencies:
|
||||
imurmurhash "^0.1.4"
|
||||
@@ -18754,7 +18942,7 @@ y18n@^5.0.5:
|
||||
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yallist@^3.0.0, yallist@^3.1.1:
|
||||
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
@@ -18781,7 +18969,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3:
|
||||
|
||||
yargs-parser@^21.0.1, yargs-parser@^21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@^16.1.0, yargs@^16.2.0:
|
||||
@@ -18799,7 +18987,7 @@ yargs@^16.1.0, yargs@^16.2.0:
|
||||
|
||||
yargs@^17.3.1:
|
||||
version "17.7.2"
|
||||
resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
||||
dependencies:
|
||||
cliui "^8.0.1"
|
||||
|
Reference in New Issue
Block a user