Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
295 lines
8.9 KiB
TypeScript
295 lines
8.9 KiB
TypeScript
import crypto from 'crypto';
|
|
|
|
import gql from 'graphql-tag';
|
|
import {
|
|
createApplicationRegistrationVariable,
|
|
deleteApplicationRegistrationVariable,
|
|
findApplicationRegistrationVariables,
|
|
updateApplicationRegistrationVariable,
|
|
} from 'test/integration/metadata/suites/application-registration-variable/utils/application-registration-variable-api.util';
|
|
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
|
|
|
const TEST_WORKSPACE_ID = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
|
|
|
const insertRegistrationDirect = async (
|
|
name: string,
|
|
): Promise<{ id: string }> => {
|
|
const id = crypto.randomUUID();
|
|
const universalIdentifier = crypto.randomUUID();
|
|
const oAuthClientId = crypto.randomUUID();
|
|
|
|
await globalThis.testDataSource.query(
|
|
`INSERT INTO core."applicationRegistration"
|
|
(id, "universalIdentifier", name, "oAuthClientId", "oAuthRedirectUris", "oAuthScopes", "workspaceId")
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
[
|
|
id,
|
|
universalIdentifier,
|
|
name,
|
|
oAuthClientId,
|
|
['http://localhost:3000/callback'],
|
|
['read'],
|
|
TEST_WORKSPACE_ID,
|
|
],
|
|
);
|
|
|
|
return { id };
|
|
};
|
|
|
|
const deleteRegistrationDirect = async (id: string): Promise<void> => {
|
|
await globalThis.testDataSource.query(
|
|
`DELETE FROM core."applicationRegistration" WHERE id = $1`,
|
|
[id],
|
|
);
|
|
};
|
|
|
|
describe('ApplicationRegistrationVariable (integration)', () => {
|
|
let registrationId: string;
|
|
|
|
beforeAll(async () => {
|
|
const registration = await insertRegistrationDirect('Variable Test App');
|
|
|
|
registrationId = registration.id;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await deleteRegistrationDirect(registrationId);
|
|
});
|
|
|
|
describe('CRUD lifecycle', () => {
|
|
let variableId: string;
|
|
|
|
it('should create a variable', async () => {
|
|
const { data } = await createApplicationRegistrationVariable({
|
|
applicationRegistrationId: registrationId,
|
|
key: 'API_KEY',
|
|
value: 'secret-value-123',
|
|
description: 'Third-party API key',
|
|
isSecret: true,
|
|
expectToFail: false,
|
|
});
|
|
|
|
const variable = data.createApplicationRegistrationVariable;
|
|
|
|
expect(variable).toBeDefined();
|
|
expect(variable.id).toBeDefined();
|
|
expect(variable.key).toBe('API_KEY');
|
|
expect(variable.description).toBe('Third-party API key');
|
|
expect(variable.isSecret).toBe(true);
|
|
expect(variable.isRequired).toBe(false);
|
|
expect(variable.isDeprecated).toBe(false);
|
|
expect(variable.isFilled).toBe(true);
|
|
|
|
variableId = variable.id;
|
|
});
|
|
|
|
it('should find variables for the registration', async () => {
|
|
const { data } = await findApplicationRegistrationVariables({
|
|
applicationRegistrationId: registrationId,
|
|
expectToFail: false,
|
|
});
|
|
|
|
const variables = data.findApplicationRegistrationVariables;
|
|
|
|
expect(variables).toBeDefined();
|
|
expect(variables.length).toBe(1);
|
|
expect(variables[0].key).toBe('API_KEY');
|
|
expect(variables[0].isFilled).toBe(true);
|
|
});
|
|
|
|
it('should update the variable value', async () => {
|
|
const { data } = await updateApplicationRegistrationVariable({
|
|
id: variableId,
|
|
value: 'new-secret-value-456',
|
|
expectToFail: false,
|
|
});
|
|
|
|
const variable = data.updateApplicationRegistrationVariable;
|
|
|
|
expect(variable).toBeDefined();
|
|
expect(variable.id).toBe(variableId);
|
|
expect(variable.isFilled).toBe(true);
|
|
});
|
|
|
|
it('should update the variable description', async () => {
|
|
const { data } = await updateApplicationRegistrationVariable({
|
|
id: variableId,
|
|
description: 'Updated API key description',
|
|
expectToFail: false,
|
|
});
|
|
|
|
const variable = data.updateApplicationRegistrationVariable;
|
|
|
|
expect(variable).toBeDefined();
|
|
expect(variable.description).toBe('Updated API key description');
|
|
});
|
|
|
|
it('should delete the variable', async () => {
|
|
const { data } = await deleteApplicationRegistrationVariable({
|
|
id: variableId,
|
|
expectToFail: false,
|
|
});
|
|
|
|
expect(data.deleteApplicationRegistrationVariable).toBe(true);
|
|
});
|
|
|
|
it('should return empty list after deletion', async () => {
|
|
const { data } = await findApplicationRegistrationVariables({
|
|
applicationRegistrationId: registrationId,
|
|
expectToFail: false,
|
|
});
|
|
|
|
expect(data.findApplicationRegistrationVariables).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe('non-secret variable', () => {
|
|
let variableId: string;
|
|
|
|
afterAll(async () => {
|
|
if (variableId) {
|
|
await deleteApplicationRegistrationVariable({
|
|
id: variableId,
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should create a non-secret variable', async () => {
|
|
const { data } = await createApplicationRegistrationVariable({
|
|
applicationRegistrationId: registrationId,
|
|
key: 'PUBLIC_URL',
|
|
value: 'https://example.com',
|
|
description: 'Public webhook URL',
|
|
isSecret: false,
|
|
expectToFail: false,
|
|
});
|
|
|
|
const variable = data.createApplicationRegistrationVariable;
|
|
|
|
expect(variable).toBeDefined();
|
|
expect(variable.key).toBe('PUBLIC_URL');
|
|
expect(variable.isSecret).toBe(false);
|
|
expect(variable.isFilled).toBe(true);
|
|
|
|
variableId = variable.id;
|
|
});
|
|
});
|
|
|
|
describe('error cases', () => {
|
|
it('should fail to create a variable for a non-existent registration', async () => {
|
|
const { errors } = await createApplicationRegistrationVariable({
|
|
applicationRegistrationId: '00000000-0000-0000-0000-000000000000',
|
|
key: 'SOME_KEY',
|
|
value: 'some-value',
|
|
expectToFail: true,
|
|
});
|
|
|
|
expect(errors).toBeDefined();
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should fail to update a non-existent variable', async () => {
|
|
const { errors } = await updateApplicationRegistrationVariable({
|
|
id: '00000000-0000-0000-0000-000000000000',
|
|
value: 'new-value',
|
|
expectToFail: true,
|
|
});
|
|
|
|
expect(errors).toBeDefined();
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should fail to delete a non-existent variable', async () => {
|
|
const { errors } = await deleteApplicationRegistrationVariable({
|
|
id: '00000000-0000-0000-0000-000000000000',
|
|
expectToFail: true,
|
|
});
|
|
|
|
expect(errors).toBeDefined();
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should fail to find variables for a registration not owned by current workspace', async () => {
|
|
const { errors } = await findApplicationRegistrationVariables({
|
|
applicationRegistrationId: '00000000-0000-0000-0000-000000000000',
|
|
expectToFail: true,
|
|
});
|
|
|
|
expect(errors).toBeDefined();
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('variable via GraphQL createApplicationRegistration', () => {
|
|
let gqlRegistrationId: string;
|
|
|
|
afterAll(async () => {
|
|
if (gqlRegistrationId) {
|
|
await globalThis.testDataSource.query(
|
|
`DELETE FROM core."applicationRegistration" WHERE id = $1`,
|
|
[gqlRegistrationId],
|
|
);
|
|
}
|
|
});
|
|
|
|
it('should create a registration via GraphQL and manage variables on it', async () => {
|
|
const createResponse = await makeMetadataAPIRequest({
|
|
query: gql`
|
|
mutation CreateApplicationRegistration(
|
|
$input: CreateApplicationRegistrationInput!
|
|
) {
|
|
createApplicationRegistration(input: $input) {
|
|
applicationRegistration {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
input: {
|
|
name: 'GQL Variable Test App',
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(createResponse.body.data).toBeDefined();
|
|
|
|
const registration =
|
|
createResponse.body.data.createApplicationRegistration
|
|
.applicationRegistration;
|
|
|
|
expect(registration).toBeDefined();
|
|
expect(registration.id).toBeDefined();
|
|
|
|
gqlRegistrationId = registration.id;
|
|
|
|
const { data: createVarData } =
|
|
await createApplicationRegistrationVariable({
|
|
applicationRegistrationId: gqlRegistrationId,
|
|
key: 'WEBHOOK_SECRET',
|
|
value: 'whsec_test123',
|
|
description: 'Webhook signing secret',
|
|
expectToFail: false,
|
|
});
|
|
|
|
expect(createVarData.createApplicationRegistrationVariable.key).toBe(
|
|
'WEBHOOK_SECRET',
|
|
);
|
|
expect(createVarData.createApplicationRegistrationVariable.isFilled).toBe(
|
|
true,
|
|
);
|
|
|
|
const { data: findVarData } = await findApplicationRegistrationVariables({
|
|
applicationRegistrationId: gqlRegistrationId,
|
|
expectToFail: false,
|
|
});
|
|
|
|
expect(findVarData.findApplicationRegistrationVariables).toHaveLength(1);
|
|
expect(findVarData.findApplicationRegistrationVariables[0].key).toBe(
|
|
'WEBHOOK_SECRET',
|
|
);
|
|
});
|
|
});
|
|
});
|