Compare commits
1 Commits
AUT-1252
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d01477306d |
1
.github/workflows/playwright.yml
vendored
1
.github/workflows/playwright.yml
vendored
@@ -23,7 +23,6 @@ env:
|
|||||||
REDIS_HOST: localhost
|
REDIS_HOST: localhost
|
||||||
APP_ENV: production
|
APP_ENV: production
|
||||||
LICENSE_KEY: dummy_license_key
|
LICENSE_KEY: dummy_license_key
|
||||||
BACKEND_APP_URL: http://localhost:3000
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
|
@@ -6,7 +6,7 @@ export default async (request, response) => {
|
|||||||
const user = await User.query().insertAndFetch(await userParams(request));
|
const user = await User.query().insertAndFetch(await userParams(request));
|
||||||
await user.sendInvitationEmail();
|
await user.sendInvitationEmail();
|
||||||
|
|
||||||
renderObject(response, user, { status: 201, serializer: 'AdminUser' });
|
renderObject(response, user, { status: 201 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const userParams = async (request) => {
|
const userParams = async (request) => {
|
||||||
|
@@ -6,7 +6,7 @@ import User from '../../../../../models/user.js';
|
|||||||
import Role from '../../../../../models/role.js';
|
import Role from '../../../../../models/role.js';
|
||||||
import { createUser } from '../../../../../../test/factories/user.js';
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
import { createRole } from '../../../../../../test/factories/role.js';
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
import createUserMock from '../../../../../../test/mocks/rest/api/v1/admin/users/create-user.js';
|
import createUserMock from '../../../../../../test/mocks/rest/api/v1/users/create-user.js';
|
||||||
|
|
||||||
describe('POST /api/v1/admin/users', () => {
|
describe('POST /api/v1/admin/users', () => {
|
||||||
let currentUser, adminRole, token;
|
let currentUser, adminRole, token;
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
import { renderObject } from '../../../../helpers/renderer.js';
|
|
||||||
|
|
||||||
export default async (request, response) => {
|
|
||||||
let step = await request.currentUser.authorizedSteps
|
|
||||||
.findById(request.params.stepId)
|
|
||||||
.throwIfNotFound();
|
|
||||||
|
|
||||||
step = await step.updateFor(request.currentUser, stepParams(request));
|
|
||||||
|
|
||||||
renderObject(response, step);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stepParams = (request) => {
|
|
||||||
const { connectionId, appKey, key, parameters } = request.body;
|
|
||||||
|
|
||||||
return {
|
|
||||||
connectionId,
|
|
||||||
appKey,
|
|
||||||
key,
|
|
||||||
parameters,
|
|
||||||
};
|
|
||||||
};
|
|
@@ -1,211 +0,0 @@
|
|||||||
import { describe, it, beforeEach, expect } from 'vitest';
|
|
||||||
import request from 'supertest';
|
|
||||||
import Crypto from 'crypto';
|
|
||||||
import app from '../../../../app.js';
|
|
||||||
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
|
||||||
import { createUser } from '../../../../../test/factories/user.js';
|
|
||||||
import { createConnection } from '../../../../../test/factories/connection.js';
|
|
||||||
import { createFlow } from '../../../../../test/factories/flow.js';
|
|
||||||
import { createStep } from '../../../../../test/factories/step.js';
|
|
||||||
import { createPermission } from '../../../../../test/factories/permission.js';
|
|
||||||
import updateStepMock from '../../../../../test/mocks/rest/api/v1/steps/update-step.js';
|
|
||||||
|
|
||||||
describe('PATCH /api/v1/steps/:stepId', () => {
|
|
||||||
let currentUser, token;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
currentUser = await createUser();
|
|
||||||
|
|
||||||
token = await createAuthTokenByUserId(currentUser.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update the step of the current user', async () => {
|
|
||||||
const currentUserFlow = await createFlow({ userId: currentUser.id });
|
|
||||||
const currentUserConnection = await createConnection({
|
|
||||||
key: 'deepl',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createStep({
|
|
||||||
flowId: currentUserFlow.id,
|
|
||||||
connectionId: currentUserConnection.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const actionStep = await createStep({
|
|
||||||
flowId: currentUserFlow.id,
|
|
||||||
connectionId: currentUserConnection.id,
|
|
||||||
appKey: 'deepl',
|
|
||||||
key: 'translateText',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.patch(`/api/v1/steps/${actionStep.id}`)
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({
|
|
||||||
parameters: {
|
|
||||||
text: 'Hello world!',
|
|
||||||
targetLanguage: 'de',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const refetchedStep = await actionStep.$query();
|
|
||||||
|
|
||||||
const expectedResponse = updateStepMock(refetchedStep);
|
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedResponse);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update the step of the another user', async () => {
|
|
||||||
const anotherUser = await createUser();
|
|
||||||
const anotherUserFlow = await createFlow({ userId: anotherUser.id });
|
|
||||||
const anotherUserConnection = await createConnection({
|
|
||||||
key: 'deepl',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createStep({
|
|
||||||
flowId: anotherUserFlow.id,
|
|
||||||
connectionId: anotherUserConnection.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const actionStep = await createStep({
|
|
||||||
flowId: anotherUserFlow.id,
|
|
||||||
connectionId: anotherUserConnection.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.patch(`/api/v1/steps/${actionStep.id}`)
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({
|
|
||||||
parameters: {
|
|
||||||
text: 'Hello world!',
|
|
||||||
targetLanguage: 'de',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const refetchedStep = await actionStep.$query();
|
|
||||||
|
|
||||||
const expectedResponse = updateStepMock(refetchedStep);
|
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedResponse);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return not found response for inaccessible connection', async () => {
|
|
||||||
const currentUserFlow = await createFlow({ userId: currentUser.id });
|
|
||||||
|
|
||||||
const anotherUser = await createUser();
|
|
||||||
const anotherUserConnection = await createConnection({
|
|
||||||
key: 'deepl',
|
|
||||||
userId: anotherUser.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createStep({
|
|
||||||
flowId: currentUserFlow.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const actionStep = await createStep({
|
|
||||||
flowId: currentUserFlow.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Connection',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch(`/api/v1/steps/${actionStep.id}`)
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({
|
|
||||||
connectionId: anotherUserConnection.id,
|
|
||||||
})
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return not found response for not existing step UUID', async () => {
|
|
||||||
await createPermission({
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const notExistingStepUUID = Crypto.randomUUID();
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch(`/api/v1/steps/${notExistingStepUUID}`)
|
|
||||||
.set('Authorization', token)
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return bad request response for invalid step UUID', async () => {
|
|
||||||
await createPermission({
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Flow',
|
|
||||||
roleId: currentUser.roleId,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch('/api/v1/steps/invalidStepUUID')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.expect(400);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,19 +1,36 @@
|
|||||||
|
import updateStep from './mutations/update-step.js';
|
||||||
|
|
||||||
// Converted mutations
|
// Converted mutations
|
||||||
import executeFlow from './mutations/execute-flow.js';
|
import executeFlow from './mutations/execute-flow.js';
|
||||||
|
import updateUser from './mutations/update-user.ee.js';
|
||||||
|
import deleteStep from './mutations/delete-step.js';
|
||||||
import verifyConnection from './mutations/verify-connection.js';
|
import verifyConnection from './mutations/verify-connection.js';
|
||||||
|
import createFlow from './mutations/create-flow.js';
|
||||||
|
import deleteCurrentUser from './mutations/delete-current-user.ee.js';
|
||||||
import updateCurrentUser from './mutations/update-current-user.js';
|
import updateCurrentUser from './mutations/update-current-user.js';
|
||||||
import generateAuthUrl from './mutations/generate-auth-url.js';
|
import generateAuthUrl from './mutations/generate-auth-url.js';
|
||||||
import createConnection from './mutations/create-connection.js';
|
import createConnection from './mutations/create-connection.js';
|
||||||
|
import deleteFlow from './mutations/delete-flow.js';
|
||||||
import resetConnection from './mutations/reset-connection.js';
|
import resetConnection from './mutations/reset-connection.js';
|
||||||
import updateConnection from './mutations/update-connection.js';
|
import updateConnection from './mutations/update-connection.js';
|
||||||
|
import createUser from './mutations/create-user.ee.js';
|
||||||
|
import updateFlowStatus from './mutations/update-flow-status.js';
|
||||||
|
|
||||||
const mutationResolvers = {
|
const mutationResolvers = {
|
||||||
createConnection,
|
createConnection,
|
||||||
|
createFlow,
|
||||||
|
createUser,
|
||||||
|
deleteCurrentUser,
|
||||||
|
deleteFlow,
|
||||||
|
deleteStep,
|
||||||
executeFlow,
|
executeFlow,
|
||||||
generateAuthUrl,
|
generateAuthUrl,
|
||||||
resetConnection,
|
resetConnection,
|
||||||
updateConnection,
|
updateConnection,
|
||||||
updateCurrentUser,
|
updateCurrentUser,
|
||||||
|
updateFlowStatus,
|
||||||
|
updateStep,
|
||||||
|
updateUser,
|
||||||
verifyConnection,
|
verifyConnection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
45
packages/backend/src/graphql/mutations/create-flow.js
Normal file
45
packages/backend/src/graphql/mutations/create-flow.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import App from '../../models/app.js';
|
||||||
|
import Step from '../../models/step.js';
|
||||||
|
|
||||||
|
const createFlow = async (_parent, params, context) => {
|
||||||
|
context.currentUser.can('create', 'Flow');
|
||||||
|
|
||||||
|
const connectionId = params?.input?.connectionId;
|
||||||
|
const appKey = params?.input?.triggerAppKey;
|
||||||
|
|
||||||
|
if (appKey) {
|
||||||
|
await App.findOneByKey(appKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const flow = await context.currentUser.$relatedQuery('flows').insert({
|
||||||
|
name: 'Name your flow',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (connectionId) {
|
||||||
|
const hasConnection = await context.currentUser
|
||||||
|
.$relatedQuery('connections')
|
||||||
|
.findById(connectionId);
|
||||||
|
|
||||||
|
if (!hasConnection) {
|
||||||
|
throw new Error('The connection does not exist!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Step.query().insert({
|
||||||
|
flowId: flow.id,
|
||||||
|
type: 'trigger',
|
||||||
|
position: 1,
|
||||||
|
appKey,
|
||||||
|
connectionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await Step.query().insert({
|
||||||
|
flowId: flow.id,
|
||||||
|
type: 'action',
|
||||||
|
position: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
return flow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createFlow;
|
66
packages/backend/src/graphql/mutations/create-user.ee.js
Normal file
66
packages/backend/src/graphql/mutations/create-user.ee.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import appConfig from '../../config/app.js';
|
||||||
|
import User from '../../models/user.js';
|
||||||
|
import Role from '../../models/role.js';
|
||||||
|
import emailQueue from '../../queues/email.js';
|
||||||
|
import {
|
||||||
|
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||||
|
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
|
} from '../../helpers/remove-job-configuration.js';
|
||||||
|
|
||||||
|
const createUser = async (_parent, params, context) => {
|
||||||
|
context.currentUser.can('create', 'User');
|
||||||
|
|
||||||
|
const { fullName, email } = params.input;
|
||||||
|
|
||||||
|
const existingUser = await User.query().findOne({
|
||||||
|
email: email.toLowerCase(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new Error('User already exists!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userPayload = {
|
||||||
|
fullName,
|
||||||
|
email,
|
||||||
|
status: 'invited',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.currentUser.can('update', 'Role');
|
||||||
|
|
||||||
|
userPayload.roleId = params.input.role.id;
|
||||||
|
} catch {
|
||||||
|
// void
|
||||||
|
const role = await Role.findAdmin();
|
||||||
|
userPayload.roleId = role.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.query().insert(userPayload);
|
||||||
|
|
||||||
|
await user.generateInvitationToken();
|
||||||
|
|
||||||
|
const jobName = `Invitation Email - ${user.id}`;
|
||||||
|
const acceptInvitationUrl = `${appConfig.webAppUrl}/accept-invitation?token=${user.invitationToken}`;
|
||||||
|
|
||||||
|
const jobPayload = {
|
||||||
|
email: user.email,
|
||||||
|
subject: 'You are invited!',
|
||||||
|
template: 'invitation-instructions',
|
||||||
|
params: {
|
||||||
|
fullName: user.fullName,
|
||||||
|
acceptInvitationUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const jobOptions = {
|
||||||
|
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
|
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||||
|
};
|
||||||
|
|
||||||
|
await emailQueue.add(jobName, jobPayload, jobOptions);
|
||||||
|
|
||||||
|
return { user, acceptInvitationUrl };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createUser;
|
@@ -0,0 +1,58 @@
|
|||||||
|
import { Duration } from 'luxon';
|
||||||
|
import deleteUserQueue from '../../queues/delete-user.ee.js';
|
||||||
|
import flowQueue from '../../queues/flow.js';
|
||||||
|
import Flow from '../../models/flow.js';
|
||||||
|
import ExecutionStep from '../../models/execution-step.js';
|
||||||
|
import appConfig from '../../config/app.js';
|
||||||
|
|
||||||
|
const deleteCurrentUser = async (_parent, params, context) => {
|
||||||
|
const id = context.currentUser.id;
|
||||||
|
|
||||||
|
const flows = await context.currentUser.$relatedQuery('flows').where({
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const repeatableJobs = await flowQueue.getRepeatableJobs();
|
||||||
|
|
||||||
|
for (const flow of flows) {
|
||||||
|
const job = repeatableJobs.find((job) => job.id === flow.id);
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
await flowQueue.removeRepeatableByKey(job.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionIds = (
|
||||||
|
await context.currentUser
|
||||||
|
.$relatedQuery('executions')
|
||||||
|
.select('executions.id')
|
||||||
|
).map((execution) => execution.id);
|
||||||
|
const flowIds = flows.map((flow) => flow.id);
|
||||||
|
|
||||||
|
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
||||||
|
await context.currentUser.$relatedQuery('executions').delete();
|
||||||
|
await context.currentUser.$relatedQuery('steps').delete();
|
||||||
|
await Flow.query().whereIn('id', flowIds).delete();
|
||||||
|
await context.currentUser.$relatedQuery('connections').delete();
|
||||||
|
await context.currentUser.$relatedQuery('identities').delete();
|
||||||
|
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
await context.currentUser.$relatedQuery('subscriptions').delete();
|
||||||
|
await context.currentUser.$relatedQuery('usageData').delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.currentUser.$query().delete();
|
||||||
|
|
||||||
|
const jobName = `Delete user - ${id}`;
|
||||||
|
const jobPayload = { id };
|
||||||
|
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
||||||
|
const jobOptions = {
|
||||||
|
delay: millisecondsFor30Days,
|
||||||
|
};
|
||||||
|
|
||||||
|
await deleteUserQueue.add(jobName, jobPayload, jobOptions);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteCurrentUser;
|
53
packages/backend/src/graphql/mutations/delete-flow.js
Normal file
53
packages/backend/src/graphql/mutations/delete-flow.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import Flow from '../../models/flow.js';
|
||||||
|
import ExecutionStep from '../../models/execution-step.js';
|
||||||
|
import globalVariable from '../../helpers/global-variable.js';
|
||||||
|
import logger from '../../helpers/logger.js';
|
||||||
|
|
||||||
|
const deleteFlow = async (_parent, params, context) => {
|
||||||
|
const conditions = context.currentUser.can('delete', 'Flow');
|
||||||
|
const isCreator = conditions.isCreator;
|
||||||
|
const allFlows = Flow.query();
|
||||||
|
const userFlows = context.currentUser.$relatedQuery('flows');
|
||||||
|
const baseQuery = isCreator ? userFlows : allFlows;
|
||||||
|
|
||||||
|
const flow = await baseQuery
|
||||||
|
.findOne({
|
||||||
|
id: params.input.id,
|
||||||
|
})
|
||||||
|
.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.id);
|
||||||
|
|
||||||
|
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
||||||
|
|
||||||
|
await flow.$relatedQuery('executions').delete();
|
||||||
|
await flow.$relatedQuery('steps').delete();
|
||||||
|
await flow.$query().delete();
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteFlow;
|
40
packages/backend/src/graphql/mutations/delete-step.js
Normal file
40
packages/backend/src/graphql/mutations/delete-step.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import Step from '../../models/step.js';
|
||||||
|
|
||||||
|
const deleteStep = async (_parent, params, context) => {
|
||||||
|
const conditions = context.currentUser.can('update', 'Flow');
|
||||||
|
const isCreator = conditions.isCreator;
|
||||||
|
const allSteps = Step.query();
|
||||||
|
const userSteps = context.currentUser.$relatedQuery('steps');
|
||||||
|
const baseQuery = isCreator ? userSteps : allSteps;
|
||||||
|
|
||||||
|
const step = await baseQuery
|
||||||
|
.withGraphFetched('flow')
|
||||||
|
.findOne({
|
||||||
|
'steps.id': params.input.id,
|
||||||
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
await step.$relatedQuery('executionSteps').delete();
|
||||||
|
await step.$query().delete();
|
||||||
|
|
||||||
|
const nextSteps = await step.flow
|
||||||
|
.$relatedQuery('steps')
|
||||||
|
.where('position', '>', step.position);
|
||||||
|
|
||||||
|
const nextStepQueries = nextSteps.map(async (nextStep) => {
|
||||||
|
await nextStep.$query().patch({
|
||||||
|
position: nextStep.position - 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(nextStepQueries);
|
||||||
|
|
||||||
|
step.flow = await step.flow
|
||||||
|
.$query()
|
||||||
|
.withGraphJoined('steps')
|
||||||
|
.orderBy('steps.position', 'asc');
|
||||||
|
|
||||||
|
return step;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteStep;
|
91
packages/backend/src/graphql/mutations/update-flow-status.js
Normal file
91
packages/backend/src/graphql/mutations/update-flow-status.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import Flow from '../../models/flow.js';
|
||||||
|
import flowQueue from '../../queues/flow.js';
|
||||||
|
import {
|
||||||
|
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||||
|
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
|
} from '../../helpers/remove-job-configuration.js';
|
||||||
|
import globalVariable from '../../helpers/global-variable.js';
|
||||||
|
|
||||||
|
const JOB_NAME = 'flow';
|
||||||
|
const EVERY_15_MINUTES_CRON = '*/15 * * * *';
|
||||||
|
|
||||||
|
const updateFlowStatus = async (_parent, params, context) => {
|
||||||
|
const conditions = context.currentUser.can('publish', 'Flow');
|
||||||
|
const isCreator = conditions.isCreator;
|
||||||
|
const allFlows = Flow.query();
|
||||||
|
const userFlows = context.currentUser.$relatedQuery('flows');
|
||||||
|
const baseQuery = isCreator ? userFlows : allFlows;
|
||||||
|
|
||||||
|
let flow = await baseQuery
|
||||||
|
.clone()
|
||||||
|
.findOne({
|
||||||
|
id: params.input.id,
|
||||||
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
const newActiveValue = params.input.active;
|
||||||
|
|
||||||
|
if (flow.active === newActiveValue) {
|
||||||
|
return flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
const triggerStep = await flow.getTriggerStep();
|
||||||
|
|
||||||
|
if (triggerStep.status === 'incomplete') {
|
||||||
|
throw flow.IncompleteStepsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trigger = await triggerStep.getTriggerCommand();
|
||||||
|
const interval = trigger.getInterval?.(triggerStep.parameters);
|
||||||
|
const repeatOptions = {
|
||||||
|
pattern: interval || EVERY_15_MINUTES_CRON,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (trigger.type === 'webhook') {
|
||||||
|
const $ = await globalVariable({
|
||||||
|
flow,
|
||||||
|
connection: await triggerStep.$relatedQuery('connection'),
|
||||||
|
app: await triggerStep.getApp(),
|
||||||
|
step: triggerStep,
|
||||||
|
testRun: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newActiveValue && trigger.registerHook) {
|
||||||
|
await trigger.registerHook($);
|
||||||
|
} else if (!newActiveValue && trigger.unregisterHook) {
|
||||||
|
await trigger.unregisterHook($);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (newActiveValue) {
|
||||||
|
flow = await flow.$query().patchAndFetch({
|
||||||
|
publishedAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const jobName = `${JOB_NAME}-${flow.id}`;
|
||||||
|
|
||||||
|
await flowQueue.add(
|
||||||
|
jobName,
|
||||||
|
{ flowId: flow.id },
|
||||||
|
{
|
||||||
|
repeat: repeatOptions,
|
||||||
|
jobId: flow.id,
|
||||||
|
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
|
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const repeatableJobs = await flowQueue.getRepeatableJobs();
|
||||||
|
const job = repeatableJobs.find((job) => job.id === flow.id);
|
||||||
|
|
||||||
|
await flowQueue.removeRepeatableByKey(job.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flow = await flow.$query().withGraphFetched('steps').patchAndFetch({
|
||||||
|
active: newActiveValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
return flow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateFlowStatus;
|
68
packages/backend/src/graphql/mutations/update-step.js
Normal file
68
packages/backend/src/graphql/mutations/update-step.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import App from '../../models/app.js';
|
||||||
|
import Step from '../../models/step.js';
|
||||||
|
import Connection from '../../models/connection.js';
|
||||||
|
|
||||||
|
const updateStep = async (_parent, params, context) => {
|
||||||
|
const { isCreator } = context.currentUser.can('update', 'Flow');
|
||||||
|
const userSteps = context.currentUser.$relatedQuery('steps');
|
||||||
|
const allSteps = Step.query();
|
||||||
|
const baseQuery = isCreator ? userSteps : allSteps;
|
||||||
|
|
||||||
|
const { input } = params;
|
||||||
|
|
||||||
|
let step = await baseQuery
|
||||||
|
.findOne({
|
||||||
|
'steps.id': input.id,
|
||||||
|
flow_id: input.flow.id,
|
||||||
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
if (input.connection.id) {
|
||||||
|
let canSeeAllConnections = false;
|
||||||
|
try {
|
||||||
|
const conditions = context.currentUser.can('read', 'Connection');
|
||||||
|
|
||||||
|
canSeeAllConnections = !conditions.isCreator;
|
||||||
|
} catch {
|
||||||
|
// void
|
||||||
|
}
|
||||||
|
|
||||||
|
const userConnections = context.currentUser.$relatedQuery('connections');
|
||||||
|
const allConnections = Connection.query();
|
||||||
|
const baseConnectionsQuery = canSeeAllConnections
|
||||||
|
? allConnections
|
||||||
|
: userConnections;
|
||||||
|
|
||||||
|
const connection = await baseConnectionsQuery
|
||||||
|
.clone()
|
||||||
|
.findById(input.connection?.id);
|
||||||
|
|
||||||
|
if (!connection) {
|
||||||
|
throw new Error('The connection does not exist!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step.isTrigger) {
|
||||||
|
await App.checkAppAndTrigger(input.appKey, input.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step.isAction) {
|
||||||
|
await App.checkAppAndAction(input.appKey, input.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
step = await Step.query()
|
||||||
|
.patchAndFetchById(input.id, {
|
||||||
|
key: input.key,
|
||||||
|
appKey: input.appKey,
|
||||||
|
connectionId: input.connection.id,
|
||||||
|
parameters: input.parameters,
|
||||||
|
status: 'incomplete'
|
||||||
|
})
|
||||||
|
.withGraphFetched('connection');
|
||||||
|
|
||||||
|
await step.updateWebhookUrl();
|
||||||
|
|
||||||
|
return step;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateStep;
|
27
packages/backend/src/graphql/mutations/update-user.ee.js
Normal file
27
packages/backend/src/graphql/mutations/update-user.ee.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import User from '../../models/user.js';
|
||||||
|
|
||||||
|
const updateUser = async (_parent, params, context) => {
|
||||||
|
context.currentUser.can('update', 'User');
|
||||||
|
|
||||||
|
const userPayload = {
|
||||||
|
email: params.input.email,
|
||||||
|
fullName: params.input.fullName,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
context.currentUser.can('update', 'Role');
|
||||||
|
|
||||||
|
userPayload.roleId = params.input.role.id;
|
||||||
|
} catch {
|
||||||
|
// void
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.query().patchAndFetchById(
|
||||||
|
params.input.id,
|
||||||
|
userPayload
|
||||||
|
);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateUser;
|
@@ -3,11 +3,19 @@ type Query {
|
|||||||
}
|
}
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createConnection(input: CreateConnectionInput): Connection
|
createConnection(input: CreateConnectionInput): Connection
|
||||||
|
createFlow(input: CreateFlowInput): Flow
|
||||||
|
createUser(input: CreateUserInput): UserWithAcceptInvitationUrl
|
||||||
|
deleteCurrentUser: Boolean
|
||||||
|
deleteFlow(input: DeleteFlowInput): Boolean
|
||||||
|
deleteStep(input: DeleteStepInput): Step
|
||||||
executeFlow(input: ExecuteFlowInput): executeFlowType
|
executeFlow(input: ExecuteFlowInput): executeFlowType
|
||||||
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
||||||
resetConnection(input: ResetConnectionInput): Connection
|
resetConnection(input: ResetConnectionInput): Connection
|
||||||
updateConnection(input: UpdateConnectionInput): Connection
|
updateConnection(input: UpdateConnectionInput): Connection
|
||||||
updateCurrentUser(input: UpdateCurrentUserInput): User
|
updateCurrentUser(input: UpdateCurrentUserInput): User
|
||||||
|
updateFlowStatus(input: UpdateFlowStatusInput): Flow
|
||||||
|
updateStep(input: UpdateStepInput): Step
|
||||||
|
updateUser(input: UpdateUserInput): User
|
||||||
verifyConnection(input: VerifyConnectionInput): Connection
|
verifyConnection(input: VerifyConnectionInput): Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,14 +240,56 @@ input VerifyConnectionInput {
|
|||||||
id: String!
|
id: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CreateFlowInput {
|
||||||
|
triggerAppKey: String
|
||||||
|
connectionId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
input UpdateFlowStatusInput {
|
||||||
|
id: String!
|
||||||
|
active: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
input ExecuteFlowInput {
|
input ExecuteFlowInput {
|
||||||
stepId: String!
|
stepId: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input DeleteFlowInput {
|
||||||
|
id: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
input UpdateStepInput {
|
||||||
|
id: String
|
||||||
|
previousStepId: String
|
||||||
|
key: String
|
||||||
|
appKey: String
|
||||||
|
connection: StepConnectionInput
|
||||||
|
flow: StepFlowInput
|
||||||
|
parameters: JSONObject
|
||||||
|
previousStep: PreviousStepInput
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeleteStepInput {
|
||||||
|
id: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
input CreateUserInput {
|
||||||
|
fullName: String!
|
||||||
|
email: String!
|
||||||
|
role: UserRoleInput!
|
||||||
|
}
|
||||||
|
|
||||||
input UserRoleInput {
|
input UserRoleInput {
|
||||||
id: String
|
id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateUserInput {
|
||||||
|
id: String!
|
||||||
|
fullName: String
|
||||||
|
email: String
|
||||||
|
role: UserRoleInput
|
||||||
|
}
|
||||||
|
|
||||||
input UpdateCurrentUserInput {
|
input UpdateCurrentUserInput {
|
||||||
email: String
|
email: String
|
||||||
password: String
|
password: String
|
||||||
@@ -323,6 +373,11 @@ type User {
|
|||||||
updatedAt: String
|
updatedAt: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserWithAcceptInvitationUrl {
|
||||||
|
user: User
|
||||||
|
acceptInvitationUrl: String
|
||||||
|
}
|
||||||
|
|
||||||
type Role {
|
type Role {
|
||||||
id: String
|
id: String
|
||||||
name: String
|
name: String
|
||||||
|
@@ -37,10 +37,6 @@ const authorizationList = {
|
|||||||
action: 'read',
|
action: 'read',
|
||||||
subject: 'Flow',
|
subject: 'Flow',
|
||||||
},
|
},
|
||||||
'PATCH /api/v1/steps/:stepId': {
|
|
||||||
action: 'update',
|
|
||||||
subject: 'Flow',
|
|
||||||
},
|
|
||||||
'POST /api/v1/steps/:stepId/test': {
|
'POST /api/v1/steps/:stepId/test': {
|
||||||
action: 'update',
|
action: 'update',
|
||||||
subject: 'Flow',
|
subject: 'Flow',
|
||||||
|
@@ -125,13 +125,7 @@ class Flow extends Base {
|
|||||||
|
|
||||||
get IncompleteStepsError() {
|
get IncompleteStepsError() {
|
||||||
return new ValidationError({
|
return new ValidationError({
|
||||||
data: {
|
message: 'All steps should be completed before updating flow status!',
|
||||||
flow: [
|
|
||||||
{
|
|
||||||
message: 'All steps should be completed before updating flow status!'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'incompleteStepsError',
|
type: 'incompleteStepsError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -372,13 +366,8 @@ class Flow extends Base {
|
|||||||
|
|
||||||
if (allSteps.length < 2) {
|
if (allSteps.length < 2) {
|
||||||
throw new ValidationError({
|
throw new ValidationError({
|
||||||
data: {
|
message:
|
||||||
flow: [
|
'There should be at least one trigger and one action steps in the flow!',
|
||||||
{
|
|
||||||
message: 'There should be at least one trigger and one action steps in the flow!'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
type: 'insufficientStepsError',
|
type: 'insufficientStepsError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -334,39 +334,6 @@ class Step extends Base {
|
|||||||
|
|
||||||
await Promise.all(nextStepQueries);
|
await Promise.all(nextStepQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFor(user, newStepData) {
|
|
||||||
const { connectionId, appKey, key, parameters } = newStepData;
|
|
||||||
|
|
||||||
if (connectionId && (appKey || this.appKey)) {
|
|
||||||
await user.authorizedConnections
|
|
||||||
.findOne({
|
|
||||||
id: connectionId,
|
|
||||||
key: appKey || this.appKey,
|
|
||||||
})
|
|
||||||
.throwIfNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isTrigger && appKey && key) {
|
|
||||||
await App.checkAppAndTrigger(appKey, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isAction && appKey && key) {
|
|
||||||
await App.checkAppAndAction(appKey, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedStep = await this.$query().patchAndFetch({
|
|
||||||
key: key,
|
|
||||||
appKey: appKey,
|
|
||||||
connectionId: connectionId,
|
|
||||||
parameters: parameters,
|
|
||||||
status: 'incomplete',
|
|
||||||
});
|
|
||||||
|
|
||||||
await updatedStep.updateWebhookUrl();
|
|
||||||
|
|
||||||
return updatedStep;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Step;
|
export default Step;
|
||||||
|
@@ -180,10 +180,6 @@ class User extends Base {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
static get virtualAttributes() {
|
|
||||||
return ['acceptInvitationUrl'];
|
|
||||||
}
|
|
||||||
|
|
||||||
get authorizedFlows() {
|
get authorizedFlows() {
|
||||||
const conditions = this.can('read', 'Flow');
|
const conditions = this.can('read', 'Flow');
|
||||||
return conditions.isCreator ? this.$relatedQuery('flows') : Flow.query();
|
return conditions.isCreator ? this.$relatedQuery('flows') : Flow.query();
|
||||||
@@ -208,10 +204,6 @@ class User extends Base {
|
|||||||
: Execution.query();
|
: Execution.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
get acceptInvitationUrl() {
|
|
||||||
return `${appConfig.webAppUrl}/accept-invitation?token=${this.invitationToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async authenticate(email, password) {
|
static async authenticate(email, password) {
|
||||||
const user = await User.query().findOne({
|
const user = await User.query().findOne({
|
||||||
email: email?.toLowerCase() || null,
|
email: email?.toLowerCase() || null,
|
||||||
@@ -370,6 +362,7 @@ class User extends Base {
|
|||||||
await this.generateInvitationToken();
|
await this.generateInvitationToken();
|
||||||
|
|
||||||
const jobName = `Invitation Email - ${this.id}`;
|
const jobName = `Invitation Email - ${this.id}`;
|
||||||
|
const acceptInvitationUrl = `${appConfig.webAppUrl}/accept-invitation?token=${this.invitationToken}`;
|
||||||
|
|
||||||
const jobPayload = {
|
const jobPayload = {
|
||||||
email: this.email,
|
email: this.email,
|
||||||
@@ -377,7 +370,7 @@ class User extends Base {
|
|||||||
template: 'invitation-instructions',
|
template: 'invitation-instructions',
|
||||||
params: {
|
params: {
|
||||||
fullName: this.fullName,
|
fullName: this.fullName,
|
||||||
acceptInvitationUrl: this.acceptInvitationUrl,
|
acceptInvitationUrl,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -7,7 +7,6 @@ import getPreviousStepsAction from '../../../controllers/api/v1/steps/get-previo
|
|||||||
import createDynamicFieldsAction from '../../../controllers/api/v1/steps/create-dynamic-fields.js';
|
import createDynamicFieldsAction from '../../../controllers/api/v1/steps/create-dynamic-fields.js';
|
||||||
import createDynamicDataAction from '../../../controllers/api/v1/steps/create-dynamic-data.js';
|
import createDynamicDataAction from '../../../controllers/api/v1/steps/create-dynamic-data.js';
|
||||||
import deleteStepAction from '../../../controllers/api/v1/steps/delete-step.js';
|
import deleteStepAction from '../../../controllers/api/v1/steps/delete-step.js';
|
||||||
import updateStepAction from '../../../controllers/api/v1/steps/update-step.js';
|
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -41,7 +40,6 @@ router.post(
|
|||||||
createDynamicDataAction
|
createDynamicDataAction
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch('/:stepId', authenticateUser, authorizeUser, updateStepAction);
|
|
||||||
router.delete('/:stepId', authenticateUser, authorizeUser, deleteStepAction);
|
router.delete('/:stepId', authenticateUser, authorizeUser, deleteStepAction);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
import userSerializer from '../user.js';
|
|
||||||
|
|
||||||
const adminUserSerializer = (user) => {
|
|
||||||
const userData = userSerializer(user);
|
|
||||||
|
|
||||||
userData.acceptInvitationUrl = user.acceptInvitationUrl;
|
|
||||||
|
|
||||||
return userData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default adminUserSerializer;
|
|
@@ -1,19 +0,0 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
|
||||||
import { createUser } from '../../../test/factories/user';
|
|
||||||
import adminUserSerializer from './user.js';
|
|
||||||
|
|
||||||
describe('adminUserSerializer', () => {
|
|
||||||
let user;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await createUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return user data with accept invitation url', async () => {
|
|
||||||
const serializedUser = adminUserSerializer(user);
|
|
||||||
|
|
||||||
expect(serializedUser.acceptInvitationUrl).toEqual(
|
|
||||||
user.acceptInvitationUrl
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -16,10 +16,8 @@ import actionSerializer from './action.js';
|
|||||||
import executionSerializer from './execution.js';
|
import executionSerializer from './execution.js';
|
||||||
import executionStepSerializer from './execution-step.js';
|
import executionStepSerializer from './execution-step.js';
|
||||||
import subscriptionSerializer from './subscription.ee.js';
|
import subscriptionSerializer from './subscription.ee.js';
|
||||||
import adminUserSerializer from './admin/user.js';
|
|
||||||
|
|
||||||
const serializers = {
|
const serializers = {
|
||||||
AdminUser: adminUserSerializer,
|
|
||||||
User: userSerializer,
|
User: userSerializer,
|
||||||
Role: roleSerializer,
|
Role: roleSerializer,
|
||||||
Permission: permissionSerializer,
|
Permission: permissionSerializer,
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
import { ValidationError } from 'objection';
|
|
||||||
|
|
||||||
export const toRequireProperty = async (model, requiredProperty) => {
|
|
||||||
try {
|
|
||||||
await model.query().insert({});
|
|
||||||
} catch (error) {
|
|
||||||
if (
|
|
||||||
error instanceof ValidationError &&
|
|
||||||
error.message.includes(
|
|
||||||
`${requiredProperty}: must have required property '${requiredProperty}'`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
pass: true,
|
|
||||||
message: () =>
|
|
||||||
`Expected ${requiredProperty} to be required, and it was.`,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
pass: false,
|
|
||||||
message: () =>
|
|
||||||
`Expected ${requiredProperty} to be required, but it was not found in the error message.`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
pass: false,
|
|
||||||
message: () =>
|
|
||||||
`Expected ${requiredProperty} to be required, but no ValidationError was thrown.`,
|
|
||||||
};
|
|
||||||
};
|
|
@@ -1,30 +0,0 @@
|
|||||||
import appConfig from '../../../../../../../src/config/app.js';
|
|
||||||
|
|
||||||
const createUserMock = (user) => {
|
|
||||||
const userData = {
|
|
||||||
createdAt: user.createdAt.getTime(),
|
|
||||||
email: user.email,
|
|
||||||
fullName: user.fullName,
|
|
||||||
id: user.id,
|
|
||||||
status: user.status,
|
|
||||||
updatedAt: user.updatedAt.getTime(),
|
|
||||||
acceptInvitationUrl: user.acceptInvitationUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (appConfig.isCloud && user.trialExpiryDate) {
|
|
||||||
userData.trialExpiryDate = user.trialExpiryDate.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: userData,
|
|
||||||
meta: {
|
|
||||||
count: 1,
|
|
||||||
currentPage: null,
|
|
||||||
isArray: false,
|
|
||||||
totalPages: null,
|
|
||||||
type: 'User',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createUserMock;
|
|
@@ -1,26 +0,0 @@
|
|||||||
const updateStepMock = (step) => {
|
|
||||||
const data = {
|
|
||||||
id: step.id,
|
|
||||||
type: step.type || 'action',
|
|
||||||
key: step.key || null,
|
|
||||||
appKey: step.appKey || null,
|
|
||||||
iconUrl: step.iconUrl || null,
|
|
||||||
webhookUrl: step.webhookUrl || null,
|
|
||||||
status: step.status || 'incomplete',
|
|
||||||
position: step.position,
|
|
||||||
parameters: step.parameters || {},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
meta: {
|
|
||||||
type: 'Step',
|
|
||||||
count: 1,
|
|
||||||
isArray: false,
|
|
||||||
currentPage: null,
|
|
||||||
totalPages: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateStepMock;
|
|
@@ -2,7 +2,6 @@ import { Model } from 'objection';
|
|||||||
import { client as knex } from '../../src/config/database.js';
|
import { client as knex } from '../../src/config/database.js';
|
||||||
import logger from '../../src/helpers/logger.js';
|
import logger from '../../src/helpers/logger.js';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import './insert-assertions.js';
|
|
||||||
|
|
||||||
global.beforeAll(async () => {
|
global.beforeAll(async () => {
|
||||||
global.knex = null;
|
global.knex = null;
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
import { expect } from 'vitest';
|
|
||||||
import { toRequireProperty } from '../assertions/to-require-property';
|
|
||||||
|
|
||||||
expect.extend({
|
|
||||||
async toRequireProperty(model, requiredProperty) {
|
|
||||||
return await toRequireProperty(model, requiredProperty);
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,40 +0,0 @@
|
|||||||
import { expect } from '@playwright/test';
|
|
||||||
|
|
||||||
const { AuthenticatedPage } = require('../authenticated-page');
|
|
||||||
|
|
||||||
export class AdminApplicationAuthClientsPage extends AuthenticatedPage {
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
constructor(page) {
|
|
||||||
super(page);
|
|
||||||
|
|
||||||
this.authClientsTab = this.page.getByText('AUTH CLIENTS');
|
|
||||||
this.saveButton = this.page.getByTestId('submitButton');
|
|
||||||
this.successSnackbar = this.page.getByTestId('snackbar-save-admin-apps-settings-success');
|
|
||||||
this.createFirstAuthClientButton = this.page.getByTestId('no-results');
|
|
||||||
this.createAuthClientButton = this.page.getByTestId('create-auth-client-button');
|
|
||||||
this.submitAuthClientFormButton = this.page.getByTestId('submit-auth-client-form');
|
|
||||||
this.authClientEntry = this.page.getByTestId('auth-client');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openAuthClientsTab() {
|
|
||||||
this.authClientsTab.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async openFirstAuthClientCreateForm() {
|
|
||||||
this.createFirstAuthClientButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async openAuthClientCreateForm() {
|
|
||||||
this.createAuthClientButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async submitAuthClientForm() {
|
|
||||||
this.submitAuthClientFormButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async authClientShouldBeVisible(authClientName) {
|
|
||||||
await expect(this.authClientEntry.filter({ hasText: authClientName })).toBeVisible();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
const { AuthenticatedPage } = require('../authenticated-page');
|
|
||||||
const { expect } = require('@playwright/test');
|
|
||||||
|
|
||||||
export class AdminApplicationSettingsPage extends AuthenticatedPage {
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
constructor(page) {
|
|
||||||
super(page);
|
|
||||||
|
|
||||||
this.allowCustomConnectionsSwitch = this.page.locator('[name="allowCustomConnection"]');
|
|
||||||
this.allowSharedConnectionsSwitch = this.page.locator('[name="shared"]');
|
|
||||||
this.disableConnectionsSwitch = this.page.locator('[name="disabled"]');
|
|
||||||
this.saveButton = this.page.getByTestId('submit-button');
|
|
||||||
this.successSnackbar = this.page.getByTestId('snackbar-save-admin-apps-settings-success');
|
|
||||||
}
|
|
||||||
|
|
||||||
async allowCustomConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.allowCustomConnectionsSwitch.check();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async allowSharedConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.allowSharedConnectionsSwitch.check();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async disallowConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.disableConnectionsSwitch.check();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async disallowCustomConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).toBeChecked();
|
|
||||||
await this.allowCustomConnectionsSwitch.uncheck();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async disallowSharedConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).toBeChecked();
|
|
||||||
await this.allowSharedConnectionsSwitch.uncheck();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async allowConnections() {
|
|
||||||
await expect(this.disableConnectionsSwitch).toBeChecked();
|
|
||||||
await this.disableConnectionsSwitch.uncheck();
|
|
||||||
await this.saveButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async expectSuccessSnackbarToBeVisible() {
|
|
||||||
await expect(this.successSnackbar).toHaveCount(1);
|
|
||||||
await this.successSnackbar.click();
|
|
||||||
await expect(this.successSnackbar).toHaveCount(0);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
const { AuthenticatedPage } = require('../authenticated-page');
|
|
||||||
|
|
||||||
export class AdminApplicationsPage extends AuthenticatedPage {
|
|
||||||
screenshotPath = '/admin-settings/apps';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('@playwright/test').Page} page
|
|
||||||
*/
|
|
||||||
constructor(page) {
|
|
||||||
super(page);
|
|
||||||
|
|
||||||
this.searchInput = page.locator('[id="search-input"]');
|
|
||||||
this.appRow = page.getByTestId('app-row');
|
|
||||||
this.appsDrawerLink = page.getByTestId('apps-drawer-link');
|
|
||||||
this.appsLoader = page.getByTestId('apps-loader');
|
|
||||||
}
|
|
||||||
|
|
||||||
async openApplication(appName) {
|
|
||||||
await this.searchInput.fill(appName);
|
|
||||||
await this.appRow.locator(this.page.getByText(appName)).click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async navigateTo() {
|
|
||||||
await this.profileMenuButton.click();
|
|
||||||
await this.adminMenuItem.click();
|
|
||||||
await this.appsDrawerLink.click();
|
|
||||||
await this.isMounted();
|
|
||||||
await this.appsLoader.waitFor({
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,10 +6,6 @@ const { AdminRolesPage } = require('./roles-page');
|
|||||||
const { AdminCreateRolePage } = require('./create-role-page');
|
const { AdminCreateRolePage } = require('./create-role-page');
|
||||||
const { AdminEditRolePage } = require('./edit-role-page');
|
const { AdminEditRolePage } = require('./edit-role-page');
|
||||||
|
|
||||||
const { AdminApplicationsPage } = require('./applications-page');
|
|
||||||
const { AdminApplicationSettingsPage } = require('./application-settings-page');
|
|
||||||
const { AdminApplicationAuthClientsPage } = require('./application-auth-clients-page');
|
|
||||||
|
|
||||||
export const adminFixtures = {
|
export const adminFixtures = {
|
||||||
adminUsersPage: async ({ page }, use) => {
|
adminUsersPage: async ({ page }, use) => {
|
||||||
await use(new AdminUsersPage(page));
|
await use(new AdminUsersPage(page));
|
||||||
@@ -29,14 +25,5 @@ export const adminFixtures = {
|
|||||||
adminCreateRolePage: async ({ page}, use) => {
|
adminCreateRolePage: async ({ page}, use) => {
|
||||||
await use(new AdminCreateRolePage(page));
|
await use(new AdminCreateRolePage(page));
|
||||||
},
|
},
|
||||||
adminApplicationsPage: async ({ page }, use) => {
|
|
||||||
await use(new AdminApplicationsPage(page));
|
|
||||||
},
|
|
||||||
adminApplicationSettingsPage: async ({ page }, use) => {
|
|
||||||
await use(new AdminApplicationSettingsPage(page));
|
|
||||||
},
|
|
||||||
adminApplicationAuthClientsPage: async ({ page }, use) => {
|
|
||||||
await use(new AdminApplicationAuthClientsPage(page));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
const { expect } = require('../fixtures/index');
|
|
||||||
|
|
||||||
export const expectNoDelayedJobForFlow = async (flowId, request) => {
|
|
||||||
const token = btoa(`${process.env.BULLMQ_DASHBOARD_USERNAME}:${process.env.BULLMQ_DASHBOARD_PASSWORD}`);
|
|
||||||
const queues = await request.get(`${process.env.BACKEND_APP_URL}/admin/queues/api/queues?activeQueue=flow&status=delayed&page=1`, {
|
|
||||||
headers: {'Authorization': `Basic ${token}`}
|
|
||||||
});
|
|
||||||
const queuesJsonResponse = await queues.json();
|
|
||||||
const flowQueue = queuesJsonResponse.queues.find(queue => queue.name === "flow");
|
|
||||||
await expect(flowQueue.jobs.find(job => job.name === `flow-${flowId}`)).toBeUndefined();
|
|
||||||
};
|
|
@@ -24,21 +24,22 @@ export class FlowEditorPage extends AuthenticatedPage {
|
|||||||
this.unpublishFlowButton = this.page.getByTestId('unpublish-flow-button');
|
this.unpublishFlowButton = this.page.getByTestId('unpublish-flow-button');
|
||||||
this.publishFlowButton = this.page.getByTestId('publish-flow-button');
|
this.publishFlowButton = this.page.getByTestId('publish-flow-button');
|
||||||
this.infoSnackbar = this.page.getByTestId('flow-cannot-edit-info-snackbar');
|
this.infoSnackbar = this.page.getByTestId('flow-cannot-edit-info-snackbar');
|
||||||
this.errorSnackbar = this.page.getByTestId('snackbar-error');
|
|
||||||
this.trigger = this.page.getByLabel('Trigger on weekends?');
|
this.trigger = this.page.getByLabel('Trigger on weekends?');
|
||||||
this.stepCircularLoader = this.page.getByTestId('step-circular-loader');
|
this.stepCircularLoader = this.page.getByTestId('step-circular-loader');
|
||||||
this.flowName = this.page.getByTestId('editableTypography');
|
this.flowName = this.page.getByTestId('editableTypography');
|
||||||
this.flowNameInput = this.page
|
this.flowNameInput = this.page
|
||||||
.getByTestId('editableTypographyInput')
|
.getByTestId('editableTypographyInput')
|
||||||
.locator('input');
|
.locator('input');
|
||||||
|
|
||||||
this.flowStep = this.page.getByTestId('flow-step');
|
|
||||||
this.rssFeedUrl = this.page.getByTestId('parameters.feedUrl-text');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createWebhookTrigger(workSynchronously) {
|
async createWebhookTrigger(workSynchronously) {
|
||||||
this.chooseAppAndTrigger('Webhook', 'Catch raw webhook');
|
await this.appAutocomplete.click();
|
||||||
|
await this.page.getByRole('option', { name: 'Webhook' }).click();
|
||||||
|
|
||||||
|
await expect(this.eventAutocomplete).toBeVisible();
|
||||||
|
await this.eventAutocomplete.click();
|
||||||
|
await this.page.getByRole('option', { name: 'Catch raw webhook' }).click();
|
||||||
|
await this.continueButton.click();
|
||||||
await this.page
|
await this.page
|
||||||
.getByTestId('parameters.workSynchronously-autocomplete')
|
.getByTestId('parameters.workSynchronously-autocomplete')
|
||||||
.click();
|
.click();
|
||||||
@@ -66,25 +67,12 @@ export class FlowEditorPage extends AuthenticatedPage {
|
|||||||
return await webhookUrl.inputValue();
|
return await webhookUrl.inputValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
async chooseAppAndTrigger(appName, triggerEvent) {
|
|
||||||
await expect(this.appAutocomplete).toHaveCount(1);
|
|
||||||
await this.appAutocomplete.click();
|
|
||||||
await this.page.getByRole('option', { name: appName }).click();
|
|
||||||
await expect(this.eventAutocomplete).toBeVisible();
|
|
||||||
await this.eventAutocomplete.click();
|
|
||||||
await Promise.all([
|
|
||||||
this.page.waitForResponse(resp => /(apps\/.*\/triggers\/.*\/substeps)/.test(resp.url()) && resp.status() === 200),
|
|
||||||
this.page.getByRole('option', { name: triggerEvent }).click(),
|
|
||||||
]);
|
|
||||||
await this.continueButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async chooseAppAndEvent(appName, eventName) {
|
async chooseAppAndEvent(appName, eventName) {
|
||||||
await expect(this.appAutocomplete).toHaveCount(1);
|
|
||||||
await this.appAutocomplete.click();
|
await this.appAutocomplete.click();
|
||||||
await this.page.getByRole('option', { name: appName }).click();
|
await this.page.getByRole('option', { name: appName }).click();
|
||||||
await expect(this.eventAutocomplete).toBeVisible();
|
await expect(this.eventAutocomplete).toBeVisible();
|
||||||
await this.eventAutocomplete.click();
|
await this.eventAutocomplete.click();
|
||||||
|
await expect(this.page.locator('[data-testid="ErrorIcon"]')).toHaveCount(2);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.page.waitForResponse(resp => /(apps\/.*\/actions\/.*\/substeps)/.test(resp.url()) && resp.status() === 200),
|
this.page.waitForResponse(resp => /(apps\/.*\/actions\/.*\/substeps)/.test(resp.url()) && resp.status() === 200),
|
||||||
this.page.getByRole('option', { name: eventName }).click(),
|
this.page.getByRole('option', { name: eventName }).click(),
|
||||||
@@ -98,10 +86,4 @@ export class FlowEditorPage extends AuthenticatedPage {
|
|||||||
await expect(this.testOutput).toBeVisible();
|
await expect(this.testOutput).toBeVisible();
|
||||||
await this.continueButton.click();
|
await this.continueButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async dismissErrorSnackbar() {
|
|
||||||
await expect(this.errorSnackbar).toBeVisible();
|
|
||||||
await this.errorSnackbar.click();
|
|
||||||
await expect(this.errorSnackbar).toHaveCount(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
const { Pool } = require('pg');
|
const { Client } = require('pg');
|
||||||
|
|
||||||
const pool = new Pool({
|
const client = new Client({
|
||||||
max: 20,
|
|
||||||
idleTimeoutMillis: 30000,
|
|
||||||
connectionTimeoutMillis: 2000,
|
|
||||||
host: process.env.POSTGRES_HOST,
|
host: process.env.POSTGRES_HOST,
|
||||||
user: process.env.POSTGRES_USERNAME,
|
user: process.env.POSTGRES_USERNAME,
|
||||||
port: process.env.POSTGRES_PORT,
|
port: process.env.POSTGRES_PORT,
|
||||||
@@ -11,4 +8,4 @@ const pool = new Pool({
|
|||||||
database: process.env.POSTGRES_DATABASE
|
database: process.env.POSTGRES_DATABASE
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.pgPool = pool;
|
exports.client = client;
|
@@ -1,18 +0,0 @@
|
|||||||
const { pgPool } = require('./postgres-config');
|
|
||||||
const { expect } = require('../../fixtures/index');
|
|
||||||
|
|
||||||
export const flowShouldNotHavePublishedAtDateFilled = async (flowId) => {
|
|
||||||
const queryFlow = {
|
|
||||||
text: 'SELECT * FROM flows WHERE id = $1',
|
|
||||||
values: [flowId]
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const queryFlowResult = await pgPool.query(queryFlow);
|
|
||||||
expect(queryFlowResult.rowCount).toEqual(1);
|
|
||||||
expect(queryFlowResult.rows[0].published_at).toBeNull();
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,231 +0,0 @@
|
|||||||
const { test, expect } = require('../../fixtures/index');
|
|
||||||
const { pgPool } = require('../../fixtures/postgres/postgres-config');
|
|
||||||
|
|
||||||
test.describe('Admin Applications', () => {
|
|
||||||
test.beforeAll(async () => {
|
|
||||||
const deleteAppAuthClients = {
|
|
||||||
text: 'DELETE FROM app_auth_clients WHERE app_key in ($1, $2, $3, $4, $5)',
|
|
||||||
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit']
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteAppConfigs = {
|
|
||||||
text: 'DELETE FROM app_configs WHERE key in ($1, $2, $3, $4, $5)',
|
|
||||||
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit']
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const deleteAppAuthClientsResult = await pgPool.query(deleteAppAuthClients);
|
|
||||||
expect(deleteAppAuthClientsResult.command).toBe('DELETE');
|
|
||||||
const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs);
|
|
||||||
expect(deleteAppConfigsResult.command).toBe('DELETE');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.beforeEach(async ({ adminApplicationsPage }) => {
|
|
||||||
await adminApplicationsPage.navigateTo();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Admin should be able to toggle Application settings', async ({
|
|
||||||
adminApplicationsPage,
|
|
||||||
adminApplicationSettingsPage,
|
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
await adminApplicationsPage.openApplication('Carbone');
|
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/carbone/settings');
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.allowCustomConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
await adminApplicationSettingsPage.allowSharedConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
await adminApplicationSettingsPage.disallowConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
|
|
||||||
await page.reload();
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.disallowCustomConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
await adminApplicationSettingsPage.disallowSharedConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
await adminApplicationSettingsPage.allowConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should allow only custom connections', async ({
|
|
||||||
adminApplicationsPage,
|
|
||||||
adminApplicationSettingsPage,
|
|
||||||
flowEditorPage,
|
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
await adminApplicationsPage.openApplication('Spotify');
|
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/spotify/settings');
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.allowCustomConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByTestId('create-flow-button').click();
|
|
||||||
await page.waitForURL(
|
|
||||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(flowEditorPage.flowStep).toHaveCount(2);
|
|
||||||
const triggerStep = flowEditorPage.flowStep.last();
|
|
||||||
await triggerStep.click();
|
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent("Spotify", "Create Playlist");
|
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
|
||||||
|
|
||||||
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
|
||||||
|
|
||||||
await expect(newConnectionOption).toBeEnabled();
|
|
||||||
await expect(newConnectionOption).toHaveCount(1);
|
|
||||||
await expect(newSharedConnectionOption).toHaveCount(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should allow only shared connections', async ({
|
|
||||||
adminApplicationsPage,
|
|
||||||
adminApplicationSettingsPage,
|
|
||||||
adminApplicationAuthClientsPage,
|
|
||||||
flowEditorPage,
|
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
await adminApplicationsPage.openApplication('Reddit');
|
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/reddit/settings');
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.allowSharedConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
|
|
||||||
await adminApplicationAuthClientsPage.openAuthClientsTab();
|
|
||||||
await adminApplicationAuthClientsPage.openFirstAuthClientCreateForm();
|
|
||||||
const authClientForm = page.getByTestId("auth-client-form");
|
|
||||||
await authClientForm.locator(page.getByTestId('switch')).check();
|
|
||||||
await authClientForm.locator(page.locator('[name="name"]')).fill('redditAuthClient');
|
|
||||||
await authClientForm.locator(page.locator('[name="clientId"]')).fill('redditClientId');
|
|
||||||
await authClientForm.locator(page.locator('[name="clientSecret"]')).fill('redditClientSecret');
|
|
||||||
await adminApplicationAuthClientsPage.submitAuthClientForm();
|
|
||||||
await adminApplicationAuthClientsPage.authClientShouldBeVisible('redditAuthClient');
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByTestId('create-flow-button').click();
|
|
||||||
await page.waitForURL(
|
|
||||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(flowEditorPage.flowStep).toHaveCount(2);
|
|
||||||
const triggerStep = flowEditorPage.flowStep.last();
|
|
||||||
await triggerStep.click();
|
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent("Reddit", "Create link post");
|
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
|
||||||
|
|
||||||
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
|
||||||
|
|
||||||
await expect(newConnectionOption).toHaveCount(0);
|
|
||||||
await expect(newSharedConnectionOption).toBeEnabled();
|
|
||||||
await expect(newSharedConnectionOption).toHaveCount(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not allow any connections', async ({
|
|
||||||
adminApplicationsPage,
|
|
||||||
adminApplicationSettingsPage,
|
|
||||||
flowEditorPage,
|
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
await adminApplicationsPage.openApplication('DeepL');
|
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/deepl/settings');
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.disallowConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByTestId('create-flow-button').click();
|
|
||||||
await page.waitForURL(
|
|
||||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(flowEditorPage.flowStep).toHaveCount(2);
|
|
||||||
const triggerStep = flowEditorPage.flowStep.last();
|
|
||||||
await triggerStep.click();
|
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent("DeepL", "Translate text");
|
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
|
||||||
|
|
||||||
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
|
||||||
const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' });
|
|
||||||
|
|
||||||
await expect(noConnectionsOption).toHaveCount(1);
|
|
||||||
await expect(newConnectionOption).toHaveCount(0);
|
|
||||||
await expect(newSharedConnectionOption).toHaveCount(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not allow new connections but only already created', async ({
|
|
||||||
adminApplicationsPage,
|
|
||||||
adminApplicationSettingsPage,
|
|
||||||
flowEditorPage,
|
|
||||||
page
|
|
||||||
}) => {
|
|
||||||
const queryUser = {
|
|
||||||
text: 'SELECT * FROM users WHERE email = $1',
|
|
||||||
values: [process.env.LOGIN_EMAIL]
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const queryUserResult = await pgPool.query(queryUser);
|
|
||||||
expect(queryUserResult.rowCount).toEqual(1);
|
|
||||||
|
|
||||||
const createMailchimpConnection = {
|
|
||||||
text: 'INSERT INTO connections (key, data, user_id, verified, draft) VALUES ($1, $2, $3, $4, $5)',
|
|
||||||
values: [
|
|
||||||
'mailchimp',
|
|
||||||
"U2FsdGVkX1+cAtdHwLiuRL4DaK/T1aljeeKyPMmtWK0AmAIsKhYwQiuyQCYJO3mdZ31z73hqF2Y+yj2Kn2/IIpLRqCxB2sC0rCDCZyolzOZ290YcBXSzYRzRUxhoOcZEtwYDKsy8AHygKK/tkj9uv9k6wOe1LjipNik4VmRhKjEYizzjLrJpbeU1oY+qW0GBpPYomFTeNf+MejSSmsUYyYJ8+E/4GeEfaonvsTSwMT7AId98Lck6Vy4wrfgpm7sZZ8xU15/HqXZNc8UCo2iTdw45xj/Oov9+brX4WUASFPG8aYrK8dl/EdaOvr89P8uIofbSNZ25GjJvVF5ymarrPkTZ7djjJXchzpwBY+7GTJfs3funR/vIk0Hq95jgOFFP1liZyqTXSa49ojG3hzojRQ==",
|
|
||||||
queryUserResult.rows[0].id,
|
|
||||||
'true',
|
|
||||||
'false'
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMailchimpConnectionResult = await pgPool.query(createMailchimpConnection);
|
|
||||||
expect(createMailchimpConnectionResult.rowCount).toBe(1);
|
|
||||||
expect(createMailchimpConnectionResult.command).toBe('INSERT');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
await adminApplicationsPage.openApplication('Mailchimp');
|
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/mailchimp/settings');
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.disallowConnections();
|
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
|
||||||
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByTestId('create-flow-button').click();
|
|
||||||
await page.waitForURL(
|
|
||||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(flowEditorPage.flowStep).toHaveCount(2);
|
|
||||||
const triggerStep = flowEditorPage.flowStep.last();
|
|
||||||
await triggerStep.click();
|
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent("Mailchimp", "Create campaign");
|
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
|
||||||
await expect(page.getByRole('option').first()).toHaveText('Unnamed');
|
|
||||||
|
|
||||||
const existingConnection = page.getByRole('option').filter({ hasText: 'Unnamed' });
|
|
||||||
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
|
||||||
const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' });
|
|
||||||
|
|
||||||
await expect(await existingConnection.count()).toBeGreaterThan(0);
|
|
||||||
await expect(noConnectionsOption).toHaveCount(0);
|
|
||||||
await expect(newConnectionOption).toHaveCount(0);
|
|
||||||
await expect(newSharedConnectionOption).toHaveCount(0);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -156,8 +156,15 @@ test.describe('User management page', () => {
|
|||||||
'option', { name: 'Admin' }
|
'option', { name: 'Admin' }
|
||||||
).click();
|
).click();
|
||||||
await adminCreateUserPage.createButton.click();
|
await adminCreateUserPage.createButton.click();
|
||||||
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
await adminUsersPage.snackbar.waitFor({
|
||||||
await expect(snackbar.variant).toBe('error');
|
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();
|
await adminUsersPage.closeSnackbar();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
const { test, expect } = require('../../fixtures/index');
|
|
||||||
const { expectNoDelayedJobForFlow } = require('../../fixtures/bullmq-helper');
|
|
||||||
const { flowShouldNotHavePublishedAtDateFilled } = require('../../fixtures/postgres/postgres-helper');
|
|
||||||
|
|
||||||
test.describe('Flow Validation', () => {
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await page.getByTestId('create-flow-button').click();
|
|
||||||
await page.waitForURL(
|
|
||||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
|
||||||
);
|
|
||||||
await expect(page.getByTestId('flow-step')).toHaveCount(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should not be able to publish flow without trigger', async ({
|
|
||||||
flowEditorPage,
|
|
||||||
page,
|
|
||||||
request
|
|
||||||
}) => {
|
|
||||||
const flowId = await page.url().split('editor/').pop();
|
|
||||||
|
|
||||||
await flowEditorPage.flowName.click();
|
|
||||||
await flowEditorPage.flowNameInput.fill('incompleteFlow');
|
|
||||||
await flowEditorPage.chooseAppAndTrigger('RSS', 'New items in feed');
|
|
||||||
|
|
||||||
await flowEditorPage.publishFlowButton.click();
|
|
||||||
await flowEditorPage.dismissErrorSnackbar();
|
|
||||||
|
|
||||||
await flowShouldNotHavePublishedAtDateFilled(flowId);
|
|
||||||
await expectNoDelayedJobForFlow(flowId, request);
|
|
||||||
|
|
||||||
await flowEditorPage.rssFeedUrl.fill('http://rss.cnn.com/rss/money_mostpopular.rss');
|
|
||||||
await expect(flowEditorPage.continueButton).toHaveCount(1);
|
|
||||||
await flowEditorPage.continueButton.click();
|
|
||||||
|
|
||||||
await flowEditorPage.publishFlowButton.click();
|
|
||||||
await flowEditorPage.dismissErrorSnackbar();
|
|
||||||
|
|
||||||
await flowShouldNotHavePublishedAtDateFilled(flowId);
|
|
||||||
await expectNoDelayedJobForFlow(flowId, request);
|
|
||||||
|
|
||||||
await expect(flowEditorPage.testOutput).not.toBeVisible();
|
|
||||||
await flowEditorPage.testAndContinueButton.click();
|
|
||||||
await expect(flowEditorPage.testOutput).toBeVisible();
|
|
||||||
await expect(flowEditorPage.hasNoOutput).not.toBeVisible();
|
|
||||||
await flowEditorPage.continueButton.click();
|
|
||||||
|
|
||||||
await flowEditorPage.publishFlowButton.click();
|
|
||||||
await expect(page.getByTestId('snackbar-error')).toBeVisible();
|
|
||||||
|
|
||||||
await flowShouldNotHavePublishedAtDateFilled(flowId);
|
|
||||||
await expectNoDelayedJobForFlow(flowId, request);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -4,9 +4,9 @@ import knexConfig from '../knexfile.js';
|
|||||||
|
|
||||||
publicTest.describe('restore db', () => {
|
publicTest.describe('restore db', () => {
|
||||||
publicTest('clean db and perform migrations', async () => {
|
publicTest('clean db and perform migrations', async () => {
|
||||||
const knexClient = knex(knexConfig);
|
const knexClient = knex(knexConfig)
|
||||||
const migrator = knexClient.migrate;
|
const migrator = knexClient.migrate;
|
||||||
await migrator.rollback({}, true);
|
await migrator.rollback({}, true);
|
||||||
await migrator.latest();
|
await migrator.latest();
|
||||||
});
|
})
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
const { publicTest, expect } = require('../../fixtures/index');
|
const { publicTest, expect } = require('../../fixtures/index');
|
||||||
const { pgPool } = require('../../fixtures/postgres/postgres-config');
|
const { client } = require('../../fixtures/postgres-client-config');
|
||||||
const { DateTime } = require('luxon');
|
const { DateTime } = require('luxon');
|
||||||
|
|
||||||
publicTest.describe('Accept invitation page', () => {
|
publicTest.describe('Accept invitation page', () => {
|
||||||
@@ -20,6 +20,14 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
const expiredTokenDate = DateTime.now().minus({days: 3}).toISO();
|
const expiredTokenDate = DateTime.now().minus({days: 3}).toISO();
|
||||||
const token = (Math.random() + 1).toString(36).substring(2);
|
const token = (Math.random() + 1).toString(36).substring(2);
|
||||||
|
|
||||||
|
publicTest.beforeAll(async () => {
|
||||||
|
await client.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
publicTest.afterAll(async () => {
|
||||||
|
await client.end();
|
||||||
|
});
|
||||||
|
|
||||||
publicTest('should not be able to set the password if token is expired', async ({ acceptInvitationPage, adminCreateUserPage }) => {
|
publicTest('should not be able to set the password if token is expired', async ({ acceptInvitationPage, adminCreateUserPage }) => {
|
||||||
adminCreateUserPage.seed(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
|
adminCreateUserPage.seed(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||||
const user = adminCreateUserPage.generateUser();
|
const user = adminCreateUserPage.generateUser();
|
||||||
@@ -30,7 +38,7 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const queryRoleIdResult = await pgPool.query(queryRole);
|
const queryRoleIdResult = await client.query(queryRole);
|
||||||
expect(queryRoleIdResult.rowCount).toEqual(1);
|
expect(queryRoleIdResult.rowCount).toEqual(1);
|
||||||
|
|
||||||
const insertUser = {
|
const insertUser = {
|
||||||
@@ -38,7 +46,7 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
values: [user.email, user.fullName, queryRoleIdResult.rows[0].id, 'invited', token, expiredTokenDate],
|
values: [user.email, user.fullName, queryRoleIdResult.rows[0].id, 'invited', token, expiredTokenDate],
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertUserResult = await pgPool.query(insertUser);
|
const insertUserResult = await client.query(insertUser);
|
||||||
expect(insertUserResult.rowCount).toBe(1);
|
expect(insertUserResult.rowCount).toBe(1);
|
||||||
expect(insertUserResult.command).toBe('INSERT');
|
expect(insertUserResult.command).toBe('INSERT');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -60,7 +68,7 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const queryRoleIdResult = await pgPool.query(queryRole);
|
const queryRoleIdResult = await client.query(queryRole);
|
||||||
expect(queryRoleIdResult.rowCount).toEqual(1);
|
expect(queryRoleIdResult.rowCount).toEqual(1);
|
||||||
|
|
||||||
const insertUser = {
|
const insertUser = {
|
||||||
@@ -68,7 +76,7 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
values: [user.email, user.fullName, dateNow, queryRoleIdResult.rows[0].id, 'invited', token, dateNow],
|
values: [user.email, user.fullName, dateNow, queryRoleIdResult.rows[0].id, 'invited', token, dateNow],
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertUserResult = await pgPool.query(insertUser);
|
const insertUserResult = await client.query(insertUser);
|
||||||
expect(insertUserResult.rowCount).toBe(1);
|
expect(insertUserResult.rowCount).toBe(1);
|
||||||
expect(insertUserResult.command).toBe('INSERT');
|
expect(insertUserResult.command).toBe('INSERT');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@@ -27,12 +27,11 @@ import useLazyApps from 'hooks/useLazyApps';
|
|||||||
|
|
||||||
function createConnectionOrFlow(appKey, supportsConnections = false) {
|
function createConnectionOrFlow(appKey, supportsConnections = false) {
|
||||||
if (!supportsConnections) {
|
if (!supportsConnections) {
|
||||||
return URLS.CREATE_FLOW;
|
return URLS.CREATE_FLOW_WITH_APP(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return URLS.APP_ADD_CONNECTION(appKey);
|
return URLS.APP_ADD_CONNECTION(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddNewAppConnection(props) {
|
function AddNewAppConnection(props) {
|
||||||
const { onClose } = props;
|
const { onClose } = props;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@@ -49,7 +49,6 @@ function AdminApplicationAuthClientDialog(props) {
|
|||||||
) : (
|
) : (
|
||||||
<DialogContentText tabIndex={-1} component="div">
|
<DialogContentText tabIndex={-1} component="div">
|
||||||
<Form
|
<Form
|
||||||
data-test="auth-client-form"
|
|
||||||
onSubmit={submitHandler}
|
onSubmit={submitHandler}
|
||||||
defaultValues={defaultValues}
|
defaultValues={defaultValues}
|
||||||
render={({ formState: { isDirty } }) => (
|
render={({ formState: { isDirty } }) => (
|
||||||
@@ -68,7 +67,6 @@ function AdminApplicationAuthClientDialog(props) {
|
|||||||
<InputCreator key={field.key} schema={field} />
|
<InputCreator key={field.key} schema={field} />
|
||||||
))}
|
))}
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
data-test="submit-auth-client-form"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@@ -43,7 +43,7 @@ function AdminApplicationAuthClients(props) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{sortedAuthClients.map((client) => (
|
{sortedAuthClients.map((client) => (
|
||||||
<Card sx={{ mb: 1 }} key={client.id} data-test="auth-client">
|
<Card sx={{ mb: 1 }} key={client.id}>
|
||||||
<CardActionArea
|
<CardActionArea
|
||||||
component={Link}
|
component={Link}
|
||||||
to={URLS.ADMIN_APP_AUTH_CLIENT(appKey, client.id)}
|
to={URLS.ADMIN_APP_AUTH_CLIENT(appKey, client.id)}
|
||||||
@@ -70,7 +70,7 @@ function AdminApplicationAuthClients(props) {
|
|||||||
))}
|
))}
|
||||||
<Stack justifyContent="flex-end" direction="row">
|
<Stack justifyContent="flex-end" direction="row">
|
||||||
<Link to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}>
|
<Link to={URLS.ADMIN_APP_AUTH_CLIENTS_CREATE(appKey)}>
|
||||||
<Button variant="contained" sx={{ mt: 2 }} component="div" data-test="create-auth-client-button">
|
<Button variant="contained" sx={{ mt: 2 }} component="div">
|
||||||
{formatMessage('createAuthClient.button')}
|
{formatMessage('createAuthClient.button')}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
@@ -87,7 +87,6 @@ function AdminApplicationSettings(props) {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
<Stack>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
data-test="submit-button"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@@ -43,7 +43,10 @@ function AppFlows(props) {
|
|||||||
text={formatMessage('app.noFlows')}
|
text={formatMessage('app.noFlows')}
|
||||||
data-test="flows-no-results"
|
data-test="flows-no-results"
|
||||||
{...(allowed && {
|
{...(allowed && {
|
||||||
to: URLS.CREATE_FLOW,
|
to: URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(
|
||||||
|
appKey,
|
||||||
|
connectionId
|
||||||
|
),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@@ -1,36 +1,31 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import ConfirmationDialog from 'components/ConfirmationDialog';
|
import ConfirmationDialog from 'components/ConfirmationDialog';
|
||||||
import apolloClient from 'graphql/client';
|
import apolloClient from 'graphql/client';
|
||||||
|
import { DELETE_CURRENT_USER } from 'graphql/mutations/delete-current-user.ee';
|
||||||
import useAuthentication from 'hooks/useAuthentication';
|
import useAuthentication from 'hooks/useAuthentication';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useCurrentUser from 'hooks/useCurrentUser';
|
import useCurrentUser from 'hooks/useCurrentUser';
|
||||||
import useDeleteCurrentUser from 'hooks/useDeleteCurrentUser';
|
|
||||||
|
|
||||||
function DeleteAccountDialog(props) {
|
function DeleteAccountDialog(props) {
|
||||||
|
const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER);
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { data } = useCurrentUser();
|
const { data } = useCurrentUser();
|
||||||
const currentUser = data?.data;
|
const currentUser = data?.data;
|
||||||
|
|
||||||
const { mutateAsync: deleteCurrentUser } = useDeleteCurrentUser(
|
|
||||||
currentUser.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const authentication = useAuthentication();
|
const authentication = useAuthentication();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleConfirm = React.useCallback(async () => {
|
const handleConfirm = React.useCallback(async () => {
|
||||||
await deleteCurrentUser();
|
await deleteCurrentUser();
|
||||||
|
|
||||||
authentication.removeToken();
|
authentication.removeToken();
|
||||||
|
|
||||||
await apolloClient.clearStore();
|
await apolloClient.clearStore();
|
||||||
|
|
||||||
navigate(URLS.LOGIN);
|
navigate(URLS.LOGIN);
|
||||||
}, [deleteCurrentUser, authentication, navigate]);
|
}, [deleteCurrentUser, currentUser]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
|
@@ -1,17 +1,18 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
import useCreateStep from 'hooks/useCreateStep';
|
import useCreateStep from 'hooks/useCreateStep';
|
||||||
import useUpdateStep from 'hooks/useUpdateStep';
|
import { UPDATE_STEP } from 'graphql/mutations/update-step';
|
||||||
import FlowStep from 'components/FlowStep';
|
import FlowStep from 'components/FlowStep';
|
||||||
import { FlowPropType } from 'propTypes/propTypes';
|
import { FlowPropType } from 'propTypes/propTypes';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
function Editor(props) {
|
function Editor(props) {
|
||||||
|
const [updateStep] = useMutation(UPDATE_STEP);
|
||||||
const { flow } = props;
|
const { flow } = props;
|
||||||
const { mutateAsync: updateStep } = useUpdateStep();
|
|
||||||
const { mutateAsync: createStep, isPending: isCreateStepPending } =
|
const { mutateAsync: createStep, isPending: isCreateStepPending } =
|
||||||
useCreateStep(flow?.id);
|
useCreateStep(flow?.id);
|
||||||
const [triggerStep] = flow.steps;
|
const [triggerStep] = flow.steps;
|
||||||
@@ -20,24 +21,29 @@ function Editor(props) {
|
|||||||
|
|
||||||
const onStepChange = React.useCallback(
|
const onStepChange = React.useCallback(
|
||||||
async (step) => {
|
async (step) => {
|
||||||
const payload = {
|
const mutationInput = {
|
||||||
id: step.id,
|
id: step.id,
|
||||||
key: step.key,
|
key: step.key,
|
||||||
parameters: step.parameters,
|
parameters: step.parameters,
|
||||||
connectionId: step.connection?.id,
|
connection: {
|
||||||
|
id: step.connection?.id,
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (step.appKey) {
|
if (step.appKey) {
|
||||||
payload.appKey = step.appKey;
|
mutationInput.appKey = step.appKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateStep(payload);
|
await updateStep({ variables: { input: mutationInput } });
|
||||||
|
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ['steps', step.id, 'connection'],
|
queryKey: ['steps', step.id, 'connection'],
|
||||||
});
|
});
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['flows', flow.id] });
|
||||||
},
|
},
|
||||||
[updateStep, queryClient],
|
[updateStep, flow.id, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
const addStep = React.useCallback(
|
const addStep = React.useCallback(
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link, useParams } from 'react-router-dom';
|
import { Link, useParams } from 'react-router-dom';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
@@ -15,11 +16,12 @@ import Container from 'components/Container';
|
|||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import Can from 'components/Can';
|
import Can from 'components/Can';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import { UPDATE_FLOW_STATUS } from 'graphql/mutations/update-flow-status';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import { TopBar } from './style';
|
import { TopBar } from './style';
|
||||||
import useFlow from 'hooks/useFlow';
|
import useFlow from 'hooks/useFlow';
|
||||||
import useUpdateFlow from 'hooks/useUpdateFlow';
|
import useUpdateFlow from 'hooks/useUpdateFlow';
|
||||||
import useUpdateFlowStatus from 'hooks/useUpdateFlowStatus';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import EditorNew from 'components/EditorNew/EditorNew';
|
import EditorNew from 'components/EditorNew/EditorNew';
|
||||||
|
|
||||||
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
|
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
|
||||||
@@ -28,9 +30,10 @@ export default function EditorLayout() {
|
|||||||
const { flowId } = useParams();
|
const { flowId } = useParams();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { mutateAsync: updateFlow } = useUpdateFlow(flowId);
|
const { mutateAsync: updateFlow } = useUpdateFlow(flowId);
|
||||||
const { mutateAsync: updateFlowStatus } = useUpdateFlowStatus(flowId);
|
const [updateFlowStatus] = useMutation(UPDATE_FLOW_STATUS);
|
||||||
const { data, isLoading: isFlowLoading } = useFlow(flowId);
|
const { data, isLoading: isFlowLoading } = useFlow(flowId);
|
||||||
const flow = data?.data;
|
const flow = data?.data;
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const onFlowNameUpdate = async (name) => {
|
const onFlowNameUpdate = async (name) => {
|
||||||
await updateFlow({
|
await updateFlow({
|
||||||
@@ -38,6 +41,24 @@ export default function EditorLayout() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFlowStatusUpdate = React.useCallback(
|
||||||
|
async (active) => {
|
||||||
|
try {
|
||||||
|
await updateFlowStatus({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id: flowId,
|
||||||
|
active,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
||||||
|
} catch (err) {}
|
||||||
|
},
|
||||||
|
[flowId, queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar
|
<TopBar
|
||||||
@@ -86,7 +107,7 @@ export default function EditorLayout() {
|
|||||||
disabled={!allowed || !flow}
|
disabled={!allowed || !flow}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => updateFlowStatus(!flow.active)}
|
onClick={() => onFlowStatusUpdate(!flow.active)}
|
||||||
data-test={
|
data-test={
|
||||||
flow?.active ? 'unpublish-flow-button' : 'publish-flow-button'
|
flow?.active ? 'unpublish-flow-button' : 'publish-flow-button'
|
||||||
}
|
}
|
||||||
@@ -132,7 +153,7 @@ export default function EditorLayout() {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => updateFlowStatus(!flow.active)}
|
onClick={() => onFlowStatusUpdate(!flow.active)}
|
||||||
data-test="unpublish-flow-from-snackbar"
|
data-test="unpublish-flow-from-snackbar"
|
||||||
>
|
>
|
||||||
{formatMessage('flowEditor.unpublish')}
|
{formatMessage('flowEditor.unpublish')}
|
||||||
|
@@ -5,8 +5,8 @@ import { FlowPropType } from 'propTypes/propTypes';
|
|||||||
import ReactFlow, { useNodesState, useEdgesState } from 'reactflow';
|
import ReactFlow, { useNodesState, useEdgesState } from 'reactflow';
|
||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
|
|
||||||
|
import { UPDATE_STEP } from 'graphql/mutations/update-step';
|
||||||
import useCreateStep from 'hooks/useCreateStep';
|
import useCreateStep from 'hooks/useCreateStep';
|
||||||
import useUpdateStep from 'hooks/useUpdateStep';
|
|
||||||
|
|
||||||
import { useAutoLayout } from './useAutoLayout';
|
import { useAutoLayout } from './useAutoLayout';
|
||||||
import { useScrollBoundaries } from './useScrollBoundaries';
|
import { useScrollBoundaries } from './useScrollBoundaries';
|
||||||
@@ -35,7 +35,7 @@ const edgeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const EditorNew = ({ flow }) => {
|
const EditorNew = ({ flow }) => {
|
||||||
const { mutateAsync: updateStep } = useUpdateStep();
|
const [updateStep] = useMutation(UPDATE_STEP);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { mutateAsync: createStep, isPending: isCreateStepPending } =
|
const { mutateAsync: createStep, isPending: isCreateStepPending } =
|
||||||
@@ -84,24 +84,31 @@ const EditorNew = ({ flow }) => {
|
|||||||
|
|
||||||
const onStepChange = useCallback(
|
const onStepChange = useCallback(
|
||||||
async (step) => {
|
async (step) => {
|
||||||
const payload = {
|
const mutationInput = {
|
||||||
id: step.id,
|
id: step.id,
|
||||||
key: step.key,
|
key: step.key,
|
||||||
parameters: step.parameters,
|
parameters: step.parameters,
|
||||||
connectionId: step.connection?.id,
|
connection: {
|
||||||
|
id: step.connection?.id,
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (step.appKey) {
|
if (step.appKey) {
|
||||||
payload.appKey = step.appKey;
|
mutationInput.appKey = step.appKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateStep(payload);
|
await updateStep({
|
||||||
|
variables: { input: mutationInput },
|
||||||
|
});
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ['steps', step.id, 'connection'],
|
queryKey: ['steps', step.id, 'connection'],
|
||||||
});
|
});
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['flows', flow.id] });
|
||||||
},
|
},
|
||||||
[updateStep, queryClient],
|
[flow.id, updateStep, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onAddStep = useCallback(
|
const onAddStep = useCallback(
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import Menu from '@mui/material/Menu';
|
import Menu from '@mui/material/Menu';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
@@ -9,9 +10,9 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import Can from 'components/Can';
|
import Can from 'components/Can';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
import { DELETE_FLOW } from 'graphql/mutations/delete-flow';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useDuplicateFlow from 'hooks/useDuplicateFlow';
|
import useDuplicateFlow from 'hooks/useDuplicateFlow';
|
||||||
import useDeleteFlow from 'hooks/useDeleteFlow';
|
|
||||||
|
|
||||||
function ContextMenu(props) {
|
function ContextMenu(props) {
|
||||||
const { flowId, onClose, anchorEl, onDuplicateFlow, onDeleteFlow, appKey } =
|
const { flowId, onClose, anchorEl, onDuplicateFlow, onDeleteFlow, appKey } =
|
||||||
@@ -20,7 +21,7 @@ function ContextMenu(props) {
|
|||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { mutateAsync: duplicateFlow } = useDuplicateFlow(flowId);
|
const { mutateAsync: duplicateFlow } = useDuplicateFlow(flowId);
|
||||||
const { mutateAsync: deleteFlow } = useDeleteFlow();
|
const [deleteFlow] = useMutation(DELETE_FLOW);
|
||||||
|
|
||||||
const onFlowDuplicate = React.useCallback(async () => {
|
const onFlowDuplicate = React.useCallback(async () => {
|
||||||
await duplicateFlow();
|
await duplicateFlow();
|
||||||
@@ -51,7 +52,18 @@ function ContextMenu(props) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const onFlowDelete = React.useCallback(async () => {
|
const onFlowDelete = React.useCallback(async () => {
|
||||||
await deleteFlow(flowId);
|
await deleteFlow({
|
||||||
|
variables: { input: { id: flowId } },
|
||||||
|
update: (cache) => {
|
||||||
|
const flowCacheId = cache.identify({
|
||||||
|
__typename: 'Flow',
|
||||||
|
id: flowId,
|
||||||
|
});
|
||||||
|
cache.evict({
|
||||||
|
id: flowCacheId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (appKey) {
|
if (appKey) {
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
|
||||||
import Menu from '@mui/material/Menu';
|
import Menu from '@mui/material/Menu';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
|
||||||
import useDeleteStep from 'hooks/useDeleteStep';
|
import { DELETE_STEP } from 'graphql/mutations/delete-step';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
@@ -12,17 +13,15 @@ function FlowStepContextMenu(props) {
|
|||||||
const { stepId, onClose, anchorEl, deletable, flowId } = props;
|
const { stepId, onClose, anchorEl, deletable, flowId } = props;
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { mutateAsync: deleteStep } = useDeleteStep();
|
const [deleteStep] = useMutation(DELETE_STEP);
|
||||||
|
|
||||||
const deleteActionHandler = React.useCallback(
|
const deleteActionHandler = React.useCallback(
|
||||||
async (event) => {
|
async (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
await deleteStep({ variables: { input: { id: stepId } } });
|
||||||
await deleteStep(stepId);
|
|
||||||
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
||||||
},
|
},
|
||||||
[deleteStep, stepId, queryClient, flowId],
|
[stepId, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -21,7 +21,7 @@ function NoResultFound(props) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card elevation={0} data-test="no-results">
|
<Card elevation={0}>
|
||||||
<CardActionArea component={ActionAreaLink} {...props}>
|
<CardActionArea component={ActionAreaLink} {...props}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!!to && <AddCircleIcon color="primary" />}
|
{!!to && <AddCircleIcon color="primary" />}
|
||||||
|
@@ -42,7 +42,6 @@ function Switch(props) {
|
|||||||
{...FormControlLabelProps}
|
{...FormControlLabelProps}
|
||||||
control={
|
control={
|
||||||
<MuiSwitch
|
<MuiSwitch
|
||||||
data-test="switch"
|
|
||||||
{...switchProps}
|
{...switchProps}
|
||||||
{...field}
|
{...field}
|
||||||
checked={value}
|
checked={value}
|
||||||
|
@@ -41,6 +41,19 @@ export const APP_FLOWS_FOR_CONNECTION = (appKey, connectionId) =>
|
|||||||
export const APP_FLOWS_PATTERN = '/app/:appKey/flows';
|
export const APP_FLOWS_PATTERN = '/app/:appKey/flows';
|
||||||
export const EDITOR = '/editor';
|
export const EDITOR = '/editor';
|
||||||
export const CREATE_FLOW = '/editor/create';
|
export const CREATE_FLOW = '/editor/create';
|
||||||
|
export const CREATE_FLOW_WITH_APP = (appKey) =>
|
||||||
|
`/editor/create?appKey=${appKey}`;
|
||||||
|
export const CREATE_FLOW_WITH_APP_AND_CONNECTION = (appKey, connectionId) => {
|
||||||
|
const params = {};
|
||||||
|
if (appKey) {
|
||||||
|
params.appKey = appKey;
|
||||||
|
}
|
||||||
|
if (connectionId) {
|
||||||
|
params.connectionId = connectionId;
|
||||||
|
}
|
||||||
|
const searchParams = new URLSearchParams(params).toString();
|
||||||
|
return `/editor/create?${searchParams}`;
|
||||||
|
};
|
||||||
export const FLOW_EDITOR = (flowId) => `/editor/${flowId}`;
|
export const FLOW_EDITOR = (flowId) => `/editor/${flowId}`;
|
||||||
export const FLOWS = '/flows';
|
export const FLOWS = '/flows';
|
||||||
// TODO: revert this back to /flows/:flowId once we have a proper single flow page
|
// TODO: revert this back to /flows/:flowId once we have a proper single flow page
|
||||||
@@ -87,4 +100,5 @@ export const DASHBOARD = FLOWS;
|
|||||||
|
|
||||||
// External links and paths
|
// External links and paths
|
||||||
// The paths are sensitive for their relativity.
|
// The paths are sensitive for their relativity.
|
||||||
export const WEBHOOK_DOCS_PATH = './apps/webhooks/connection';
|
export const WEBHOOK_DOCS_PATH =
|
||||||
|
'./apps/webhooks/connection';
|
||||||
|
9
packages/web/src/graphql/mutations/create-flow.js
Normal file
9
packages/web/src/graphql/mutations/create-flow.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const CREATE_FLOW = gql`
|
||||||
|
mutation CreateFlow($input: CreateFlowInput) {
|
||||||
|
createFlow(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
16
packages/web/src/graphql/mutations/create-user.ee.js
Normal file
16
packages/web/src/graphql/mutations/create-user.ee.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const CREATE_USER = gql`
|
||||||
|
mutation CreateUser($input: CreateUserInput) {
|
||||||
|
createUser(input: $input) {
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
fullName
|
||||||
|
role {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acceptInvitationUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -0,0 +1,6 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const DELETE_CURRENT_USER = gql`
|
||||||
|
mutation DeleteCurrentUser {
|
||||||
|
deleteCurrentUser
|
||||||
|
}
|
||||||
|
`;
|
6
packages/web/src/graphql/mutations/delete-flow.js
Normal file
6
packages/web/src/graphql/mutations/delete-flow.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const DELETE_FLOW = gql`
|
||||||
|
mutation DeleteFlow($input: DeleteFlowInput) {
|
||||||
|
deleteFlow(input: $input)
|
||||||
|
}
|
||||||
|
`;
|
14
packages/web/src/graphql/mutations/delete-step.js
Normal file
14
packages/web/src/graphql/mutations/delete-step.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const DELETE_STEP = gql`
|
||||||
|
mutation DeleteStep($input: DeleteStepInput) {
|
||||||
|
deleteStep(input: $input) {
|
||||||
|
id
|
||||||
|
flow {
|
||||||
|
id
|
||||||
|
steps {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
9
packages/web/src/graphql/mutations/update-flow-status.js
Normal file
9
packages/web/src/graphql/mutations/update-flow-status.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const UPDATE_FLOW_STATUS = gql`
|
||||||
|
mutation UpdateFlowStatus($input: UpdateFlowStatusInput) {
|
||||||
|
updateFlowStatus(input: $input) {
|
||||||
|
id
|
||||||
|
active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
17
packages/web/src/graphql/mutations/update-step.js
Normal file
17
packages/web/src/graphql/mutations/update-step.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const UPDATE_STEP = gql`
|
||||||
|
mutation UpdateStep($input: UpdateStepInput) {
|
||||||
|
updateStep(input: $input) {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
key
|
||||||
|
appKey
|
||||||
|
parameters
|
||||||
|
status
|
||||||
|
webhookUrl
|
||||||
|
connection {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
10
packages/web/src/graphql/mutations/update-user.ee.js
Normal file
10
packages/web/src/graphql/mutations/update-user.ee.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
export const UPDATE_USER = gql`
|
||||||
|
mutation UpdateUser($input: UpdateUserInput) {
|
||||||
|
updateUser(input: $input) {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
fullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -12,7 +12,7 @@ export default function useAdminCreateRole() {
|
|||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ['admin', 'roles'],
|
queryKey: ['apps', 'roles'],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useAdminCreateUser() {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async (payload) => {
|
|
||||||
const { data } = await api.post('/v1/admin/users', payload);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['admin', 'users'],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,34 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import api from 'helpers/api';
|
|
||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
|
||||||
|
|
||||||
export default function useAdminUpdateUser(userId) {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
|
||||||
const formatMessage = useFormatMessage();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async (payload) => {
|
|
||||||
const { data } = await api.patch(`/v1/admin/users/${userId}`, payload);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['admin', 'users'],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
enqueueSnackbar(formatMessage('editUser.error'), {
|
|
||||||
variant: 'error',
|
|
||||||
persist: true,
|
|
||||||
SnackbarProps: {
|
|
||||||
'data-test': 'snackbar-error',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useCreateFlow() {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
const { data } = await api.post('/v1/flows');
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['flows'],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useDeleteCurrentUser(userId) {
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
const { data } = await api.delete(`/v1/users/${userId}`);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useDeleteFlow() {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async (flowId) => {
|
|
||||||
const { data } = await api.delete(`/v1/flows/${flowId}`);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['flows'],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useDeleteStep() {
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async (stepId) => {
|
|
||||||
const { data } = await api.delete(`/v1/steps/${stepId}`);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,39 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import api from 'helpers/api';
|
|
||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
|
||||||
|
|
||||||
export default function useUpdateFlowStatus(flowId) {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async (active) => {
|
|
||||||
const { data } = await api.patch(`/v1/flows/${flowId}/status`, {
|
|
||||||
active,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['flows', flowId],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
const errors = Object.values(
|
|
||||||
error.response.data.errors || [['Failed while updating flow status!']],
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const [error] of errors) {
|
|
||||||
enqueueSnackbar(error, {
|
|
||||||
variant: 'error',
|
|
||||||
SnackbarProps: {
|
|
||||||
'data-test': 'snackbar-error',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import api from 'helpers/api';
|
|
||||||
|
|
||||||
export default function useUpdateStep() {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const query = useMutation({
|
|
||||||
mutationFn: async ({ id, appKey, key, connectionId, parameters }) => {
|
|
||||||
const { data } = await api.patch(`/v1/steps/${id}`, {
|
|
||||||
appKey,
|
|
||||||
key,
|
|
||||||
connectionId,
|
|
||||||
parameters,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: ['flows'],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
@@ -223,12 +223,10 @@
|
|||||||
"createUser.submit": "Create",
|
"createUser.submit": "Create",
|
||||||
"createUser.successfullyCreated": "The user has been created.",
|
"createUser.successfullyCreated": "The user has been created.",
|
||||||
"createUser.invitationEmailInfo": "Invitation email will be sent if SMTP credentials are valid. Otherwise, you can share the invitation link manually: <link></link>",
|
"createUser.invitationEmailInfo": "Invitation email will be sent if SMTP credentials are valid. Otherwise, you can share the invitation link manually: <link></link>",
|
||||||
"createUser.error": "Error while creating the user.",
|
|
||||||
"editUserPage.title": "Edit user",
|
"editUserPage.title": "Edit user",
|
||||||
"editUser.status": "Status",
|
"editUser.status": "Status",
|
||||||
"editUser.submit": "Update",
|
"editUser.submit": "Update",
|
||||||
"editUser.successfullyUpdated": "The user has been updated.",
|
"editUser.successfullyUpdated": "The user has been updated.",
|
||||||
"editUser.error": "Error while updating the user.",
|
|
||||||
"userList.fullName": "Full name",
|
"userList.fullName": "Full name",
|
||||||
"userList.email": "Email",
|
"userList.email": "Email",
|
||||||
"userList.role": "Role",
|
"userList.role": "Role",
|
||||||
|
@@ -134,7 +134,10 @@ export default function Application() {
|
|||||||
color="primary"
|
color="primary"
|
||||||
size="large"
|
size="large"
|
||||||
component={Link}
|
component={Link}
|
||||||
to={URLS.CREATE_FLOW}
|
to={URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(
|
||||||
|
appKey,
|
||||||
|
connectionId,
|
||||||
|
)}
|
||||||
fullWidth
|
fullWidth
|
||||||
icon={<AddIcon />}
|
icon={<AddIcon />}
|
||||||
disabled={!allowed}
|
disabled={!allowed}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
@@ -13,9 +14,9 @@ import ControlledAutocomplete from 'components/ControlledAutocomplete';
|
|||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
|
import { CREATE_USER } from 'graphql/mutations/create-user.ee';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useRoles from 'hooks/useRoles.ee';
|
import useRoles from 'hooks/useRoles.ee';
|
||||||
import useAdminCreateUser from 'hooks/useAdminCreateUser';
|
|
||||||
|
|
||||||
function generateRoleOptions(roles) {
|
function generateRoleOptions(roles) {
|
||||||
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
return roles?.map(({ name: label, id: value }) => ({ label, value }));
|
||||||
@@ -23,11 +24,7 @@ function generateRoleOptions(roles) {
|
|||||||
|
|
||||||
export default function CreateUser() {
|
export default function CreateUser() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const {
|
const [createUser, { loading, data }] = useMutation(CREATE_USER);
|
||||||
mutateAsync: createUser,
|
|
||||||
isPending: isCreateUserPending,
|
|
||||||
data: createdUser,
|
|
||||||
} = useAdminCreateUser();
|
|
||||||
const { data: rolesData, loading: isRolesLoading } = useRoles();
|
const { data: rolesData, loading: isRolesLoading } = useRoles();
|
||||||
const roles = rolesData?.data;
|
const roles = rolesData?.data;
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
@@ -36,13 +33,17 @@ export default function CreateUser() {
|
|||||||
const handleUserCreation = async (userData) => {
|
const handleUserCreation = async (userData) => {
|
||||||
try {
|
try {
|
||||||
await createUser({
|
await createUser({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
fullName: userData.fullName,
|
fullName: userData.fullName,
|
||||||
email: userData.email,
|
email: userData.email,
|
||||||
roleId: userData.role?.id,
|
role: {
|
||||||
|
id: userData.role?.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] });
|
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] });
|
||||||
|
|
||||||
enqueueSnackbar(formatMessage('createUser.successfullyCreated'), {
|
enqueueSnackbar(formatMessage('createUser.successfullyCreated'), {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
persist: true,
|
persist: true,
|
||||||
@@ -51,14 +52,6 @@ export default function CreateUser() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
enqueueSnackbar(formatMessage('createUser.error'), {
|
|
||||||
variant: 'error',
|
|
||||||
persist: true,
|
|
||||||
SnackbarProps: {
|
|
||||||
'data-test': 'snackbar-error',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error('Failed while creating!');
|
throw new Error('Failed while creating!');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -114,13 +107,13 @@ export default function CreateUser() {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{ boxShadow: 2 }}
|
sx={{ boxShadow: 2 }}
|
||||||
loading={isCreateUserPending}
|
loading={loading}
|
||||||
data-test="create-button"
|
data-test="create-button"
|
||||||
>
|
>
|
||||||
{formatMessage('createUser.submit')}
|
{formatMessage('createUser.submit')}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
|
|
||||||
{createdUser && (
|
{data && (
|
||||||
<Alert
|
<Alert
|
||||||
severity="info"
|
severity="info"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -130,11 +123,11 @@ export default function CreateUser() {
|
|||||||
{formatMessage('createUser.invitationEmailInfo', {
|
{formatMessage('createUser.invitationEmailInfo', {
|
||||||
link: () => (
|
link: () => (
|
||||||
<a
|
<a
|
||||||
href={createdUser.data.acceptInvitationUrl}
|
href={data.createUser.acceptInvitationUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{createdUser.data.acceptInvitationUrl}
|
{data.createUser.acceptInvitationUrl}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
})}
|
})}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Skeleton from '@mui/material/Skeleton';
|
import Skeleton from '@mui/material/Skeleton';
|
||||||
@@ -8,6 +9,7 @@ import MuiTextField from '@mui/material/TextField';
|
|||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import Can from 'components/Can';
|
import Can from 'components/Can';
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
@@ -16,9 +18,9 @@ import Form from 'components/Form';
|
|||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
import { UPDATE_USER } from 'graphql/mutations/update-user.ee';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useRoles from 'hooks/useRoles.ee';
|
import useRoles from 'hooks/useRoles.ee';
|
||||||
import useAdminUpdateUser from 'hooks/useAdminUpdateUser';
|
|
||||||
import useAdminUser from 'hooks/useAdminUser';
|
import useAdminUser from 'hooks/useAdminUser';
|
||||||
|
|
||||||
function generateRoleOptions(roles) {
|
function generateRoleOptions(roles) {
|
||||||
@@ -27,23 +29,31 @@ function generateRoleOptions(roles) {
|
|||||||
|
|
||||||
export default function EditUser() {
|
export default function EditUser() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
const [updateUser, { loading }] = useMutation(UPDATE_USER);
|
||||||
const { userId } = useParams();
|
const { userId } = useParams();
|
||||||
const { mutateAsync: updateUser, isPending: isAdminUpdateUserPending } =
|
|
||||||
useAdminUpdateUser(userId);
|
|
||||||
const { data: userData, isLoading: isUserLoading } = useAdminUser({ userId });
|
const { data: userData, isLoading: isUserLoading } = useAdminUser({ userId });
|
||||||
const user = userData?.data;
|
const user = userData?.data;
|
||||||
const { data, isLoading: isRolesLoading } = useRoles();
|
const { data, isLoading: isRolesLoading } = useRoles();
|
||||||
const roles = data?.data;
|
const roles = data?.data;
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const handleUserUpdate = async (userDataToUpdate) => {
|
const handleUserUpdate = async (userDataToUpdate) => {
|
||||||
try {
|
try {
|
||||||
await updateUser({
|
await updateUser({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id: userId,
|
||||||
fullName: userDataToUpdate.fullName,
|
fullName: userDataToUpdate.fullName,
|
||||||
email: userDataToUpdate.email,
|
email: userDataToUpdate.email,
|
||||||
roleId: userDataToUpdate.role?.id,
|
role: {
|
||||||
|
id: userDataToUpdate.role?.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] });
|
||||||
|
|
||||||
enqueueSnackbar(formatMessage('editUser.successfullyUpdated'), {
|
enqueueSnackbar(formatMessage('editUser.successfullyUpdated'), {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
@@ -132,7 +142,7 @@ export default function EditUser() {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{ boxShadow: 2 }}
|
sx={{ boxShadow: 2 }}
|
||||||
loading={isAdminUpdateUserPending}
|
loading={loading}
|
||||||
data-test="update-button"
|
data-test="update-button"
|
||||||
>
|
>
|
||||||
{formatMessage('editUser.submit')}
|
{formatMessage('editUser.submit')}
|
||||||
|
@@ -1,30 +1,42 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useCreateFlow from 'hooks/useCreateFlow';
|
import { CREATE_FLOW } from 'graphql/mutations/create-flow';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
export default function CreateFlow() {
|
export default function CreateFlow() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { mutateAsync: createFlow, isError } = useCreateFlow();
|
const [createFlow, { error }] = useMutation(CREATE_FLOW);
|
||||||
|
const appKey = searchParams.get('appKey');
|
||||||
|
const connectionId = searchParams.get('connectionId');
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
async function initiate() {
|
async function initiate() {
|
||||||
const response = await createFlow();
|
const variables = {};
|
||||||
|
if (appKey) {
|
||||||
const flowId = response.data?.id;
|
variables.triggerAppKey = appKey;
|
||||||
|
}
|
||||||
|
if (connectionId) {
|
||||||
|
variables.connectionId = connectionId;
|
||||||
|
}
|
||||||
|
const response = await createFlow({
|
||||||
|
variables: {
|
||||||
|
input: variables,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const flowId = response.data?.createFlow?.id;
|
||||||
navigate(URLS.FLOW_EDITOR(flowId), { replace: true });
|
navigate(URLS.FLOW_EDITOR(flowId), { replace: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
initiate();
|
initiate();
|
||||||
}, [createFlow, navigate]);
|
}, [createFlow, navigate, appKey, connectionId]);
|
||||||
|
|
||||||
if (isError) {
|
if (error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
445
yarn.lock
445
yarn.lock
@@ -1629,130 +1629,130 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz"
|
||||||
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
||||||
|
|
||||||
"@esbuild/aix-ppc64@0.20.2":
|
"@esbuild/aix-ppc64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537"
|
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
|
||||||
integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==
|
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
|
||||||
|
|
||||||
"@esbuild/android-arm64@0.20.2":
|
"@esbuild/android-arm64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
|
||||||
integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==
|
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
|
||||||
|
|
||||||
"@esbuild/android-arm@0.15.11":
|
"@esbuild/android-arm@0.15.11":
|
||||||
version "0.15.11"
|
version "0.15.11"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.11.tgz#bdd9c3e098183bdca97075aa4c3e0152ed3e10ee"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.11.tgz#bdd9c3e098183bdca97075aa4c3e0152ed3e10ee"
|
||||||
integrity sha512-PzMcQLazLBkwDEkrNPi9AbjFt6+3I7HKbiYF2XtWQ7wItrHvEOeO3T8Am434zAozWtVP7lrTue1bEfc2nYWeCA==
|
integrity sha512-PzMcQLazLBkwDEkrNPi9AbjFt6+3I7HKbiYF2XtWQ7wItrHvEOeO3T8Am434zAozWtVP7lrTue1bEfc2nYWeCA==
|
||||||
|
|
||||||
"@esbuild/android-arm@0.20.2":
|
"@esbuild/android-arm@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
|
||||||
integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==
|
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
|
||||||
|
|
||||||
"@esbuild/android-x64@0.20.2":
|
"@esbuild/android-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
|
||||||
integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==
|
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
|
||||||
|
|
||||||
"@esbuild/darwin-arm64@0.20.2":
|
"@esbuild/darwin-arm64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb"
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
|
||||||
integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==
|
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
|
||||||
|
|
||||||
"@esbuild/darwin-x64@0.20.2":
|
"@esbuild/darwin-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0"
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
|
||||||
integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
|
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
|
||||||
|
|
||||||
"@esbuild/freebsd-arm64@0.20.2":
|
"@esbuild/freebsd-arm64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911"
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
|
||||||
integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==
|
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
|
||||||
|
|
||||||
"@esbuild/freebsd-x64@0.20.2":
|
"@esbuild/freebsd-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c"
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
|
||||||
integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==
|
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
|
||||||
|
|
||||||
"@esbuild/linux-arm64@0.20.2":
|
"@esbuild/linux-arm64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
|
||||||
integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==
|
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
|
||||||
|
|
||||||
"@esbuild/linux-arm@0.20.2":
|
"@esbuild/linux-arm@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
|
||||||
integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==
|
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
|
||||||
|
|
||||||
"@esbuild/linux-ia32@0.20.2":
|
"@esbuild/linux-ia32@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
|
||||||
integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==
|
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
|
||||||
|
|
||||||
"@esbuild/linux-loong64@0.15.11":
|
"@esbuild/linux-loong64@0.15.11":
|
||||||
version "0.15.11"
|
version "0.15.11"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.11.tgz#2f4f9a1083dcb4fc65233b6f59003c406abf32e5"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.11.tgz#2f4f9a1083dcb4fc65233b6f59003c406abf32e5"
|
||||||
integrity sha512-geWp637tUhNmhL3Xgy4Bj703yXB9dqiLJe05lCUfjSFDrQf9C/8pArusyPUbUbPwlC/EAUjBw32sxuIl/11dZw==
|
integrity sha512-geWp637tUhNmhL3Xgy4Bj703yXB9dqiLJe05lCUfjSFDrQf9C/8pArusyPUbUbPwlC/EAUjBw32sxuIl/11dZw==
|
||||||
|
|
||||||
"@esbuild/linux-loong64@0.20.2":
|
"@esbuild/linux-loong64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
|
||||||
integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==
|
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
|
||||||
|
|
||||||
"@esbuild/linux-mips64el@0.20.2":
|
"@esbuild/linux-mips64el@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
|
||||||
integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==
|
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
|
||||||
|
|
||||||
"@esbuild/linux-ppc64@0.20.2":
|
"@esbuild/linux-ppc64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
|
||||||
integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==
|
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
|
||||||
|
|
||||||
"@esbuild/linux-riscv64@0.20.2":
|
"@esbuild/linux-riscv64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
|
||||||
integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==
|
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
|
||||||
|
|
||||||
"@esbuild/linux-s390x@0.20.2":
|
"@esbuild/linux-s390x@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
|
||||||
integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==
|
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
|
||||||
|
|
||||||
"@esbuild/linux-x64@0.20.2":
|
"@esbuild/linux-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
|
||||||
integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==
|
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
|
||||||
|
|
||||||
"@esbuild/netbsd-x64@0.20.2":
|
"@esbuild/netbsd-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6"
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
|
||||||
integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==
|
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
|
||||||
|
|
||||||
"@esbuild/openbsd-x64@0.20.2":
|
"@esbuild/openbsd-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf"
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
|
||||||
integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==
|
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
|
||||||
|
|
||||||
"@esbuild/sunos-x64@0.20.2":
|
"@esbuild/sunos-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f"
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
|
||||||
integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==
|
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
|
||||||
|
|
||||||
"@esbuild/win32-arm64@0.20.2":
|
"@esbuild/win32-arm64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
|
||||||
integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==
|
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
|
||||||
|
|
||||||
"@esbuild/win32-ia32@0.20.2":
|
"@esbuild/win32-ia32@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
|
||||||
integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==
|
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
|
||||||
|
|
||||||
"@esbuild/win32-x64@0.20.2":
|
"@esbuild/win32-x64@0.21.5":
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
|
||||||
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
|
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.4.0":
|
"@eslint-community/eslint-utils@^4.4.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
@@ -3459,80 +3459,85 @@
|
|||||||
estree-walker "^1.0.1"
|
estree-walker "^1.0.1"
|
||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi@4.14.0":
|
"@rollup/rollup-android-arm-eabi@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz#57936f50d0335e2e7bfac496d209606fa516add4"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz#155c7d82c1b36c3ad84d9adf9b3cd520cba81a0f"
|
||||||
integrity sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==
|
integrity sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64@4.14.0":
|
"@rollup/rollup-android-arm64@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz#81bba83b37382a2d0e30ceced06c8d3d85138054"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz#b94b6fa002bd94a9cbd8f9e47e23b25e5bd113ba"
|
||||||
integrity sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==
|
integrity sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64@4.14.0":
|
"@rollup/rollup-darwin-arm64@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz#a371bd723a5c4c4a33376da72abfc3938066842b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz#0934126cf9cbeadfe0eb7471ab5d1517e8cd8dcc"
|
||||||
integrity sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==
|
integrity sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64@4.14.0":
|
"@rollup/rollup-darwin-x64@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz#8baf2fda277c9729125017c65651296282412886"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz#0ce8e1e0f349778938c7c90e4bdc730640e0a13e"
|
||||||
integrity sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==
|
integrity sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf@4.14.0":
|
"@rollup/rollup-linux-arm-gnueabihf@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz#822830a8f7388d5b81d04c69415408d3bab1079b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz#5669d34775ad5d71e4f29ade99d0ff4df523afb6"
|
||||||
integrity sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==
|
integrity sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu@4.14.0":
|
"@rollup/rollup-linux-arm-musleabihf@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz#e20fbe1bd4414c7119f9e0bba8ad17a6666c8365"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz#f6d1a0e1da4061370cb2f4244fbdd727c806dd88"
|
||||||
integrity sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==
|
integrity sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl@4.14.0":
|
"@rollup/rollup-linux-arm64-gnu@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz#13f475596a62e1924f13fe1c8cf2c40e09a99b47"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz#ed96a05e99743dee4d23cc4913fc6e01a0089c88"
|
||||||
integrity sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==
|
integrity sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==
|
||||||
|
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu@4.14.0":
|
"@rollup/rollup-linux-arm64-musl@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz#6a431c441420d1c510a205e08c6673355a0a2ea9"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz#057ea26eaa7e537a06ded617d23d57eab3cecb58"
|
||||||
integrity sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==
|
integrity sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu@4.14.0":
|
"@rollup/rollup-linux-powerpc64le-gnu@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz#53d9448962c3f9ed7a1672269655476ea2d67567"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz#6e6e1f9404c9bf3fbd7d51cd11cd288a9a2843aa"
|
||||||
integrity sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==
|
integrity sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu@4.14.0":
|
"@rollup/rollup-linux-riscv64-gnu@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz#95f0c133b324da3e7e5c7d12855e0eb71d21a946"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz#eef1536a53f6e6658a2a778130e6b1a4a41cb439"
|
||||||
integrity sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==
|
integrity sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu@4.14.0":
|
"@rollup/rollup-linux-s390x-gnu@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz#820ada75c68ead1acc486e41238ca0d8f8531478"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz#2b28fb89ca084efaf8086f435025d96b4a966957"
|
||||||
integrity sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==
|
integrity sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl@4.14.0":
|
"@rollup/rollup-linux-x64-gnu@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz#ca74f22e125efbe94c1148d989ef93329b464443"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz#5226cde6c6b495b04a3392c1d2c572844e42f06b"
|
||||||
integrity sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==
|
integrity sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc@4.14.0":
|
"@rollup/rollup-linux-x64-musl@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz#269023332297051d037a9593dcba92c10fef726b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz#2c2412982e6c2a00a2ecac6d548ebb02f0aa6ca4"
|
||||||
integrity sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==
|
integrity sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc@4.14.0":
|
"@rollup/rollup-win32-arm64-msvc@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz#d7701438daf964011fd7ca33e3f13f3ff5129e7b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz#fbb6ef5379199e2ec0103ef32877b0985c773a55"
|
||||||
integrity sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==
|
integrity sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc@4.14.0":
|
"@rollup/rollup-win32-ia32-msvc@4.21.3":
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz#0bb7ac3cd1c3292db1f39afdabfd03ccea3a3d34"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz#d50e2082e147e24d87fe34abbf6246525ec3845a"
|
||||||
integrity sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==
|
integrity sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.21.3":
|
||||||
|
version "4.21.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz#4115233aa1bd5a2060214f96d8511f6247093212"
|
||||||
|
integrity sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==
|
||||||
|
|
||||||
"@rudderstack/rudder-sdk-node@^1.1.2":
|
"@rudderstack/rudder-sdk-node@^1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
@@ -8035,34 +8040,34 @@ esbuild@^0.15.9:
|
|||||||
esbuild-windows-64 "0.15.11"
|
esbuild-windows-64 "0.15.11"
|
||||||
esbuild-windows-arm64 "0.15.11"
|
esbuild-windows-arm64 "0.15.11"
|
||||||
|
|
||||||
esbuild@^0.20.1:
|
esbuild@^0.21.3:
|
||||||
version "0.20.2"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
|
||||||
integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==
|
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@esbuild/aix-ppc64" "0.20.2"
|
"@esbuild/aix-ppc64" "0.21.5"
|
||||||
"@esbuild/android-arm" "0.20.2"
|
"@esbuild/android-arm" "0.21.5"
|
||||||
"@esbuild/android-arm64" "0.20.2"
|
"@esbuild/android-arm64" "0.21.5"
|
||||||
"@esbuild/android-x64" "0.20.2"
|
"@esbuild/android-x64" "0.21.5"
|
||||||
"@esbuild/darwin-arm64" "0.20.2"
|
"@esbuild/darwin-arm64" "0.21.5"
|
||||||
"@esbuild/darwin-x64" "0.20.2"
|
"@esbuild/darwin-x64" "0.21.5"
|
||||||
"@esbuild/freebsd-arm64" "0.20.2"
|
"@esbuild/freebsd-arm64" "0.21.5"
|
||||||
"@esbuild/freebsd-x64" "0.20.2"
|
"@esbuild/freebsd-x64" "0.21.5"
|
||||||
"@esbuild/linux-arm" "0.20.2"
|
"@esbuild/linux-arm" "0.21.5"
|
||||||
"@esbuild/linux-arm64" "0.20.2"
|
"@esbuild/linux-arm64" "0.21.5"
|
||||||
"@esbuild/linux-ia32" "0.20.2"
|
"@esbuild/linux-ia32" "0.21.5"
|
||||||
"@esbuild/linux-loong64" "0.20.2"
|
"@esbuild/linux-loong64" "0.21.5"
|
||||||
"@esbuild/linux-mips64el" "0.20.2"
|
"@esbuild/linux-mips64el" "0.21.5"
|
||||||
"@esbuild/linux-ppc64" "0.20.2"
|
"@esbuild/linux-ppc64" "0.21.5"
|
||||||
"@esbuild/linux-riscv64" "0.20.2"
|
"@esbuild/linux-riscv64" "0.21.5"
|
||||||
"@esbuild/linux-s390x" "0.20.2"
|
"@esbuild/linux-s390x" "0.21.5"
|
||||||
"@esbuild/linux-x64" "0.20.2"
|
"@esbuild/linux-x64" "0.21.5"
|
||||||
"@esbuild/netbsd-x64" "0.20.2"
|
"@esbuild/netbsd-x64" "0.21.5"
|
||||||
"@esbuild/openbsd-x64" "0.20.2"
|
"@esbuild/openbsd-x64" "0.21.5"
|
||||||
"@esbuild/sunos-x64" "0.20.2"
|
"@esbuild/sunos-x64" "0.21.5"
|
||||||
"@esbuild/win32-arm64" "0.20.2"
|
"@esbuild/win32-arm64" "0.21.5"
|
||||||
"@esbuild/win32-ia32" "0.20.2"
|
"@esbuild/win32-ia32" "0.21.5"
|
||||||
"@esbuild/win32-x64" "0.20.2"
|
"@esbuild/win32-x64" "0.21.5"
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
@@ -13073,6 +13078,11 @@ picocolors@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
|
||||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||||
|
|
||||||
|
picocolors@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
|
||||||
|
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
|
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||||
@@ -13690,14 +13700,14 @@ postcss@^8.4.18:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.4.38:
|
postcss@^8.4.43:
|
||||||
version "8.4.38"
|
version "8.4.47"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||||
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
|
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.3.7"
|
nanoid "^3.3.7"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.1.0"
|
||||||
source-map-js "^1.2.0"
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
postgres-array@~2.0.0:
|
postgres-array@~2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@@ -14763,28 +14773,29 @@ rollup@^2.79.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
rollup@^4.13.0:
|
rollup@^4.20.0:
|
||||||
version "4.14.0"
|
version "4.21.3"
|
||||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.14.0.tgz#c3e2cd479f1b2358b65c1f810fa05b51603d7be8"
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.3.tgz#c64ba119e6aeb913798a6f7eef2780a0df5a0821"
|
||||||
integrity sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==
|
integrity sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "1.0.5"
|
"@types/estree" "1.0.5"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@rollup/rollup-android-arm-eabi" "4.14.0"
|
"@rollup/rollup-android-arm-eabi" "4.21.3"
|
||||||
"@rollup/rollup-android-arm64" "4.14.0"
|
"@rollup/rollup-android-arm64" "4.21.3"
|
||||||
"@rollup/rollup-darwin-arm64" "4.14.0"
|
"@rollup/rollup-darwin-arm64" "4.21.3"
|
||||||
"@rollup/rollup-darwin-x64" "4.14.0"
|
"@rollup/rollup-darwin-x64" "4.21.3"
|
||||||
"@rollup/rollup-linux-arm-gnueabihf" "4.14.0"
|
"@rollup/rollup-linux-arm-gnueabihf" "4.21.3"
|
||||||
"@rollup/rollup-linux-arm64-gnu" "4.14.0"
|
"@rollup/rollup-linux-arm-musleabihf" "4.21.3"
|
||||||
"@rollup/rollup-linux-arm64-musl" "4.14.0"
|
"@rollup/rollup-linux-arm64-gnu" "4.21.3"
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.14.0"
|
"@rollup/rollup-linux-arm64-musl" "4.21.3"
|
||||||
"@rollup/rollup-linux-riscv64-gnu" "4.14.0"
|
"@rollup/rollup-linux-powerpc64le-gnu" "4.21.3"
|
||||||
"@rollup/rollup-linux-s390x-gnu" "4.14.0"
|
"@rollup/rollup-linux-riscv64-gnu" "4.21.3"
|
||||||
"@rollup/rollup-linux-x64-gnu" "4.14.0"
|
"@rollup/rollup-linux-s390x-gnu" "4.21.3"
|
||||||
"@rollup/rollup-linux-x64-musl" "4.14.0"
|
"@rollup/rollup-linux-x64-gnu" "4.21.3"
|
||||||
"@rollup/rollup-win32-arm64-msvc" "4.14.0"
|
"@rollup/rollup-linux-x64-musl" "4.21.3"
|
||||||
"@rollup/rollup-win32-ia32-msvc" "4.14.0"
|
"@rollup/rollup-win32-arm64-msvc" "4.21.3"
|
||||||
"@rollup/rollup-win32-x64-msvc" "4.14.0"
|
"@rollup/rollup-win32-ia32-msvc" "4.21.3"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.21.3"
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
run-async@^2.4.0:
|
run-async@^2.4.0:
|
||||||
@@ -15287,10 +15298,10 @@ source-map-js@^1.0.1, source-map-js@^1.0.2:
|
|||||||
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
|
||||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
|
|
||||||
source-map-js@^1.2.0:
|
source-map-js@^1.2.1:
|
||||||
version "1.2.0"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
source-map-loader@^3.0.0:
|
source-map-loader@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
@@ -16620,9 +16631,9 @@ vite-node@1.1.3:
|
|||||||
vite "^5.0.0"
|
vite "^5.0.0"
|
||||||
|
|
||||||
vite@^3.1.6:
|
vite@^3.1.6:
|
||||||
version "3.2.10"
|
version "3.2.11"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.10.tgz#7ac79fead82cfb6b5bf65613cd82fba6dcc81340"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.11.tgz#8d1c8e05ef2f24b04c8693f56d3e01fe8835e6d7"
|
||||||
integrity sha512-Dx3olBo/ODNiMVk/cA5Yft9Ws+snLOXrhLtrI3F4XLt4syz2Yg8fayZMWScPKoz12v5BUv7VEmQHnsfpY80fYw==
|
integrity sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.15.9"
|
esbuild "^0.15.9"
|
||||||
postcss "^8.4.18"
|
postcss "^8.4.18"
|
||||||
@@ -16632,13 +16643,13 @@ vite@^3.1.6:
|
|||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
vite@^5.0.0:
|
vite@^5.0.0:
|
||||||
version "5.2.8"
|
version "5.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.8.tgz#a99e09939f1a502992381395ce93efa40a2844aa"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.6.tgz#85a93a1228a7fb5a723ca1743e337a2588ed008f"
|
||||||
integrity sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==
|
integrity sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.20.1"
|
esbuild "^0.21.3"
|
||||||
postcss "^8.4.38"
|
postcss "^8.4.43"
|
||||||
rollup "^4.13.0"
|
rollup "^4.20.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.3"
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user