Merge remote-tracking branch 'upstream/main' into AUT-1115
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
import { createHmac } from 'node:crypto';
|
||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create HMAC',
|
||||||
|
key: 'createHmac',
|
||||||
|
description: 'Create a Hash-based Message Authentication Code (HMAC) using the specified algorithm, secret key, and message.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Algorithm',
|
||||||
|
key: 'algorithm',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
value: 'sha256',
|
||||||
|
description: 'Specifies the cryptographic hash function to use for HMAC generation.',
|
||||||
|
options: [
|
||||||
|
{ label: 'SHA-256', value: 'sha256' },
|
||||||
|
],
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Message',
|
||||||
|
key: 'message',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The input message to be hashed. This is the value that will be processed to generate the HMAC.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Secret Key',
|
||||||
|
key: 'secretKey',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The secret key used to create the HMAC.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Output Encoding',
|
||||||
|
key: 'outputEncoding',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
value: 'hex',
|
||||||
|
description: 'Specifies the encoding format for the HMAC digest output.',
|
||||||
|
options: [
|
||||||
|
{ label: 'base64', value: 'base64' },
|
||||||
|
{ label: 'base64url', value: 'base64url' },
|
||||||
|
{ label: 'hex', value: 'hex' },
|
||||||
|
],
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const hash = createHmac($.step.parameters.algorithm, $.step.parameters.secretKey)
|
||||||
|
.update($.step.parameters.message)
|
||||||
|
.digest($.step.parameters.outputEncoding);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: {
|
||||||
|
hash
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,65 @@
|
|||||||
|
import crypto from 'node:crypto';
|
||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create Signature',
|
||||||
|
key: 'createSignature',
|
||||||
|
description: 'Create a digital signature using the specified algorithm, secret key, and message.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Algorithm',
|
||||||
|
key: 'algorithm',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
value: 'RSA-SHA256',
|
||||||
|
description: 'Specifies the cryptographic hash function to use for HMAC generation.',
|
||||||
|
options: [
|
||||||
|
{ label: 'RSA-SHA256', value: 'RSA-SHA256' },
|
||||||
|
],
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Message',
|
||||||
|
key: 'message',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The input message to be signed.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Private Key',
|
||||||
|
key: 'privateKey',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The RSA private key in PEM format used for signing.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Output Encoding',
|
||||||
|
key: 'outputEncoding',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
value: 'hex',
|
||||||
|
description: 'Specifies the encoding format for the digital signature output. This determines how the generated signature will be represented as a string.',
|
||||||
|
options: [
|
||||||
|
{ label: 'base64', value: 'base64' },
|
||||||
|
{ label: 'base64url', value: 'base64url' },
|
||||||
|
{ label: 'hex', value: 'hex' },
|
||||||
|
],
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const signer = crypto.createSign($.step.parameters.algorithm);
|
||||||
|
signer.update($.step.parameters.message);
|
||||||
|
signer.end();
|
||||||
|
const signature = signer.sign($.step.parameters.privateKey, $.step.parameters.outputEncoding);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: {
|
||||||
|
signature
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
4
packages/backend/src/apps/cryptography/actions/index.js
Normal file
4
packages/backend/src/apps/cryptography/actions/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import createHmac from './create-hmac/index.js';
|
||||||
|
import createRsaSha256Signature from './create-rsa-sha256-signature/index.js';
|
||||||
|
|
||||||
|
export default [createHmac, createRsaSha256Signature];
|
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="100pt" height="100pt" version="1.1" viewBox="0 0 100 100">
|
||||||
|
<path d="m66.012 33h-3.0117v-11c0-7.1719-5.8281-13-13-13s-13 5.8281-13 13v11h-3.0117c-2.75 0-4.9883 2.2383-4.9883 4.9883v28.012c0 2.75 2.2383 4.9883 4.9883 4.9883h32.012c2.75 0 4.9883-2.2383 4.9883-4.9883v-28.012c0.011719-2.75-2.2266-4.9883-4.9766-4.9883zm-27.012-11c0-6.0703 4.9297-11 11-11s11 4.9297 11 11v11h-22zm30 44.012c0 1.6484-1.3398 2.9883-2.9883 2.9883h-32.023c-1.6484 0-2.9883-1.3398-2.9883-2.9883v-28.023c0-1.6484 1.3398-2.9883 2.9883-2.9883h32.023c1.6484 0 2.9883 1.3398 2.9883 2.9883zm-18 9.9883v14c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-14c0-0.55078 0.44922-1 1-1s1 0.44922 1 1zm20 8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v7h7c0.55078 0 1 0.44922 1 1zm-32-8v8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h7v-7c0-0.55078 0.44922-1 1-1s1 0.44922 1 1zm-14-26c0 0.55078-0.44922 1-1 1h-14c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h14c0.55078 0 1 0.44922 1 1zm0-12c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v7h7c0.55078 0 1 0.44922 1 1zm0 24c0 0.55078-0.44922 1-1 1h-7v7c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1h8c0.55078 0 1 0.44922 1 1zm66-12c0 0.55078-0.44922 1-1 1h-14c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h14c0.55078 0 1 0.44922 1 1zm-16-12c0-0.55078 0.44922-1 1-1h7v-7c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1zm10 24v8c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-7h-7c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h8c0.55078 0 1 0.44922 1 1zm-35-17c-2.7617 0-5 2.2383-5 5 0 2.4102 1.7188 4.4297 4 4.8984v5.1016c0 0.55078 0.44922 1 1 1s1-0.44922 1-1v-5.1016c2.2812-0.46094 4-2.4805 4-4.8984 0-2.7617-2.2383-5-5-5zm0 8c-1.6484 0-3-1.3516-3-3s1.3516-3 3-3 3 1.3516 3 3-1.3516 3-3 3z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
14
packages/backend/src/apps/cryptography/index.js
Normal file
14
packages/backend/src/apps/cryptography/index.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import defineApp from '../../helpers/define-app.js';
|
||||||
|
import actions from './actions/index.js';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Cryptography',
|
||||||
|
key: 'cryptography',
|
||||||
|
iconUrl: '{BASE_URL}/apps/cryptography/assets/favicon.svg',
|
||||||
|
authDocUrl: '{DOCS_URL}/apps/cryptography/connection',
|
||||||
|
supportsConnections: false,
|
||||||
|
baseUrl: '',
|
||||||
|
apiBaseUrl: '',
|
||||||
|
primaryColor: '001F52',
|
||||||
|
actions,
|
||||||
|
});
|
@@ -1,8 +1,10 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
import formatDateTime from './transformers/format-date-time.js';
|
import formatDateTime from './transformers/format-date-time.js';
|
||||||
|
import getCurrentTimestamp from './transformers/get-current-timestamp.js';
|
||||||
|
|
||||||
const transformers = {
|
const transformers = {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
|
getCurrentTimestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineAction({
|
export default defineAction({
|
||||||
@@ -16,7 +18,16 @@ export default defineAction({
|
|||||||
type: 'dropdown',
|
type: 'dropdown',
|
||||||
required: true,
|
required: true,
|
||||||
variables: true,
|
variables: true,
|
||||||
options: [{ label: 'Format Date / Time', value: 'formatDateTime' }],
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Get current timestamp',
|
||||||
|
value: 'getCurrentTimestamp',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Format Date / Time',
|
||||||
|
value: 'formatDateTime',
|
||||||
|
},
|
||||||
|
],
|
||||||
additionalFields: {
|
additionalFields: {
|
||||||
type: 'query',
|
type: 'query',
|
||||||
name: 'getDynamicFields',
|
name: 'getDynamicFields',
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
const getCurrentTimestamp = () => {
|
||||||
|
return Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentTimestamp;
|
@@ -15,6 +15,7 @@ import encodeUri from './transformers/encode-uri.js';
|
|||||||
import trimWhitespace from './transformers/trim-whitespace.js';
|
import trimWhitespace from './transformers/trim-whitespace.js';
|
||||||
import useDefaultValue from './transformers/use-default-value.js';
|
import useDefaultValue from './transformers/use-default-value.js';
|
||||||
import parseStringifiedJson from './transformers/parse-stringified-json.js';
|
import parseStringifiedJson from './transformers/parse-stringified-json.js';
|
||||||
|
import createUuid from './transformers/create-uuid.js';
|
||||||
|
|
||||||
const transformers = {
|
const transformers = {
|
||||||
base64ToString,
|
base64ToString,
|
||||||
@@ -32,6 +33,7 @@ const transformers = {
|
|||||||
trimWhitespace,
|
trimWhitespace,
|
||||||
useDefaultValue,
|
useDefaultValue,
|
||||||
parseStringifiedJson,
|
parseStringifiedJson,
|
||||||
|
createUuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineAction({
|
export default defineAction({
|
||||||
@@ -49,22 +51,23 @@ export default defineAction({
|
|||||||
options: [
|
options: [
|
||||||
{ label: 'Base64 to String', value: 'base64ToString' },
|
{ label: 'Base64 to String', value: 'base64ToString' },
|
||||||
{ label: 'Capitalize', value: 'capitalize' },
|
{ label: 'Capitalize', value: 'capitalize' },
|
||||||
|
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
||||||
|
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
||||||
|
{ label: 'Create UUID', value: 'createUuid' },
|
||||||
|
{ label: 'Encode URI', value: 'encodeUri' },
|
||||||
{
|
{
|
||||||
label: 'Encode URI Component',
|
label: 'Encode URI Component',
|
||||||
value: 'encodeUriComponent',
|
value: 'encodeUriComponent',
|
||||||
},
|
},
|
||||||
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
|
||||||
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
|
||||||
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
|
{ label: 'Extract Email Address', value: 'extractEmailAddress' },
|
||||||
{ label: 'Extract Number', value: 'extractNumber' },
|
{ label: 'Extract Number', value: 'extractNumber' },
|
||||||
{ label: 'Lowercase', value: 'lowercase' },
|
{ label: 'Lowercase', value: 'lowercase' },
|
||||||
|
{ label: 'Parse stringified JSON', value: 'parseStringifiedJson' },
|
||||||
{ label: 'Pluralize', value: 'pluralize' },
|
{ label: 'Pluralize', value: 'pluralize' },
|
||||||
{ label: 'Replace', value: 'replace' },
|
{ label: 'Replace', value: 'replace' },
|
||||||
{ label: 'String to Base64', value: 'stringToBase64' },
|
{ label: 'String to Base64', value: 'stringToBase64' },
|
||||||
{ label: 'Encode URI', value: 'encodeUri' },
|
|
||||||
{ label: 'Trim Whitespace', value: 'trimWhitespace' },
|
{ label: 'Trim Whitespace', value: 'trimWhitespace' },
|
||||||
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
||||||
{ label: 'Parse stringified JSON', value: 'parseStringifiedJson' },
|
|
||||||
],
|
],
|
||||||
additionalFields: {
|
additionalFields: {
|
||||||
type: 'query',
|
type: 'query',
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
const createUuidV4 = () => {
|
||||||
|
return uuidv4();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createUuidV4;
|
@@ -29,11 +29,6 @@ import upsertSamlAuthProvider from './mutations/upsert-saml-auth-provider.ee.js'
|
|||||||
import upsertSamlAuthProvidersRoleMappings from './mutations/upsert-saml-auth-providers-role-mappings.ee.js';
|
import upsertSamlAuthProvidersRoleMappings from './mutations/upsert-saml-auth-providers-role-mappings.ee.js';
|
||||||
import verifyConnection from './mutations/verify-connection.js';
|
import verifyConnection from './mutations/verify-connection.js';
|
||||||
|
|
||||||
// Converted mutations
|
|
||||||
import deleteUser from './mutations/delete-user.ee.js';
|
|
||||||
import login from './mutations/login.js';
|
|
||||||
import resetPassword from './mutations/reset-password.ee.js';
|
|
||||||
|
|
||||||
const mutationResolvers = {
|
const mutationResolvers = {
|
||||||
createAppAuthClient,
|
createAppAuthClient,
|
||||||
createAppConfig,
|
createAppConfig,
|
||||||
@@ -47,14 +42,11 @@ const mutationResolvers = {
|
|||||||
deleteFlow,
|
deleteFlow,
|
||||||
deleteRole,
|
deleteRole,
|
||||||
deleteStep,
|
deleteStep,
|
||||||
deleteUser,
|
|
||||||
duplicateFlow,
|
duplicateFlow,
|
||||||
executeFlow,
|
executeFlow,
|
||||||
generateAuthUrl,
|
generateAuthUrl,
|
||||||
login,
|
|
||||||
registerUser,
|
registerUser,
|
||||||
resetConnection,
|
resetConnection,
|
||||||
resetPassword,
|
|
||||||
updateAppAuthClient,
|
updateAppAuthClient,
|
||||||
updateAppConfig,
|
updateAppConfig,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
|
@@ -1,24 +0,0 @@
|
|||||||
import { Duration } from 'luxon';
|
|
||||||
import User from '../../models/user.js';
|
|
||||||
import deleteUserQueue from '../../queues/delete-user.ee.js';
|
|
||||||
|
|
||||||
const deleteUser = async (_parent, params, context) => {
|
|
||||||
context.currentUser.can('delete', 'User');
|
|
||||||
|
|
||||||
const id = params.input.id;
|
|
||||||
|
|
||||||
await User.query().deleteById(id);
|
|
||||||
|
|
||||||
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 deleteUser;
|
|
@@ -1,17 +0,0 @@
|
|||||||
import User from '../../models/user.js';
|
|
||||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id.js';
|
|
||||||
|
|
||||||
const login = async (_parent, params) => {
|
|
||||||
const user = await User.query().findOne({
|
|
||||||
email: params.input.email.toLowerCase(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (user && (await user.login(params.input.password))) {
|
|
||||||
const token = await createAuthTokenByUserId(user.id);
|
|
||||||
return { token, user };
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('User could not be found.');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default login;
|
|
@@ -1,23 +0,0 @@
|
|||||||
import User from '../../models/user.js';
|
|
||||||
|
|
||||||
const resetPassword = async (_parent, params) => {
|
|
||||||
const { token, password } = params.input;
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
throw new Error('Reset password token is required!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await User.query().findOne({ reset_password_token: token });
|
|
||||||
|
|
||||||
if (!user || !user.isResetPasswordTokenValid()) {
|
|
||||||
throw new Error(
|
|
||||||
'Reset password link is not valid or expired. Try generating a new link.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await user.resetPassword(password);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default resetPassword;
|
|
@@ -14,14 +14,11 @@ type Mutation {
|
|||||||
deleteFlow(input: DeleteFlowInput): Boolean
|
deleteFlow(input: DeleteFlowInput): Boolean
|
||||||
deleteRole(input: DeleteRoleInput): Boolean
|
deleteRole(input: DeleteRoleInput): Boolean
|
||||||
deleteStep(input: DeleteStepInput): Step
|
deleteStep(input: DeleteStepInput): Step
|
||||||
deleteUser(input: DeleteUserInput): Boolean
|
|
||||||
duplicateFlow(input: DuplicateFlowInput): Flow
|
duplicateFlow(input: DuplicateFlowInput): Flow
|
||||||
executeFlow(input: ExecuteFlowInput): executeFlowType
|
executeFlow(input: ExecuteFlowInput): executeFlowType
|
||||||
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
||||||
login(input: LoginInput): Auth
|
|
||||||
registerUser(input: RegisterUserInput): User
|
registerUser(input: RegisterUserInput): User
|
||||||
resetConnection(input: ResetConnectionInput): Connection
|
resetConnection(input: ResetConnectionInput): Connection
|
||||||
resetPassword(input: ResetPasswordInput): Boolean
|
|
||||||
updateAppAuthClient(input: UpdateAppAuthClientInput): AppAuthClient
|
updateAppAuthClient(input: UpdateAppAuthClientInput): AppAuthClient
|
||||||
updateAppConfig(input: UpdateAppConfigInput): AppConfig
|
updateAppConfig(input: UpdateAppConfigInput): AppConfig
|
||||||
updateConfig(input: JSONObject): JSONObject
|
updateConfig(input: JSONObject): JSONObject
|
||||||
@@ -153,11 +150,6 @@ enum ArgumentEnumType {
|
|||||||
string
|
string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth {
|
|
||||||
user: User
|
|
||||||
token: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthenticationStep {
|
type AuthenticationStep {
|
||||||
type: String
|
type: String
|
||||||
name: String
|
name: String
|
||||||
@@ -388,10 +380,6 @@ input UpdateUserInput {
|
|||||||
role: UserRoleInput
|
role: UserRoleInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteUserInput {
|
|
||||||
id: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input RegisterUserInput {
|
input RegisterUserInput {
|
||||||
fullName: String!
|
fullName: String!
|
||||||
email: String!
|
email: String!
|
||||||
@@ -404,16 +392,6 @@ input UpdateCurrentUserInput {
|
|||||||
fullName: String
|
fullName: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
|
||||||
token: String!
|
|
||||||
password: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input LoginInput {
|
|
||||||
email: String!
|
|
||||||
password: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input PermissionInput {
|
input PermissionInput {
|
||||||
action: String!
|
action: String!
|
||||||
subject: String!
|
subject: String!
|
||||||
|
@@ -53,9 +53,7 @@ const isAuthenticatedRule = rule()(isAuthenticated);
|
|||||||
export const authenticationRules = {
|
export const authenticationRules = {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
'*': isAuthenticatedRule,
|
'*': isAuthenticatedRule,
|
||||||
login: allow,
|
|
||||||
registerUser: allow,
|
registerUser: allow,
|
||||||
resetPassword: allow,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -59,6 +59,15 @@ export default defineConfig({
|
|||||||
{ text: 'Connection', link: '/apps/carbone/connection' },
|
{ text: 'Connection', link: '/apps/carbone/connection' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'Cryptography',
|
||||||
|
collapsible: true,
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Actions', link: '/apps/cryptography/actions' },
|
||||||
|
{ text: 'Connection', link: '/apps/cryptography/connection' },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Datastore',
|
text: 'Datastore',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
14
packages/docs/pages/apps/cryptography/actions.md
Normal file
14
packages/docs/pages/apps/cryptography/actions.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
favicon: /favicons/cryptography.svg
|
||||||
|
items:
|
||||||
|
- name: Create HMAC
|
||||||
|
desc: Create a Hash-based Message Authentication Code (HMAC) using the specified algorithm, secret key, and message.
|
||||||
|
- name: Create Signature
|
||||||
|
desc: Create a digital signature using the specified algorithm, secret key, and message.
|
||||||
|
---
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import CustomListing from '../../components/CustomListing.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CustomListing />
|
3
packages/docs/pages/apps/cryptography/connection.md
Normal file
3
packages/docs/pages/apps/cryptography/connection.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Cryptography
|
||||||
|
|
||||||
|
Cryptography is a built-in app shipped with Automatisch, allowing you to perform cryptographic operations without needing to connect to any external services.
|
3
packages/docs/pages/public/favicons/cryptography.svg
Normal file
3
packages/docs/pages/public/favicons/cryptography.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="100pt" height="100pt" version="1.1" viewBox="0 0 100 100">
|
||||||
|
<path d="m66.012 33h-3.0117v-11c0-7.1719-5.8281-13-13-13s-13 5.8281-13 13v11h-3.0117c-2.75 0-4.9883 2.2383-4.9883 4.9883v28.012c0 2.75 2.2383 4.9883 4.9883 4.9883h32.012c2.75 0 4.9883-2.2383 4.9883-4.9883v-28.012c0.011719-2.75-2.2266-4.9883-4.9766-4.9883zm-27.012-11c0-6.0703 4.9297-11 11-11s11 4.9297 11 11v11h-22zm30 44.012c0 1.6484-1.3398 2.9883-2.9883 2.9883h-32.023c-1.6484 0-2.9883-1.3398-2.9883-2.9883v-28.023c0-1.6484 1.3398-2.9883 2.9883-2.9883h32.023c1.6484 0 2.9883 1.3398 2.9883 2.9883zm-18 9.9883v14c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-14c0-0.55078 0.44922-1 1-1s1 0.44922 1 1zm20 8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v7h7c0.55078 0 1 0.44922 1 1zm-32-8v8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h7v-7c0-0.55078 0.44922-1 1-1s1 0.44922 1 1zm-14-26c0 0.55078-0.44922 1-1 1h-14c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h14c0.55078 0 1 0.44922 1 1zm0-12c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v7h7c0.55078 0 1 0.44922 1 1zm0 24c0 0.55078-0.44922 1-1 1h-7v7c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-8c0-0.55078 0.44922-1 1-1h8c0.55078 0 1 0.44922 1 1zm66-12c0 0.55078-0.44922 1-1 1h-14c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h14c0.55078 0 1 0.44922 1 1zm-16-12c0-0.55078 0.44922-1 1-1h7v-7c0-0.55078 0.44922-1 1-1s1 0.44922 1 1v8c0 0.55078-0.44922 1-1 1h-8c-0.55078 0-1-0.44922-1-1zm10 24v8c0 0.55078-0.44922 1-1 1s-1-0.44922-1-1v-7h-7c-0.55078 0-1-0.44922-1-1s0.44922-1 1-1h8c0.55078 0 1 0.44922 1 1zm-35-17c-2.7617 0-5 2.2383-5 5 0 2.4102 1.7188 4.4297 4 4.8984v5.1016c0 0.55078 0.44922 1 1 1s1-0.44922 1-1v-5.1016c2.2812-0.46094 4-2.4805 4-4.8984 0-2.7617-2.2383-5-5-5zm0 8c-1.6484 0-3-1.3516-3-3s1.3516-3 3-3 3 1.3516 3 3-1.3516 3-3 3z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
6
packages/e2e-tests/.eslintignore
Normal file
6
packages/e2e-tests/.eslintignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
.eslintrc.js
|
||||||
|
|
||||||
|
playwright-report/*
|
25
packages/e2e-tests/.eslintrc.json
Normal file
25
packages/e2e-tests/.eslintrc.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"semi": [
|
||||||
|
2,
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@@ -14,6 +14,6 @@ export class DeleteUserModal {
|
|||||||
async close () {
|
async close () {
|
||||||
await this.page.click('body', {
|
await this.page.click('body', {
|
||||||
position: { x: 10, y: 10 }
|
position: { x: 10, y: 10 }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
const { AdminCreateRolePage } = require('./create-role-page')
|
const { AdminCreateRolePage } = require('./create-role-page');
|
||||||
|
|
||||||
export class AdminEditRolePage extends AdminCreateRolePage {
|
export class AdminEditRolePage extends AdminCreateRolePage {
|
||||||
constructor (page) {
|
constructor (page) {
|
||||||
|
@@ -23,6 +23,7 @@ export class AdminEditUserPage extends AuthenticatedPage {
|
|||||||
*/
|
*/
|
||||||
async waitForLoad(fullName) {
|
async waitForLoad(fullName) {
|
||||||
return await this.page.waitForFunction((fullName) => {
|
return await this.page.waitForFunction((fullName) => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const el = document.querySelector("[data-test='full-name-input']");
|
const el = document.querySelector("[data-test='full-name-input']");
|
||||||
return el && el.value === fullName;
|
return el && el.value === fullName;
|
||||||
}, fullName);
|
}, fullName);
|
||||||
|
@@ -25,5 +25,5 @@ export const adminFixtures = {
|
|||||||
adminCreateRolePage: async ({ page}, use) => {
|
adminCreateRolePage: async ({ page}, use) => {
|
||||||
await use(new AdminCreateRolePage(page));
|
await use(new AdminCreateRolePage(page));
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
@@ -87,6 +87,7 @@ export class AdminUsersPage extends AuthenticatedPage {
|
|||||||
await this.firstPageButton.click();
|
await this.firstPageButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
if (await this.usersLoader.isVisible()) {
|
if (await this.usersLoader.isVisible()) {
|
||||||
await this.usersLoader.waitFor({
|
await this.usersLoader.waitFor({
|
||||||
@@ -108,6 +109,7 @@ export class AdminUsersPage extends AuthenticatedPage {
|
|||||||
|
|
||||||
async getTotalRows() {
|
async getTotalRows() {
|
||||||
return await this.page.evaluate(() => {
|
return await this.page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const node = document.querySelector('[data-total-count]');
|
const node = document.querySelector('[data-total-count]');
|
||||||
if (node) {
|
if (node) {
|
||||||
const count = Number(node.dataset.totalCount);
|
const count = Number(node.dataset.totalCount);
|
||||||
@@ -121,6 +123,7 @@ export class AdminUsersPage extends AuthenticatedPage {
|
|||||||
|
|
||||||
async getRowsPerPage() {
|
async getRowsPerPage() {
|
||||||
return await this.page.evaluate(() => {
|
return await this.page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const node = document.querySelector('[data-rows-per-page]');
|
const node = document.querySelector('[data-rows-per-page]');
|
||||||
if (node) {
|
if (node) {
|
||||||
const count = Number(node.dataset.rowsPerPage);
|
const count = Number(node.dataset.rowsPerPage);
|
||||||
|
@@ -25,7 +25,7 @@ export class ApplicationsModal extends BasePage {
|
|||||||
if (this.applications[link] === undefined) {
|
if (this.applications[link] === undefined) {
|
||||||
throw {
|
throw {
|
||||||
message: `Unknown link "${link}" passed to ApplicationsModal.selectLink`
|
message: `Unknown link "${link}" passed to ApplicationsModal.selectLink`
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
await this.searchInput.fill(link);
|
await this.searchInput.fill(link);
|
||||||
await this.appListItem.first().click();
|
await this.appListItem.first().click();
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
const { BasePage } = require('../../base-page');
|
const { BasePage } = require('../../base-page');
|
||||||
const { AddGithubConnectionModal } = require('./add-github-connection-modal');
|
const { AddGithubConnectionModal } = require('./add-github-connection-modal');
|
||||||
|
const { expect } = require('@playwright/test');
|
||||||
|
|
||||||
export class GithubPage extends BasePage {
|
export class GithubPage extends BasePage {
|
||||||
|
|
||||||
constructor (page) {
|
constructor (page) {
|
||||||
super(page)
|
super(page);
|
||||||
this.addConnectionButton = page.getByTestId('add-connection-button');
|
this.addConnectionButton = page.getByTestId('add-connection-button');
|
||||||
this.connectionsTab = page.getByTestId('connections-tab');
|
this.connectionsTab = page.getByTestId('connections-tab');
|
||||||
this.flowsTab = page.getByTestId('flows-tab');
|
this.flowsTab = page.getByTestId('flows-tab');
|
||||||
@@ -38,7 +39,7 @@ export class GithubPage extends BasePage {
|
|||||||
await this.flowsTab.click();
|
await this.flowsTab.click();
|
||||||
await expect(this.flowsTab).toBeVisible();
|
await expect(this.flowsTab).toBeVisible();
|
||||||
}
|
}
|
||||||
return await this.flowRows.count() > 0
|
return await this.flowRows.count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasConnections () {
|
async hasConnections () {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
const { BasePage } = require('../../base-page');
|
const { BasePage } = require('../../base-page');
|
||||||
|
const { expect } = require('@playwright/test');
|
||||||
|
|
||||||
export class GithubPopup extends BasePage {
|
export class GithubPopup extends BasePage {
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ export class GithubPopup extends BasePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPathname () {
|
getPathname () {
|
||||||
const url = this.page.url()
|
const url = this.page.url();
|
||||||
try {
|
try {
|
||||||
return new URL(url).pathname;
|
return new URL(url).pathname;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -34,17 +35,17 @@ export class GithubPopup extends BasePage {
|
|||||||
loginInput.click();
|
loginInput.click();
|
||||||
await loginInput.fill(process.env.GITHUB_USERNAME);
|
await loginInput.fill(process.env.GITHUB_USERNAME);
|
||||||
const passwordInput = this.page.getByLabel('Password');
|
const passwordInput = this.page.getByLabel('Password');
|
||||||
passwordInput.click()
|
passwordInput.click();
|
||||||
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
||||||
await this.page.getByRole('button', { name: 'Sign in' }).click();
|
await this.page.getByRole('button', { name: 'Sign in' }).click();
|
||||||
// await this.page.waitForTimeout(2000);
|
// await this.page.waitForTimeout(2000);
|
||||||
if (this.page.isClosed()) {
|
if (this.page.isClosed()) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
// await this.page.waitForLoadState('networkidle', 30000);
|
// await this.page.waitForLoadState('networkidle', 30000);
|
||||||
this.page.waitForEvent('load');
|
this.page.waitForEvent('load');
|
||||||
if (this.page.isClosed()) {
|
if (this.page.isClosed()) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
await this.page.waitForURL(function (url) {
|
await this.page.waitForURL(function (url) {
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
@@ -55,7 +56,7 @@ export class GithubPopup extends BasePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleAuthorize () {
|
async handleAuthorize () {
|
||||||
if (this.page.isClosed()) { return }
|
if (this.page.isClosed()) { return; }
|
||||||
const authorizeButton = this.page.getByRole(
|
const authorizeButton = this.page.getByRole(
|
||||||
'button',
|
'button',
|
||||||
{ name: 'Authorize' }
|
{ name: 'Authorize' }
|
||||||
@@ -69,7 +70,7 @@ export class GithubPopup extends BasePage {
|
|||||||
) && (
|
) && (
|
||||||
u.searchParams.get('client_id') === null
|
u.searchParams.get('client_id') === null
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
const passwordInput = this.page.getByLabel('Password');
|
const passwordInput = this.page.getByLabel('Password');
|
||||||
if (await passwordInput.isVisible()) {
|
if (await passwordInput.isVisible()) {
|
||||||
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
||||||
@@ -87,6 +88,6 @@ export class GithubPopup extends BasePage {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.page.waitForEvent('close')
|
await this.page.waitForEvent('close');
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,7 +1,4 @@
|
|||||||
const path = require('node:path');
|
|
||||||
const { expect } = require('@playwright/test');
|
|
||||||
const { BasePage } = require('./base-page');
|
const { BasePage } = require('./base-page');
|
||||||
const { LoginPage } = require('./login-page');
|
|
||||||
|
|
||||||
export class AuthenticatedPage extends BasePage {
|
export class AuthenticatedPage extends BasePage {
|
||||||
/**
|
/**
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
const path = require('node:path');
|
|
||||||
const { AuthenticatedPage } = require('./authenticated-page');
|
const { AuthenticatedPage } = require('./authenticated-page');
|
||||||
|
|
||||||
export class ConnectionsPage extends AuthenticatedPage {
|
export class ConnectionsPage extends AuthenticatedPage {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
const path = require('node:path');
|
|
||||||
const { AuthenticatedPage } = require('./authenticated-page');
|
const { AuthenticatedPage } = require('./authenticated-page');
|
||||||
|
|
||||||
export class ExecutionsPage extends AuthenticatedPage {
|
export class ExecutionsPage extends AuthenticatedPage {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
const path = require('node:path');
|
|
||||||
const { AuthenticatedPage } = require('./authenticated-page');
|
const { AuthenticatedPage } = require('./authenticated-page');
|
||||||
|
|
||||||
export class FlowEditorPage extends AuthenticatedPage {
|
export class FlowEditorPage extends AuthenticatedPage {
|
||||||
|
@@ -8,6 +8,7 @@ const { LoginPage } = require('./login-page');
|
|||||||
const { AcceptInvitation } = require('./accept-invitation-page');
|
const { AcceptInvitation } = require('./accept-invitation-page');
|
||||||
const { adminFixtures } = require('./admin');
|
const { adminFixtures } = require('./admin');
|
||||||
const { AdminSetupPage } = require('./admin-setup-page');
|
const { AdminSetupPage } = require('./admin-setup-page');
|
||||||
|
const { AdminCreateUserPage } = require('./admin/create-user-page');
|
||||||
|
|
||||||
exports.test = test.extend({
|
exports.test = test.extend({
|
||||||
page: async ({ page }, use) => {
|
page: async ({ page }, use) => {
|
||||||
@@ -58,6 +59,11 @@ exports.publicTest = test.extend({
|
|||||||
const adminSetupPage = new AdminSetupPage(page);
|
const adminSetupPage = new AdminSetupPage(page);
|
||||||
await use(adminSetupPage);
|
await use(adminSetupPage);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
adminCreateUserPage: async ({page}, use) => {
|
||||||
|
const adminCreateUserPage = new AdminCreateUserPage(page);
|
||||||
|
await use(adminCreateUserPage);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
const { Client } = require('pg');
|
const { Client } = require('pg');
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
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,
|
||||||
password: process.env.POSTGRES_PASSWORD,
|
password: process.env.POSTGRES_PASSWORD,
|
||||||
database: process.env.POSTGRES_DATABASE
|
database: process.env.POSTGRES_DATABASE
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.client = client;
|
exports.client = client;
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
const path = require('node:path');
|
|
||||||
const { AuthenticatedPage } = require('./authenticated-page');
|
const { AuthenticatedPage } = require('./authenticated-page');
|
||||||
|
|
||||||
export class UserInterfacePage extends AuthenticatedPage {
|
export class UserInterfacePage extends AuthenticatedPage {
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start-mock-license-server": "node ./license-server-with-mock.js",
|
"start-mock-license-server": "node ./license-server-with-mock.js",
|
||||||
"test": "playwright test",
|
"test": "playwright test",
|
||||||
"test:fast": "yarn test -j 90% --quiet --reporter null --ignore-snapshots -x"
|
"test:fast": "yarn test -j 90% --quiet --reporter null --ignore-snapshots -x",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
|
@@ -17,7 +17,6 @@ test.describe('Role management page', () => {
|
|||||||
adminCreateRolePage,
|
adminCreateRolePage,
|
||||||
adminEditRolePage,
|
adminEditRolePage,
|
||||||
adminRolesPage,
|
adminRolesPage,
|
||||||
page,
|
|
||||||
}) => {
|
}) => {
|
||||||
await test.step('Create a new role', async () => {
|
await test.step('Create a new role', async () => {
|
||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
@@ -126,12 +125,14 @@ test.describe('Role management page', () => {
|
|||||||
await adminCreateRolePage.isMounted();
|
await adminCreateRolePage.isMounted();
|
||||||
|
|
||||||
const initScrollTop = await page.evaluate(() => {
|
const initScrollTop = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
return document.documentElement.scrollTop;
|
return document.documentElement.scrollTop;
|
||||||
});
|
});
|
||||||
await page.mouse.move(400, 100);
|
await page.mouse.move(400, 100);
|
||||||
await page.mouse.click(400, 100);
|
await page.mouse.click(400, 100);
|
||||||
await page.mouse.wheel(200, 0);
|
await page.mouse.wheel(200, 0);
|
||||||
const updatedScrollTop = await page.evaluate(() => {
|
const updatedScrollTop = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
return document.documentElement.scrollTop;
|
return document.documentElement.scrollTop;
|
||||||
});
|
});
|
||||||
await expect(initScrollTop).not.toBe(updatedScrollTop);
|
await expect(initScrollTop).not.toBe(updatedScrollTop);
|
||||||
@@ -144,11 +145,13 @@ test.describe('Role management page', () => {
|
|||||||
await adminEditRolePage.isMounted();
|
await adminEditRolePage.isMounted();
|
||||||
|
|
||||||
const initScrollTop = await page.evaluate(() => {
|
const initScrollTop = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
return document.documentElement.scrollTop;
|
return document.documentElement.scrollTop;
|
||||||
});
|
});
|
||||||
await page.mouse.move(400, 100);
|
await page.mouse.move(400, 100);
|
||||||
await page.mouse.wheel(200, 0);
|
await page.mouse.wheel(200, 0);
|
||||||
const updatedScrollTop = await page.evaluate(() => {
|
const updatedScrollTop = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
return document.documentElement.scrollTop;
|
return document.documentElement.scrollTop;
|
||||||
});
|
});
|
||||||
await expect(initScrollTop).not.toBe(updatedScrollTop);
|
await expect(initScrollTop).not.toBe(updatedScrollTop);
|
||||||
@@ -165,7 +168,6 @@ test.describe('Role management page', () => {
|
|||||||
adminUsersPage,
|
adminUsersPage,
|
||||||
adminCreateUserPage,
|
adminCreateUserPage,
|
||||||
adminEditUserPage,
|
adminEditUserPage,
|
||||||
page,
|
|
||||||
}) => {
|
}) => {
|
||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
await test.step('Create a new role', async () => {
|
await test.step('Create a new role', async () => {
|
||||||
@@ -270,7 +272,6 @@ test.describe('Role management page', () => {
|
|||||||
adminRolesPage,
|
adminRolesPage,
|
||||||
adminUsersPage,
|
adminUsersPage,
|
||||||
adminCreateUserPage,
|
adminCreateUserPage,
|
||||||
page,
|
|
||||||
}) => {
|
}) => {
|
||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
await test.step('Create a new role', async () => {
|
await test.step('Create a new role', async () => {
|
||||||
@@ -429,6 +430,7 @@ test('Accessibility of role management page', async ({
|
|||||||
await page.goto(url);
|
await page.goto(url);
|
||||||
await page.waitForTimeout(750);
|
await page.waitForTimeout(750);
|
||||||
const isUnmounted = await page.evaluate(() => {
|
const isUnmounted = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const root = document.querySelector('#root');
|
const root = document.querySelector('#root');
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
|
@@ -98,7 +98,7 @@ test.describe('User management page', () => {
|
|||||||
await expect(userRow).not.toBeVisible(false);
|
await expect(userRow).not.toBeVisible(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Creating a user which has been deleted',
|
'Creating a user which has been deleted',
|
||||||
|
@@ -51,7 +51,7 @@ test(
|
|||||||
|
|
||||||
const subjects = ['Connection', 'Execution', 'Flow'];
|
const subjects = ['Connection', 'Execution', 'Flow'];
|
||||||
for (let subject of subjects) {
|
for (let subject of subjects) {
|
||||||
const row = adminCreateRolePage.getSubjectRow(subject)
|
const row = adminCreateRolePage.getSubjectRow(subject);
|
||||||
const modal = adminCreateRolePage.getRoleConditionsModal(subject);
|
const modal = adminCreateRolePage.getRoleConditionsModal(subject);
|
||||||
await adminCreateRolePage.clickPermissionSettings(row);
|
await adminCreateRolePage.clickPermissionSettings(row);
|
||||||
await expect(modal.modal).toBeVisible();
|
await expect(modal.modal).toBeVisible();
|
||||||
|
@@ -2,7 +2,7 @@ const { test, expect } = require('../../fixtures/index');
|
|||||||
|
|
||||||
// no execution data exists in an empty account
|
// no execution data exists in an empty account
|
||||||
test.describe.skip('Executions page', () => {
|
test.describe.skip('Executions page', () => {
|
||||||
test.beforeEach(async ({ page, executionsPage }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.getByTestId('executions-page-drawer-link').click();
|
await page.getByTestId('executions-page-drawer-link').click();
|
||||||
await page.getByTestId('execution-row').first().click();
|
await page.getByTestId('execution-row').first().click();
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ test('Ensure creating a new flow works', async ({ page }) => {
|
|||||||
await expect(page).toHaveURL(
|
await expect(page).toHaveURL(
|
||||||
/\/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}/
|
/\/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}/
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Create a new flow with a Scheduler step then an Ntfy step',
|
'Create a new flow with a Scheduler step then an Ntfy step',
|
||||||
|
@@ -1,17 +1,8 @@
|
|||||||
const { AdminCreateUserPage } = require('../../fixtures/admin/create-user-page');
|
|
||||||
const { publicTest, expect } = require('../../fixtures/index');
|
const { publicTest, expect } = require('../../fixtures/index');
|
||||||
const { client } = require('../../fixtures/postgres-client-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', () => {
|
||||||
publicTest.beforeAll(async () => {
|
|
||||||
await client.connect();
|
|
||||||
});
|
|
||||||
|
|
||||||
publicTest.afterAll(async () => {
|
|
||||||
await client.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
publicTest('should not be able to set the password if token is empty', async ({ acceptInvitationPage }) => {
|
publicTest('should not be able to set the password if token is empty', async ({ acceptInvitationPage }) => {
|
||||||
await acceptInvitationPage.open('');
|
await acceptInvitationPage.open('');
|
||||||
await acceptInvitationPage.excpectSubmitButtonToBeDisabled();
|
await acceptInvitationPage.excpectSubmitButtonToBeDisabled();
|
||||||
@@ -19,44 +10,83 @@ publicTest.describe('Accept invitation page', () => {
|
|||||||
await acceptInvitationPage.excpectSubmitButtonToBeDisabled();
|
await acceptInvitationPage.excpectSubmitButtonToBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
publicTest('should not be able to set the password if token is expired', async ({ acceptInvitationPage, page }) => {
|
|
||||||
const expiredTokenDate = DateTime.now().minus({days: 3}).toISO();
|
|
||||||
const expiredToken = (Math.random() + 1).toString(36).substring(2);
|
|
||||||
|
|
||||||
const adminCreateUserPage = new AdminCreateUserPage(page);
|
|
||||||
adminCreateUserPage.seed(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
|
|
||||||
const user = adminCreateUserPage.generateUser();
|
|
||||||
|
|
||||||
const queryRole = {
|
|
||||||
text: 'SELECT * FROM roles WHERE name = $1',
|
|
||||||
values: ['Admin']
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const queryRoleIdResult = await client.query(queryRole);
|
|
||||||
expect(queryRoleIdResult.rowCount).toEqual(1);
|
|
||||||
|
|
||||||
const insertUser = {
|
|
||||||
text: 'INSERT INTO users (email, full_name, role_id, status, invitation_token, invitation_token_sent_at) VALUES ($1, $2, $3, $4, $5, $6)',
|
|
||||||
values: [user.email, user.fullName, queryRoleIdResult.rows[0].id, 'invited', expiredToken, expiredTokenDate],
|
|
||||||
};
|
|
||||||
|
|
||||||
const insertUserResult = await client.query(insertUser);
|
|
||||||
expect(insertUserResult.rowCount).toBe(1);
|
|
||||||
expect(insertUserResult.command).toBe('INSERT');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
await acceptInvitationPage.open(expiredToken);
|
|
||||||
await acceptInvitationPage.acceptInvitation('something');
|
|
||||||
await acceptInvitationPage.expectAlertToBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
publicTest('should not be able to set the password if token is not in db', async ({ acceptInvitationPage }) => {
|
publicTest('should not be able to set the password if token is not in db', async ({ acceptInvitationPage }) => {
|
||||||
await acceptInvitationPage.open('abc');
|
await acceptInvitationPage.open('abc');
|
||||||
await acceptInvitationPage.acceptInvitation('something');
|
await acceptInvitationPage.acceptInvitation('something');
|
||||||
await acceptInvitationPage.expectAlertToBeVisible();
|
await acceptInvitationPage.expectAlertToBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
publicTest.describe('Accept invitation page - users', () => {
|
||||||
|
const expiredTokenDate = DateTime.now().minus({days: 3}).toISO();
|
||||||
|
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 }) => {
|
||||||
|
adminCreateUserPage.seed(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||||
|
const user = adminCreateUserPage.generateUser();
|
||||||
|
|
||||||
|
const queryRole = {
|
||||||
|
text: 'SELECT * FROM roles WHERE name = $1',
|
||||||
|
values: ['Admin']
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const queryRoleIdResult = await client.query(queryRole);
|
||||||
|
expect(queryRoleIdResult.rowCount).toEqual(1);
|
||||||
|
|
||||||
|
const insertUser = {
|
||||||
|
text: 'INSERT INTO users (email, full_name, role_id, status, invitation_token, invitation_token_sent_at) VALUES ($1, $2, $3, $4, $5, $6)',
|
||||||
|
values: [user.email, user.fullName, queryRoleIdResult.rows[0].id, 'invited', token, expiredTokenDate],
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertUserResult = await client.query(insertUser);
|
||||||
|
expect(insertUserResult.rowCount).toBe(1);
|
||||||
|
expect(insertUserResult.command).toBe('INSERT');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
await acceptInvitationPage.open(token);
|
||||||
|
await acceptInvitationPage.acceptInvitation('something');
|
||||||
|
await acceptInvitationPage.expectAlertToBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
publicTest('should not be able to accept invitation if user was soft deleted', async ({ acceptInvitationPage, adminCreateUserPage }) => {
|
||||||
|
const dateNow = DateTime.now().toISO();
|
||||||
|
const user = adminCreateUserPage.generateUser();
|
||||||
|
|
||||||
|
const queryRole = {
|
||||||
|
text: 'SELECT * FROM roles WHERE name = $1',
|
||||||
|
values: ['Admin']
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const queryRoleIdResult = await client.query(queryRole);
|
||||||
|
expect(queryRoleIdResult.rowCount).toEqual(1);
|
||||||
|
|
||||||
|
const insertUser = {
|
||||||
|
text: 'INSERT INTO users (email, full_name, deleted_at, role_id, status, invitation_token, invitation_token_sent_at) VALUES ($1, $2, $3, $4, $5, $6, $7)',
|
||||||
|
values: [user.email, user.fullName, dateNow, queryRoleIdResult.rows[0].id, 'invited', token, dateNow],
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertUserResult = await client.query(insertUser);
|
||||||
|
expect(insertUserResult.rowCount).toBe(1);
|
||||||
|
expect(insertUserResult.command).toBe('INSERT');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
await acceptInvitationPage.open(token);
|
||||||
|
await acceptInvitationPage.acceptInvitation('something');
|
||||||
|
await acceptInvitationPage.expectAlertToBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,7 +10,7 @@ import CardActionArea from '@mui/material/CardActionArea';
|
|||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
import ConnectionContextMenu from 'components/AppConnectionContextMenu';
|
import ConnectionContextMenu from 'components/AppConnectionContextMenu';
|
||||||
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
||||||
@@ -35,6 +35,7 @@ function AppConnectionRow(props) {
|
|||||||
const [verificationVisible, setVerificationVisible] = React.useState(false);
|
const [verificationVisible, setVerificationVisible] = React.useState(false);
|
||||||
const contextButtonRef = React.useRef(null);
|
const contextButtonRef = React.useRef(null);
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
||||||
|
|
||||||
@@ -75,6 +76,9 @@ function AppConnectionRow(props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ['apps', key, 'connections'],
|
||||||
|
});
|
||||||
enqueueSnackbar(formatMessage('connection.deletedMessage'), {
|
enqueueSnackbar(formatMessage('connection.deletedMessage'), {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
SnackbarProps: {
|
SnackbarProps: {
|
||||||
@@ -86,7 +90,7 @@ function AppConnectionRow(props) {
|
|||||||
testConnection({ variables: { id } });
|
testConnection({ variables: { id } });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[deleteConnection, id, testConnection, formatMessage, enqueueSnackbar],
|
[deleteConnection, id, queryClient, key, enqueueSnackbar, formatMessage, testConnection],
|
||||||
);
|
);
|
||||||
|
|
||||||
const relativeCreatedAt = DateTime.fromMillis(
|
const relativeCreatedAt = DateTime.fromMillis(
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useMutation } from '@apollo/client';
|
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
@@ -7,16 +6,14 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import ConfirmationDialog from 'components/ConfirmationDialog';
|
import ConfirmationDialog from 'components/ConfirmationDialog';
|
||||||
import { DELETE_USER } from 'graphql/mutations/delete-user.ee';
|
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useAdminUserDelete from 'hooks/useAdminUserDelete';
|
||||||
|
|
||||||
function DeleteUserButton(props) {
|
function DeleteUserButton(props) {
|
||||||
const { userId } = props;
|
const { userId } = props;
|
||||||
const [showConfirmation, setShowConfirmation] = React.useState(false);
|
const [showConfirmation, setShowConfirmation] = React.useState(false);
|
||||||
const [deleteUser] = useMutation(DELETE_USER, {
|
const { mutateAsync: deleteUser } = useAdminUserDelete(userId);
|
||||||
variables: { input: { id: userId } },
|
|
||||||
refetchQueries: ['GetUsers'],
|
|
||||||
});
|
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -33,7 +30,12 @@ function DeleteUserButton(props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Failed while deleting!');
|
enqueueSnackbar(
|
||||||
|
error?.message || formatMessage('deleteUserButton.deleteError'),
|
||||||
|
{
|
||||||
|
variant: 'error',
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [deleteUser]);
|
}, [deleteUser]);
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate, Link as RouterLink } from 'react-router-dom';
|
import { useNavigate, Link as RouterLink } from 'react-router-dom';
|
||||||
import { useMutation } from '@apollo/client';
|
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
@@ -8,17 +7,20 @@ import LoadingButton from '@mui/lab/LoadingButton';
|
|||||||
import useAuthentication from 'hooks/useAuthentication';
|
import useAuthentication from 'hooks/useAuthentication';
|
||||||
import useCloud from 'hooks/useCloud';
|
import useCloud from 'hooks/useCloud';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import { LOGIN } from 'graphql/mutations/login';
|
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
||||||
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
|
|
||||||
function LoginForm() {
|
function LoginForm() {
|
||||||
const isCloud = useCloud();
|
const isCloud = useCloud();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
const authentication = useAuthentication();
|
const authentication = useAuthentication();
|
||||||
const [login, { loading }] = useMutation(LOGIN);
|
const { mutateAsync: createAccessToken, isPending: loading } =
|
||||||
|
useCreateAccessToken();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (authentication.isAuthenticated) {
|
if (authentication.isAuthenticated) {
|
||||||
@@ -27,13 +29,19 @@ function LoginForm() {
|
|||||||
}, [authentication.isAuthenticated]);
|
}, [authentication.isAuthenticated]);
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
const { data } = await login({
|
try {
|
||||||
variables: {
|
const { email, password } = values;
|
||||||
input: values,
|
const { data } = await createAccessToken({
|
||||||
},
|
email,
|
||||||
});
|
password,
|
||||||
const { token } = data.login;
|
});
|
||||||
authentication.updateToken(token);
|
const { token } = data;
|
||||||
|
authentication.updateToken(token);
|
||||||
|
} catch (error) {
|
||||||
|
enqueueSnackbar(error?.message || formatMessage('loginForm.error'), {
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { useMutation } from '@apollo/client';
|
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
@@ -7,11 +6,12 @@ import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import { RESET_PASSWORD } from 'graphql/mutations/reset-password.ee';
|
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useResetPassword from 'hooks/useResetPassword';
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const validationSchema = yup.object().shape({
|
||||||
password: yup.string().required('resetPasswordForm.mandatoryInput'),
|
password: yup.string().required('resetPasswordForm.mandatoryInput'),
|
||||||
@@ -26,25 +26,35 @@ export default function ResetPasswordForm() {
|
|||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const [resetPassword, { data, loading }] = useMutation(RESET_PASSWORD);
|
const {
|
||||||
|
mutateAsync: resetPassword,
|
||||||
|
isPending,
|
||||||
|
isSuccess,
|
||||||
|
} = useResetPassword();
|
||||||
const token = searchParams.get('token');
|
const token = searchParams.get('token');
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
await resetPassword({
|
const { password } = values;
|
||||||
variables: {
|
try {
|
||||||
input: {
|
await resetPassword({
|
||||||
password: values.password,
|
password,
|
||||||
token,
|
token,
|
||||||
|
});
|
||||||
|
enqueueSnackbar(formatMessage('resetPasswordForm.passwordUpdated'), {
|
||||||
|
variant: 'success',
|
||||||
|
SnackbarProps: {
|
||||||
|
'data-test': 'snackbar-reset-password-success',
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
navigate(URLS.LOGIN);
|
||||||
enqueueSnackbar(formatMessage('resetPasswordForm.passwordUpdated'), {
|
} catch (error) {
|
||||||
variant: 'success',
|
enqueueSnackbar(
|
||||||
SnackbarProps: {
|
error?.message || formatMessage('resetPasswordForm.error'),
|
||||||
'data-test': 'snackbar-reset-password-success',
|
{
|
||||||
},
|
variant: 'error',
|
||||||
});
|
},
|
||||||
navigate(URLS.LOGIN);
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -113,8 +123,8 @@ export default function ResetPasswordForm() {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{ boxShadow: 2, my: 3 }}
|
sx={{ boxShadow: 2, my: 3 }}
|
||||||
loading={loading}
|
loading={isPending}
|
||||||
disabled={data || !token}
|
disabled={isSuccess || !token}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{formatMessage('resetPasswordForm.submit')}
|
{formatMessage('resetPasswordForm.submit')}
|
||||||
|
@@ -11,8 +11,10 @@ import * as URLS from 'config/urls';
|
|||||||
import { REGISTER_USER } from 'graphql/mutations/register-user.ee';
|
import { REGISTER_USER } from 'graphql/mutations/register-user.ee';
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import { LOGIN } from 'graphql/mutations/login';
|
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
||||||
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const validationSchema = yup.object().shape({
|
||||||
fullName: yup.string().trim().required('signupForm.mandatoryInput'),
|
fullName: yup.string().trim().required('signupForm.mandatoryInput'),
|
||||||
email: yup
|
email: yup
|
||||||
@@ -26,39 +28,57 @@ const validationSchema = yup.object().shape({
|
|||||||
.required('signupForm.mandatoryInput')
|
.required('signupForm.mandatoryInput')
|
||||||
.oneOf([yup.ref('password')], 'signupForm.passwordsMustMatch'),
|
.oneOf([yup.ref('password')], 'signupForm.passwordsMustMatch'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
fullName: '',
|
fullName: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
function SignUpForm() {
|
function SignUpForm() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const authentication = useAuthentication();
|
const authentication = useAuthentication();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
const [registerUser, { loading: registerUserLoading }] =
|
const [registerUser, { loading: registerUserLoading }] =
|
||||||
useMutation(REGISTER_USER);
|
useMutation(REGISTER_USER);
|
||||||
const [login, { loading: loginLoading }] = useMutation(LOGIN);
|
const { mutateAsync: createAccessToken, isPending: loginLoading } =
|
||||||
|
useCreateAccessToken();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (authentication.isAuthenticated) {
|
if (authentication.isAuthenticated) {
|
||||||
navigate(URLS.DASHBOARD);
|
navigate(URLS.DASHBOARD);
|
||||||
}
|
}
|
||||||
}, [authentication.isAuthenticated]);
|
}, [authentication.isAuthenticated]);
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
const { fullName, email, password } = values;
|
const { fullName, email, password } = values;
|
||||||
|
|
||||||
await registerUser({
|
await registerUser({
|
||||||
variables: {
|
variables: {
|
||||||
input: { fullName, email, password },
|
input: {
|
||||||
|
fullName,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { data } = await login({
|
|
||||||
variables: {
|
try {
|
||||||
input: { email, password },
|
const { data } = await createAccessToken({
|
||||||
},
|
email,
|
||||||
});
|
password,
|
||||||
const { token } = data.login;
|
});
|
||||||
authentication.updateToken(token);
|
const { token } = data;
|
||||||
|
authentication.updateToken(token);
|
||||||
|
} catch (error) {
|
||||||
|
enqueueSnackbar(error?.message || formatMessage('signupForm.error'), {
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ px: 2, py: 4 }}>
|
<Paper sx={{ px: 2, py: 4 }}>
|
||||||
<Typography
|
<Typography
|
||||||
@@ -168,4 +188,5 @@ function SignUpForm() {
|
|||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SignUpForm;
|
export default SignUpForm;
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const DELETE_USER = gql`
|
|
||||||
mutation DeleteUser($input: DeleteUserInput) {
|
|
||||||
deleteUser(input: $input)
|
|
||||||
}
|
|
||||||
`;
|
|
@@ -1,12 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const LOGIN = gql`
|
|
||||||
mutation Login($input: LoginInput) {
|
|
||||||
login(input: $input) {
|
|
||||||
token
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
@@ -1,6 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const RESET_PASSWORD = gql`
|
|
||||||
mutation ResetPassword($input: ResetPasswordInput) {
|
|
||||||
resetPassword(input: $input)
|
|
||||||
}
|
|
||||||
`;
|
|
15
packages/web/src/hooks/useAdminUserDelete.js
Normal file
15
packages/web/src/hooks/useAdminUserDelete.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import api from 'helpers/api';
|
||||||
|
|
||||||
|
export default function useAdminUserDelete(userId) {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
const { data } = await api.delete(`/v1/admin/users/${userId}`);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return mutation;
|
||||||
|
}
|
15
packages/web/src/hooks/useCreateAccessToken.js
Normal file
15
packages/web/src/hooks/useCreateAccessToken.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import api from 'helpers/api';
|
||||||
|
|
||||||
|
export default function useCreateAccessToken() {
|
||||||
|
const query = useMutation({
|
||||||
|
mutationFn: async ({ email, password }) => {
|
||||||
|
const { data } = await api.post('/v1/access-tokens', { email, password });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
15
packages/web/src/hooks/useResetPassword.js
Normal file
15
packages/web/src/hooks/useResetPassword.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import api from 'helpers/api';
|
||||||
|
|
||||||
|
export default function useResetPassword() {
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: async (payload) => {
|
||||||
|
const { data } = await api.post('/v1/users/reset-password', payload);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return mutation;
|
||||||
|
}
|
@@ -144,6 +144,7 @@
|
|||||||
"signupForm.validateEmail": "Email must be valid.",
|
"signupForm.validateEmail": "Email must be valid.",
|
||||||
"signupForm.passwordsMustMatch": "Passwords must match.",
|
"signupForm.passwordsMustMatch": "Passwords must match.",
|
||||||
"signupForm.mandatoryInput": "{inputName} is required.",
|
"signupForm.mandatoryInput": "{inputName} is required.",
|
||||||
|
"signupForm.error": "Something went wrong. Please try again.",
|
||||||
"loginForm.title": "Login",
|
"loginForm.title": "Login",
|
||||||
"loginForm.emailFieldLabel": "Email",
|
"loginForm.emailFieldLabel": "Email",
|
||||||
"loginForm.passwordFieldLabel": "Password",
|
"loginForm.passwordFieldLabel": "Password",
|
||||||
@@ -152,6 +153,7 @@
|
|||||||
"loginForm.noAccount": "Don't have an Automatisch account yet?",
|
"loginForm.noAccount": "Don't have an Automatisch account yet?",
|
||||||
"loginForm.signUp": "Sign up",
|
"loginForm.signUp": "Sign up",
|
||||||
"loginPage.divider": "OR",
|
"loginPage.divider": "OR",
|
||||||
|
"loginForm.error": "Something went wrong. Please try again.",
|
||||||
"ssoProviders.loginWithProvider": "Login with {providerName}",
|
"ssoProviders.loginWithProvider": "Login with {providerName}",
|
||||||
"forgotPasswordForm.title": "Forgot password",
|
"forgotPasswordForm.title": "Forgot password",
|
||||||
"forgotPasswordForm.submit": "Send reset instructions",
|
"forgotPasswordForm.submit": "Send reset instructions",
|
||||||
@@ -165,6 +167,7 @@
|
|||||||
"resetPasswordForm.passwordFieldLabel": "Password",
|
"resetPasswordForm.passwordFieldLabel": "Password",
|
||||||
"resetPasswordForm.confirmPasswordFieldLabel": "Confirm password",
|
"resetPasswordForm.confirmPasswordFieldLabel": "Confirm password",
|
||||||
"resetPasswordForm.passwordUpdated": "The password has been updated. Now, you can login.",
|
"resetPasswordForm.passwordUpdated": "The password has been updated. Now, you can login.",
|
||||||
|
"resetPasswordForm.error": "Something went wrong. Please try again.",
|
||||||
"acceptInvitationForm.passwordsMustMatch": "Passwords must match.",
|
"acceptInvitationForm.passwordsMustMatch": "Passwords must match.",
|
||||||
"acceptInvitationForm.mandatoryInput": "{inputName} is required.",
|
"acceptInvitationForm.mandatoryInput": "{inputName} is required.",
|
||||||
"acceptInvitationForm.title": "Accept invitation",
|
"acceptInvitationForm.title": "Accept invitation",
|
||||||
@@ -210,6 +213,7 @@
|
|||||||
"deleteUserButton.cancel": "Cancel",
|
"deleteUserButton.cancel": "Cancel",
|
||||||
"deleteUserButton.confirm": "Delete",
|
"deleteUserButton.confirm": "Delete",
|
||||||
"deleteUserButton.successfullyDeleted": "The user has been deleted.",
|
"deleteUserButton.successfullyDeleted": "The user has been deleted.",
|
||||||
|
"deleteUserButton.deleteError": "Failed while deleting!",
|
||||||
"createUserPage.title": "Create user",
|
"createUserPage.title": "Create user",
|
||||||
"userForm.fullName": "Full name",
|
"userForm.fullName": "Full name",
|
||||||
"userForm.email": "Email",
|
"userForm.email": "Email",
|
||||||
|
@@ -32,11 +32,11 @@ import useAuthentication from 'hooks/useAuthentication';
|
|||||||
import Installation from 'pages/Installation';
|
import Installation from 'pages/Installation';
|
||||||
|
|
||||||
function Routes() {
|
function Routes() {
|
||||||
const { data: configData } = useAutomatischConfig();
|
const { data: configData, isSuccess } = useAutomatischConfig();
|
||||||
const { isAuthenticated } = useAuthentication();
|
const { isAuthenticated } = useAuthentication();
|
||||||
const config = configData?.data;
|
const config = configData?.data;
|
||||||
|
|
||||||
const installed = configData?.data?.['installation.completed'] === true;
|
const installed = isSuccess ? config?.['installation.completed'] === true : true;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Reference in New Issue
Block a user