From 8d48ea10048a83592d21b17456bd177c334b4058 Mon Sep 17 00:00:00 2001 From: Ana Escontrela <87698274+ana-yldio@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:34:33 +0100 Subject: [PATCH] dev: removes invitation script (#1112) --- apps/asap-cli/src/cli.ts | 24 -- apps/asap-cli/src/invite/index.ts | 198 ----------- apps/asap-cli/src/invite/send-mail.ts | 25 -- apps/asap-cli/test/import/users.fixtures.ts | 86 ++++- apps/asap-cli/test/import/users.test.ts | 3 +- apps/asap-cli/test/invite.fixtures.ts | 167 --------- apps/asap-cli/test/invite.test.ts | 317 ------------------ apps/messages/src/templates/invite.tsx | 2 +- apps/storybook/src/WelcomeMessage.stories.tsx | 10 +- .../react-components/src/messages/Welcome.tsx | 68 +--- .../src/messages/__tests__/Welcome.test.tsx | 30 +- 11 files changed, 98 insertions(+), 832 deletions(-) delete mode 100644 apps/asap-cli/src/invite/index.ts delete mode 100644 apps/asap-cli/src/invite/send-mail.ts delete mode 100644 apps/asap-cli/test/invite.fixtures.ts delete mode 100644 apps/asap-cli/test/invite.test.ts diff --git a/apps/asap-cli/src/cli.ts b/apps/asap-cli/src/cli.ts index 13a0583b56..bae36d3c4b 100644 --- a/apps/asap-cli/src/cli.ts +++ b/apps/asap-cli/src/cli.ts @@ -7,7 +7,6 @@ import yargs from 'yargs/yargs'; import * as importers from './import'; -import inviteUsersFactory from './invite'; // eslint-disable-next-line no-unused-expressions yargs(process.argv.slice(2)) @@ -28,29 +27,6 @@ yargs(process.argv.slice(2)) handler: async ({ path, entity }) => importers[entity as 'users' | 'protocols'](path as string), }) - .command({ - command: 'invite [Options]', - describe: 'invite people to the ASAP Hub', - builder: (cli) => - cli - .positional('role', { - describe: 'specify a role to invite (optional)', - type: 'string', - default: undefined, - choices: ['Staff', 'Grantee', 'Guest'], - }) - .option('reinvite', { - alias: 'r', - type: 'boolean', - description: - "flag to reinvite users that didn't complete the registration process", - }), - - handler: async ({ role, reinvite }) => { - const inviteUsers = inviteUsersFactory(); - inviteUsers(role as string | undefined, Boolean(reinvite)); - }, - }) .demandCommand(1) .help('h') .alias('h', 'help') diff --git a/apps/asap-cli/src/invite/index.ts b/apps/asap-cli/src/invite/index.ts deleted file mode 100644 index 538542e566..0000000000 --- a/apps/asap-cli/src/invite/index.ts +++ /dev/null @@ -1,198 +0,0 @@ -import Intercept from 'apr-intercept'; -import { Squidex, RestUser, Query, RestTeam } from '@asap-hub/squidex'; -import { RateLimit } from 'async-sema'; -import { v4 as uuidV4 } from 'uuid'; -import path from 'path'; -import url from 'url'; -import { sendEmail } from './send-mail'; -import { origin } from '../config'; -import logger from '../logger'; - -interface HTTPError extends Error { - response?: { - statusCode: number; - body: string; - }; -} - -interface inviteUsersFn { - /* eslint-disable no-unused-vars */ - ( - role?: string, - reinvite?: boolean, - take?: number, - skip?: number, - ): Promise; - /* eslint-enable no-unused-vars */ -} - -const inviteUsersFactory = ( - log: typeof console.log = logger, -): inviteUsersFn => { - const userClient: Squidex = new Squidex('users'); - const teamClient: Squidex = new Squidex('teams'); - - const limiter = RateLimit(10); - const uuidMatch = - /^([\d\w]{8})-?([\d\w]{4})-?([\d\w]{4})-?([\d\w]{4})-?([\d\w]{12})|[{0x]*([\d\w]{8})[0x, ]{4}([\d\w]{4})[0x, ]{4}([\d\w]{4})[0x, {]{5}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})[0x, ]{4}([\d\w]{2})$/; - - const inviteUsers: inviteUsersFn = async ( - role, - reinvite = false, - take = 20, - skip = 0, - ) => { - const query: Query = { - skip, - take, - sort: [{ path: 'created', order: 'descending' }], - }; - - if (role) { - query.filter = { - path: 'data.role.iv', - op: 'eq', - value: role, - }; - } - - const { items } = await userClient.fetch(query); - - const usersToInvite = items.filter((u) => { - const connections = u.data.connections.iv; - if (!connections || connections.length === 0) { - return true; - } - - const authConnections = connections.filter( - (c) => !c.code.match(uuidMatch), - ); - if (reinvite && authConnections.length === 0) { - return true; - } - - return false; - }); - - const userTeamIds = usersToInvite - .flatMap((user) => user.data.teams.iv) - .flatMap((team) => team?.id) - .filter(Boolean) - .filter((team, pos, self) => self.indexOf(team) === pos) as string[]; - - let teamMap: { [key: string]: string }; - if (userTeamIds.length > 0) { - const teamQuery: Query = { - skip: 0, - take: 20, // Dont expect a user to have more than 20 teams - filter: { - path: 'id', - op: 'in', - value: userTeamIds, - }, - }; - const [teamsError, res] = await Intercept(teamClient.fetch(teamQuery)); - - const fetchTeamError = teamsError as HTTPError; - if (fetchTeamError) { - log({ - op: 'Fetch teams', - message: fetchTeamError.message, - statusCode: fetchTeamError.response?.statusCode, - body: fetchTeamError.response?.body, - }); - throw fetchTeamError; - } - - const { items: teams } = res; - teamMap = teams.reduce( - (acc, team) => ({ - ...acc, - [team.id]: team.data.displayName.iv, - }), - {}, - ); - } - - await Promise.all( - usersToInvite.map(async (user) => { - await limiter(); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - let code = user.data.connections - .iv! // asserting not null as the nulls values are filtered in usersToInvite - .map((c) => c.code) - .find((c) => c.match(uuidMatch)); - - if (!code) { - code = uuidV4(); - log(`Created invite code for ${user.data.email.iv}`); - const [err1] = await Intercept( - userClient.patch(user.id, { - email: user.data.email, - connections: { - iv: [{ code }], - }, - }), - ); - - const error = err1 as HTTPError; - if (error) { - log({ - op: `patch '${user.id}'`, - message: error.message, - statusCode: error.response?.statusCode, - body: error.response?.body, - }); - throw err1; - } - } - - const link = new url.URL(path.join(`/welcome/${code}`), origin); - const [err2] = await Intercept( - sendEmail({ - to: [user.data.email.iv], - template: 'Welcome', - values: { - firstName: user.data.firstName.iv, - link: link.toString(), - }, - }), - ); - - if (!err2) { - let userTeams; - if (teamMap) { - userTeams = user.data.teams.iv - ?.flatMap((team) => team.id) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .map((id) => teamMap![id]) - .join(' | '); - } - const teamReport = userTeams ? `(${userTeams})` : ''; - log(`Invited user ${user.data.email.iv} ${teamReport}`); - return; - } - - // error sending mail, remove inserted code - await userClient.patch(user.id, { - email: user.data.email, - connections: { iv: [] }, - }); - - throw err2; - }), - ); - - if (items.length < take) { - log('Found no more uninvited users'); - return; - } - - await inviteUsers(role, reinvite, take, skip + take); - }; - - return inviteUsers; -}; - -export default inviteUsersFactory; diff --git a/apps/asap-cli/src/invite/send-mail.ts b/apps/asap-cli/src/invite/send-mail.ts deleted file mode 100644 index 4bfe32f0c7..0000000000 --- a/apps/asap-cli/src/invite/send-mail.ts +++ /dev/null @@ -1,25 +0,0 @@ -import aws from 'aws-sdk'; -import { welcome } from '@asap-hub/message-templates'; -import { grantsFromEmail } from '../config'; - -const ses = new aws.SES({ apiVersion: '2010-12-01', region: 'us-east-1' }); -export const sendEmail = async ({ - to, - template, - values, -}: { - to: string[]; - template: 'Welcome'; - values: typeof welcome; -}): Promise => { - const params = { - Destination: { - ToAddresses: to, - }, - Template: template, - TemplateData: JSON.stringify(values), - Source: grantsFromEmail, - }; - - return ses.sendTemplatedEmail(params).promise(); -}; diff --git a/apps/asap-cli/test/import/users.fixtures.ts b/apps/asap-cli/test/import/users.fixtures.ts index b4fa95f9b9..aaf46f069c 100644 --- a/apps/asap-cli/test/import/users.fixtures.ts +++ b/apps/asap-cli/test/import/users.fixtures.ts @@ -1,4 +1,4 @@ -import { RestTeam } from '@asap-hub/squidex'; +import { RestTeam, RestUser } from '@asap-hub/squidex'; export const fetchTeamsResponse: { total: number; items: RestTeam[] } = { total: 1, @@ -17,3 +17,87 @@ export const fetchTeamsResponse: { total: number; items: RestTeam[] } = { }, ], }; + +export const fetchUsersResponse: { total: number; items: RestUser[] } = { + total: 200, + items: [ + { + id: 'userId1', + lastModified: '2020-09-25T11:06:27.164Z', + created: '2020-09-24T11:06:27.164Z', + data: { + avatar: { iv: [] }, + lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, + email: { iv: 'testUser@asap.science' }, + firstName: { iv: 'First' }, + lastName: { iv: 'Last' }, + jobTitle: { iv: 'Title' }, + institution: { iv: 'Institution' }, + connections: { iv: [] }, + biography: { iv: 'Biography' }, + teams: { iv: [] }, + questions: { iv: [] }, + skills: { iv: [] }, + role: { iv: 'Grantee' }, + onboarded: { + iv: true, + }, + labs: { iv: [] }, + }, + }, + { + id: 'userId2', + lastModified: '2020-09-25T11:06:27.164Z', + created: '2020-09-24T11:06:27.164Z', + data: { + avatar: { iv: [] }, + lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, + email: { iv: 'testUser@asap.science' }, + firstName: { iv: 'First' }, + lastName: { iv: 'Last' }, + jobTitle: { iv: 'Title' }, + institution: { iv: 'Institution' }, + connections: { iv: [] }, + biography: { iv: 'Biography' }, + questions: { iv: [{ question: 'Question?' }] }, + teams: { iv: [] }, + skills: { iv: [] }, + role: { iv: 'Grantee' }, + onboarded: { + iv: true, + }, + labs: { iv: [] }, + }, + }, + { + id: 'userId3', + lastModified: '2020-09-25T11:06:27.164Z', + created: '2020-09-24T11:06:27.164Z', + data: { + avatar: { iv: [] }, + lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, + email: { iv: 'me@example.com' }, + firstName: { iv: 'First' }, + lastName: { iv: 'Last' }, + jobTitle: { iv: 'Title' }, + institution: { iv: 'Institution' }, + connections: { + iv: [ + { + code: 'ALREADY_HAS_CODE', + }, + ], + }, + biography: { iv: 'Biography' }, + questions: { iv: [{ question: 'Question?' }] }, + teams: { iv: [] }, + skills: { iv: [] }, + role: { iv: 'Grantee' }, + onboarded: { + iv: true, + }, + labs: { iv: [] }, + }, + }, + ], +}; diff --git a/apps/asap-cli/test/import/users.test.ts b/apps/asap-cli/test/import/users.test.ts index d12a2241d1..78efe70b20 100644 --- a/apps/asap-cli/test/import/users.test.ts +++ b/apps/asap-cli/test/import/users.test.ts @@ -3,8 +3,7 @@ import { join } from 'path'; import { config } from '@asap-hub/squidex'; import { identity } from '../helpers/squidex'; import { users as importUsers } from '../../src/import'; -import { fetchUsersResponse } from '../invite.fixtures'; -import { fetchTeamsResponse } from './users.fixtures'; +import { fetchTeamsResponse, fetchUsersResponse } from './users.fixtures'; const body = { email: { diff --git a/apps/asap-cli/test/invite.fixtures.ts b/apps/asap-cli/test/invite.fixtures.ts deleted file mode 100644 index 6406c5d859..0000000000 --- a/apps/asap-cli/test/invite.fixtures.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { RestTeam, RestUser } from '@asap-hub/squidex'; - -export const fetchUsersResponse: { total: number; items: RestUser[] } = { - total: 200, - items: [ - { - id: 'userId1', - lastModified: '2020-09-25T11:06:27.164Z', - created: '2020-09-24T11:06:27.164Z', - data: { - avatar: { iv: [] }, - lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, - email: { iv: 'testUser@asap.science' }, - firstName: { iv: 'First' }, - lastName: { iv: 'Last' }, - jobTitle: { iv: 'Title' }, - institution: { iv: 'Institution' }, - connections: { iv: [] }, - biography: { iv: 'Biography' }, - teams: { iv: [] }, - questions: { iv: [] }, - skills: { iv: [] }, - role: { iv: 'Grantee' }, - onboarded: { - iv: true, - }, - labs: { iv: [] }, - }, - }, - { - id: 'userId2', - lastModified: '2020-09-25T11:06:27.164Z', - created: '2020-09-24T11:06:27.164Z', - data: { - avatar: { iv: [] }, - lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, - email: { iv: 'testUser@asap.science' }, - firstName: { iv: 'First' }, - lastName: { iv: 'Last' }, - jobTitle: { iv: 'Title' }, - institution: { iv: 'Institution' }, - connections: { iv: [] }, - biography: { iv: 'Biography' }, - questions: { iv: [{ question: 'Question?' }] }, - teams: { iv: [] }, - skills: { iv: [] }, - role: { iv: 'Grantee' }, - onboarded: { - iv: true, - }, - labs: { iv: [] }, - }, - }, - { - id: 'userId3', - lastModified: '2020-09-25T11:06:27.164Z', - created: '2020-09-24T11:06:27.164Z', - data: { - avatar: { iv: [] }, - lastModifiedDate: { iv: '2020-09-25T11:06:27.164Z' }, - email: { iv: 'me@example.com' }, - firstName: { iv: 'First' }, - lastName: { iv: 'Last' }, - jobTitle: { iv: 'Title' }, - institution: { iv: 'Institution' }, - connections: { - iv: [ - { - code: 'ALREADY_HAS_CODE', - }, - ], - }, - biography: { iv: 'Biography' }, - questions: { iv: [{ question: 'Question?' }] }, - teams: { iv: [] }, - skills: { iv: [] }, - role: { iv: 'Grantee' }, - onboarded: { - iv: true, - }, - labs: { iv: [] }, - }, - }, - ], -}; - -export const getFetchUsersWithTeamsResponse: () => { - total: number; - items: RestUser[]; -} = () => ({ - total: 3, - items: [ - { - ...fetchUsersResponse.items[0], - data: { - ...fetchUsersResponse.items[0].data, - teams: { - iv: [ - { - id: ['team-id-1'], - role: 'Project Manager', - }, - ], - }, - }, - }, - { - ...fetchUsersResponse.items[1], - data: { - ...fetchUsersResponse.items[1].data, - teams: { - iv: [ - { - id: ['team-id-2'], - role: 'Collaborating PI', - }, - ], - }, - }, - }, - ], -}); - -export const getListTeamResponse: () => { - total: number; - items: RestTeam[]; -} = () => ({ - total: 2, - items: [ - { - id: 'team-id-1', - data: { - displayName: { iv: 'Team 1' }, - applicationNumber: { iv: 'hofded' }, - projectTitle: { - iv: 'Ce fe kok ob lovkad pim cukiviw lakwujuz vilid camiduci nim ca perkeb mekkaho wuculate re huppoljop.', - }, - projectSummary: { - iv: 'Wi dalev fu jusjuh buw nauzi kas ma. Fo ajelo pu vaenusug ezuhsi resdudif ebsofak tav dan mumooz awgabu meki gicub bowec afegeir tozab umefarow.', - }, - skills: { iv: [] }, - outputs: { iv: [] }, - tools: { iv: [] }, - }, - created: '2020-09-08T16:35:28Z', - lastModified: '2020-09-08T16:35:28Z', - }, - { - id: 'team-id-2', - data: { - displayName: { iv: 'Team 2' }, - applicationNumber: { iv: 'hofded' }, - projectTitle: { - iv: 'Ce fe kok ob lovkad pim cukiviw lakwujuz vilid camiduci nim ca perkeb mekkaho wuculate re huppoljop.', - }, - projectSummary: { - iv: 'Wi dalev fu jusjuh buw nauzi kas ma. Fo ajelo pu vaenusug ezuhsi resdudif ebsofak tav dan mumooz awgabu meki gicub bowec afegeir tozab umefarow.', - }, - skills: { iv: [] }, - outputs: { iv: [] }, - tools: { iv: [] }, - }, - created: '2020-09-08T16:35:28Z', - lastModified: '2020-09-08T16:35:28Z', - }, - ], -}); diff --git a/apps/asap-cli/test/invite.test.ts b/apps/asap-cli/test/invite.test.ts deleted file mode 100644 index 9332f82676..0000000000 --- a/apps/asap-cli/test/invite.test.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { SES } from 'aws-sdk'; -import nock from 'nock'; -import { config } from '@asap-hub/squidex'; -import { identity } from './helpers/squidex'; -import inviteUsersFactory from '../src/invite'; -import { origin, grantsFromEmail } from '../src/config'; -import { - fetchUsersResponse, - getFetchUsersWithTeamsResponse, - getListTeamResponse, -} from './invite.fixtures'; - -jest.mock('aws-sdk', () => ({ - SES: jest.fn().mockReturnValue({ - sendTemplatedEmail: jest.fn().mockReturnValue({ - promise: jest.fn().mockResolvedValue({}), - }), - }), -})); - -jest.mock('uuid', () => ({ - v4: jest.fn(() => 'uuid'), -})); - -const consoleLog = jest.fn().mockImplementation(() => {}); -const inviteUsers = inviteUsersFactory(consoleLog); - -describe('Invite user', () => { - beforeAll(() => { - identity(); - }); - - afterEach(() => { - expect(nock.isDone()).toBe(true); - }); - - afterEach(() => { - jest.clearAllMocks(); // clear call count - nock.cleanAll(); - }); - - test('Doesnt send mails when doesnt find users without code', async () => { - const ses = new SES(); - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - filter: { - path: 'data.role.iv', - op: 'eq', - value: 'Staff', - }, - }), - }) - .reply(200, { items: [fetchUsersResponse.items[2]] }); - - await inviteUsers('Staff'); - expect(ses.sendTemplatedEmail).toBeCalledTimes(0); - }); - - test('Sends emails and fetches users with the role "Staff" correctly', async () => { - const ses = new SES(); - - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - filter: { - path: 'data.role.iv', - op: 'eq', - value: 'Staff', - }, - }), - }) - .reply(200, fetchUsersResponse) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersResponse.items[0]) - .patch(`/api/content/${config.appName}/users/userId2`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersResponse.items[1]) - .get(`/api/content/${config.appName}/users`); - - await inviteUsers('Staff'); - expect(ses.sendTemplatedEmail).toBeCalledTimes(2); - expect(ses.sendTemplatedEmail).toBeCalledWith({ - Source: grantsFromEmail, - Destination: { - ToAddresses: ['testUser@asap.science'], - }, - Template: 'Welcome', - TemplateData: JSON.stringify({ - firstName: 'First', - link: `${origin}/welcome/uuid`, - }), - }); - }); - - test('Sends emails and fetches users with no particular role', async () => { - const ses = new SES(); - - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - }), - }) - .reply(200, fetchUsersResponse) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersResponse.items[0]) - .patch(`/api/content/${config.appName}/users/userId2`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersResponse.items[1]) - .get(`/api/content/${config.appName}/users`); - - await inviteUsers(); - - expect(ses.sendTemplatedEmail).toBeCalledTimes(2); - expect(ses.sendTemplatedEmail).toBeCalledWith({ - Source: grantsFromEmail, - Destination: { - ToAddresses: ['testUser@asap.science'], - }, - Template: 'Welcome', - TemplateData: JSON.stringify({ - firstName: 'First', - link: `${origin}/welcome/uuid`, - }), - }); - }); - - test('Logs invited users and the teams they belong to', async () => { - const ses = new SES(); - - const fetchUsersWithTeamsResponse = getFetchUsersWithTeamsResponse(); - const listTeamResponse = getListTeamResponse(); - - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - }), - }) - .reply(200, fetchUsersWithTeamsResponse) - .get(`/api/content/${config.appName}/teams`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - filter: { path: 'id', op: 'in', value: ['team-id-1', 'team-id-2'] }, - }), - }) - .reply(200, listTeamResponse) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersWithTeamsResponse.items[0]) - .patch(`/api/content/${config.appName}/users/userId2`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersWithTeamsResponse.items[1]) - .get(`/api/content/${config.appName}/users`); - - await inviteUsers(); - - expect(ses.sendTemplatedEmail).toBeCalledTimes(2); - expect(consoleLog).toBeCalledWith( - `Invited user ${fetchUsersWithTeamsResponse.items[0].data.email.iv} (${listTeamResponse.items[0].data.displayName.iv})`, - ); - expect(consoleLog).toBeCalledWith( - `Invited user ${fetchUsersWithTeamsResponse.items[1].data.email.iv} (${listTeamResponse.items[1].data.displayName.iv})`, - ); - }); - - test('Logs user teams when they belong to a multiple', async () => { - const user = { - id: 'userId1', - lastModified: '2020-09-25T11:06:27.164Z', - created: '2020-09-24T11:06:27.164Z', - data: { - ...fetchUsersResponse.items[0].data, - teams: { - iv: [ - { - id: ['team-id-3', 'team-id-4'], - role: 'Project Manager' as const, - }, - ], - }, - }, - }; - const fetchUserMultipleTeamsResponse = getFetchUsersWithTeamsResponse(); - fetchUserMultipleTeamsResponse.items = [user]; - - const listTeamResponse = getListTeamResponse(); - listTeamResponse.items[0].id = 'team-id-3'; - listTeamResponse.items[1].id = 'team-id-4'; - - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - }), - }) - .reply(200, fetchUserMultipleTeamsResponse) - .get(`/api/content/${config.appName}/teams`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - filter: { path: 'id', op: 'in', value: ['team-id-3', 'team-id-4'] }, - }), - }) - .reply(200, listTeamResponse) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, getFetchUsersWithTeamsResponse().items[0]) - .get(`/api/content/${config.appName}/users`); - - await inviteUsers(); - - expect(consoleLog).toBeCalledWith( - `Invited user ${user.data.email.iv} (${listTeamResponse.items[0].data.displayName.iv} | ${listTeamResponse.items[1].data.displayName.iv})`, - ); - }); - - test('Doesnt send mail when fails to write the users code on squidex', async () => { - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - filter: { - path: 'data.role.iv', - op: 'eq', - value: 'Staff', - }, - }), - }) - .reply(200, { items: [fetchUsersResponse.items[0]] }) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(500); - - const ses = new SES(); - await expect(inviteUsers('Staff')).rejects.toThrow(); - expect(ses.sendTemplatedEmail).toBeCalledTimes(0); - }); - - test('Deletes code after failure sending mail', async () => { - const ses = new SES(); - jest - .spyOn(ses.sendTemplatedEmail(), 'promise') - .mockRejectedValue(new Error()); - - nock(config.baseUrl) - .get(`/api/content/${config.appName}/users`) - .query({ - q: JSON.stringify({ - take: 20, - skip: 0, - sort: [{ path: 'created', order: 'descending' }], - filter: { - path: 'data.role.iv', - op: 'eq', - value: 'Staff', - }, - }), - }) - .reply(200, { items: [fetchUsersResponse.items[0]] }) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [{ code: 'uuid' }] }, - }) - .reply(200, fetchUsersResponse.items[0]) - .patch(`/api/content/${config.appName}/users/userId1`, { - email: { iv: 'testUser@asap.science' }, - connections: { iv: [] }, - }) - .reply(200, fetchUsersResponse.items[0]); - - await expect(inviteUsers('Staff')).rejects.toThrow(); - expect(ses.sendTemplatedEmail().promise).toBeCalledTimes(1); - }); -}); diff --git a/apps/messages/src/templates/invite.tsx b/apps/messages/src/templates/invite.tsx index 59ee8fc5c2..b5293bb48c 100644 --- a/apps/messages/src/templates/invite.tsx +++ b/apps/messages/src/templates/invite.tsx @@ -5,7 +5,7 @@ import { APP_ORIGIN } from '../config'; export default ( - + ); diff --git a/apps/storybook/src/WelcomeMessage.stories.tsx b/apps/storybook/src/WelcomeMessage.stories.tsx index 2be8dfcdc6..c9f39bc8b0 100644 --- a/apps/storybook/src/WelcomeMessage.stories.tsx +++ b/apps/storybook/src/WelcomeMessage.stories.tsx @@ -9,17 +9,9 @@ export default { decorators: [MessageLayoutDecorator], }; -export const InviteScript = () => ( +export const Normal = () => ( ); - -export const Invite = () => ( - -); diff --git a/packages/react-components/src/messages/Welcome.tsx b/packages/react-components/src/messages/Welcome.tsx index a2407833d1..5a4a6afcde 100644 --- a/packages/react-components/src/messages/Welcome.tsx +++ b/packages/react-components/src/messages/Welcome.tsx @@ -1,42 +1,14 @@ import { Display, Paragraph, Link } from '../atoms'; import { mailToSupport } from '../mail'; -type WelcomeContentProps = { link: string }; - -const InviteScriptWelcomeTemplate: React.FC = ({ - link, -}) => ( - <> - - Thank you for filling out your Profile Form, you’re one step away from - joining the ASAP Hub! Please choose a login method and activate your - account. - - - The ASAP Hub is a platform where you’ll be able to collaborate with your - team, connect with others, join ASAP events, and access new resources - generated throughout the network. - - - Activate account - - - If you're facing a technical issue with the Hub, please{' '} - get in touch. Our Support team is - happy to help! - - - Note: please be mindful that the ASAP Hub is a closed platform developed - for ASAP grantees only. The closed nature of the Hub is meant to foster - trust, candor, and connection. As a reminder, your commitment to - confidentiality has been codified in the ASAP grant agreement to which - your team has agreed - - -); +type WelcomeProps = { + readonly firstName: string; + readonly link: string; +}; -const InviteWelcomeTemplate: React.FC = ({ link }) => ( - <> +const Welcome: React.FC = ({ firstName, link }) => ( +
+ Dear {firstName} Thank you for starting this journey with us! You’re one step closer to joining the ASAP Hub - the virtual home of the @@ -70,31 +42,7 @@ const InviteWelcomeTemplate: React.FC = ({ link }) => ( connection among grantees. In line with your team’s grant agreement, you are expected to comply with confidentiality guidelines. - +
); -type WelcomeProps = { - readonly firstName: string; - readonly link: string; - readonly variant?: 'InviteScriptWelcomeTemplate' | 'InviteWelcomeTemplate'; -}; - -const Welcome: React.FC = ({ - firstName, - link, - variant = 'InviteScriptWelcomeTemplate', -}) => { - const contentVariants = { - InviteScriptWelcomeTemplate, - InviteWelcomeTemplate, - }; - const ContentTag = contentVariants[variant]; - return ( -
- Dear {firstName} - -
- ); -}; - export default Welcome; diff --git a/packages/react-components/src/messages/__tests__/Welcome.test.tsx b/packages/react-components/src/messages/__tests__/Welcome.test.tsx index 09898645be..e898106317 100644 --- a/packages/react-components/src/messages/__tests__/Welcome.test.tsx +++ b/packages/react-components/src/messages/__tests__/Welcome.test.tsx @@ -17,42 +17,16 @@ describe('welcome email template', () => { }); it('renders the correct link on the Activate Account CTA', () => { - const cta = result.getByText(/activate account/i); + const cta = result.getByRole('link', { name: /create account/i }); expect(cta.closest('a')).toHaveAttribute('href', 'https://example.com'); }); it('renders the correct mail to on the get in touch mailto link', () => { const mailToSupport = result.getByRole('link', { - name: /get in touch/i, + name: /our team/i, }) as HTMLAnchorElement; expect(mailToSupport.protocol).toBe('mailto:'); expect(mailToSupport.pathname).toBe('techsupport@asap.science'); }); - - it('renders different variants of the content', () => { - let cta1; - let cta2; - const { queryByRole, rerender } = result; - - cta1 = queryByRole('link', { name: /activate account/i }); - cta2 = queryByRole('link', { name: /create account/i }); - expect(cta1).toHaveTextContent('Activate account'); - expect(cta1).not.toBeNull(); - expect(cta2).toBeNull(); - - rerender( - , - ); - - cta1 = queryByRole('link', { name: /activate account/i }); - cta2 = queryByRole('link', { name: /create account/i }); - expect(cta2).toHaveTextContent('Create account'); - expect(cta2).not.toBeNull(); - expect(cta1).toBeNull(); - }); });