Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core,schemas,test): rename DataHook data update event name #5876

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ describe('submit action', () => {
login: { accountId: 'foo' },
});
expect(ctx.assignDataHookContext).toBeCalledWith({
event: 'User.Updated',
event: 'User.Data.Updated',
user: updateProfile,
});
});
Expand Down Expand Up @@ -433,7 +433,7 @@ describe('submit action', () => {
login: { accountId: 'foo' },
});
expect(ctx.assignDataHookContext).toBeCalledWith({
event: 'User.Updated',
event: 'User.Data.Updated',
user: {
primaryEmail: 'email',
name: userInfo.name,
Expand All @@ -459,7 +459,7 @@ describe('submit action', () => {
});
expect(assignInteractionResults).not.toBeCalled();
expect(ctx.assignDataHookContext).toBeCalledWith({
event: 'User.Updated',
event: 'User.Data.Updated',
user: {
passwordEncrypted: 'passwordEncrypted',
passwordEncryptionMethod: 'plain',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@
// If it's Logto Cloud, Check if the new user has any pending invitations, if yes, skip onboarding flow.
const invitations =
isCloud && userProfile.primaryEmail
? await organizations.invitations.findEntities({ invitee: userProfile.primaryEmail })
? await organizations.invitations.findEntities({
invitee: userProfile.primaryEmail,
})

Check warning on line 132 in packages/core/src/routes/interaction/actions/submit-interaction.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/routes/interaction/actions/submit-interaction.ts#L131-L132

Added lines #L131 - L132 were not covered by tests
: [];
const hasPendingInvitations = invitations.some(
(invitation) => invitation.status === OrganizationInvitationStatus.Pending
Expand Down Expand Up @@ -189,7 +191,9 @@
ctx.assignDataHookContext({ event: 'User.Created', user });

log?.append({ userId: id });
appInsights.client?.trackEvent({ name: getEventName(Component.Core, CoreEvent.Register) });
appInsights.client?.trackEvent({
name: getEventName(Component.Core, CoreEvent.Register),
});

void trySafe(postAffiliateLogs(ctx, cloudConnection, id, tenantId), (error) => {
getConsoleLogFromContext(ctx).warn('Failed to post affiliate logs', error);
Expand Down Expand Up @@ -238,10 +242,15 @@
ctx.assignInteractionHookResult({ userId: accountId });
// Trigger user.updated data hook event if the user profile or mfa data is updated
if (hasUpdatedProfile(updateUserProfile) || mfaVerifications.length > 0) {
ctx.assignDataHookContext({ event: 'User.Updated', user: updatedUser });
ctx.assignDataHookContext({
event: 'User.Data.Updated',
user: updatedUser,
});
}

appInsights.client?.trackEvent({ name: getEventName(Component.Core, CoreEvent.SignIn) });
appInsights.client?.trackEvent({
name: getEventName(Component.Core, CoreEvent.SignIn),
});
}

export default async function submitInteraction(
Expand Down Expand Up @@ -270,9 +279,12 @@
profile.password
);

const user = await updateUserById(accountId, { passwordEncrypted, passwordEncryptionMethod });
const user = await updateUserById(accountId, {
passwordEncrypted,
passwordEncryptionMethod,
});
ctx.assignInteractionHookResult({ userId: accountId });
ctx.assignDataHookContext({ event: 'User.Updated', user });
ctx.assignDataHookContext({ event: 'User.Data.Updated', user });

await clearInteractionStorage(ctx, provider);
ctx.status = 204;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ describe('interaction api trigger hooks', () => {
});

// Assert user updated event is not triggered
await assertHookLogResult(dataHook, 'User.Updated', {
await assertHookLogResult(dataHook, 'User.Data.Updated', {
toBeUndefined: true,
});

Expand Down Expand Up @@ -221,7 +221,7 @@ describe('interaction api trigger hooks', () => {
toBeUndefined: true,
});

await assertHookLogResult(dataHook, 'User.Updated', {
await assertHookLogResult(dataHook, 'User.Data.Updated', {
toBeUndefined: true,
});
});
Expand Down Expand Up @@ -257,9 +257,9 @@ describe('interaction api trigger hooks', () => {
toBeUndefined: true,
});

await assertHookLogResult(dataHook, 'User.Updated', {
await assertHookLogResult(dataHook, 'User.Data.Updated', {
hookPayload: {
event: 'User.Updated',
event: 'User.Data.Updated',
interactionEvent: InteractionEvent.SignIn,
sessionId: expect.any(String),
data: expect.objectContaining({ id: user.id, username, primaryEmail: email }),
Expand All @@ -286,9 +286,9 @@ describe('interaction api trigger hooks', () => {
hookPayload: interactionHookEventPayload,
});

await assertHookLogResult(dataHook, 'User.Updated', {
await assertHookLogResult(dataHook, 'User.Data.Updated', {
hookPayload: {
event: 'User.Updated',
event: 'User.Data.Updated',
interactionEvent: InteractionEvent.ForgotPassword,
sessionId: expect.any(String),
data: expect.objectContaining({ id: user.id, username, primaryEmail: email }),
Expand Down
18 changes: 9 additions & 9 deletions packages/integration-tests/src/tests/api/hook/test-cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,28 @@ type TestCase = {
export const userDataHookTestCases: TestCase[] = [
{
route: 'PATCH /users/:userId',
event: 'User.Updated',
event: 'User.Data.Updated',
method: 'patch',
endpoint: `users/{userId}`,
payload: { name: 'new name' },
},
{
route: 'PATCH /users/:userId/custom-data',
event: 'User.Updated',
event: 'User.Data.Updated',
method: 'patch',
endpoint: `users/{userId}/custom-data`,
payload: { customData: { foo: 'bar' } },
},
{
route: 'PATCH /users/:userId/profile',
event: 'User.Updated',
event: 'User.Data.Updated',
method: 'patch',
endpoint: `users/{userId}/profile`,
payload: { profile: { nickname: 'darcy' } },
},
{
route: 'PATCH /users/:userId/password',
event: 'User.Updated',
event: 'User.Data.Updated',
method: 'patch',
endpoint: `users/{userId}/password`,
payload: { password: 'new-password' },
Expand All @@ -56,7 +56,7 @@ export const userDataHookTestCases: TestCase[] = [
export const roleDataHookTestCases: TestCase[] = [
{
route: 'PATCH /roles/:id',
event: 'Role.Updated',
event: 'Role.Data.Updated',
method: 'patch',
endpoint: `roles/{roleId}`,
payload: { name: 'new name' },
Expand Down Expand Up @@ -87,7 +87,7 @@ export const roleDataHookTestCases: TestCase[] = [
export const scopesDataHookTestCases: TestCase[] = [
{
route: 'PATCH /resources/:resourceId/scopes/:scopeId',
event: 'Scope.Updated',
event: 'Scope.Data.Updated',
method: 'patch',
endpoint: `resources/{resourceId}/scopes/{scopeId}`,
payload: { name: generateName() },
Expand All @@ -104,7 +104,7 @@ export const scopesDataHookTestCases: TestCase[] = [
export const organizationDataHookTestCases: TestCase[] = [
{
route: 'PATCH /organizations/:id',
event: 'Organization.Updated',
event: 'Organization.Data.Updated',
method: 'patch',
endpoint: `organizations/{organizationId}`,
payload: { description: 'new org description' },
Expand Down Expand Up @@ -142,7 +142,7 @@ export const organizationDataHookTestCases: TestCase[] = [
export const organizationScopeDataHookTestCases: TestCase[] = [
{
route: 'PATCH /organization-scopes/:id',
event: 'OrganizationScope.Updated',
event: 'OrganizationScope.Data.Updated',
method: 'patch',
endpoint: `organization-scopes/{organizationScopeId}`,
payload: { description: 'new org scope description' },
Expand All @@ -159,7 +159,7 @@ export const organizationScopeDataHookTestCases: TestCase[] = [
export const organizationRoleDataHookTestCases: TestCase[] = [
{
route: 'PATCH /organization-roles/:id',
event: 'OrganizationRole.Updated',
event: 'OrganizationRole.Data.Updated',
method: 'patch',
endpoint: `organization-roles/{organizationRoleId}`,
payload: { name: generateName() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type Page } from 'puppeteer';
export const expectToCreateWebhook = async (page: Page) => {
await expect(page).toClick('div[class$=main] div[class$=headline] > button');
await expect(page).toClick('span[class$=label]', { text: 'PostRegister' });
await expect(page).toClick('span[class$=label]', { text: 'User.Updated' });
await expect(page).toClick('span[class$=label]', { text: 'User.Data.Updated' });
await expect(page).toFill('input[name=name]', 'hook_name');
await expect(page).toFill('input[name=url]', 'https://localhost/webhook');
await expect(page).toClick('button[type=submit]');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { sql } from '@silverhand/slonik';

import type { AlterationScript } from '../lib/types/alteration.js';

enum DataHookSchema {
User = 'User',
Role = 'Role',
Scope = 'Scope',
Organization = 'Organization',
OrganizationRole = 'OrganizationRole',
OrganizationScope = 'OrganizationScope',
}

type OldSchemaUpdateEvent = `${DataHookSchema}.${'Updated'}`;
type NewSchemaUpdateEvent = `${DataHookSchema}.Data.Updated`;

const oldSchemaUpdateEvents = Object.freeze([
'User.Updated',
'Role.Updated',
'Scope.Updated',
'Organization.Updated',
'OrganizationRole.Updated',
'OrganizationScope.Updated',
] satisfies OldSchemaUpdateEvent[]);

const newSchemaUpdateEvents = Object.freeze([
'User.Data.Updated',
'Role.Data.Updated',
'Scope.Data.Updated',
'Organization.Data.Updated',
'OrganizationRole.Data.Updated',
'OrganizationScope.Data.Updated',
] as const satisfies NewSchemaUpdateEvent[]);

const updateMap: { [key in OldSchemaUpdateEvent]: NewSchemaUpdateEvent } = {
'User.Updated': 'User.Data.Updated',
'Role.Updated': 'Role.Data.Updated',
'Scope.Updated': 'Scope.Data.Updated',
'Organization.Updated': 'Organization.Data.Updated',
'OrganizationRole.Updated': 'OrganizationRole.Data.Updated',
'OrganizationScope.Updated': 'OrganizationScope.Data.Updated',
};

const reverseMap: { [key in NewSchemaUpdateEvent]: OldSchemaUpdateEvent } = {
'User.Data.Updated': 'User.Updated',
'Role.Data.Updated': 'Role.Updated',
'Scope.Data.Updated': 'Scope.Updated',
'Organization.Data.Updated': 'Organization.Updated',
'OrganizationRole.Data.Updated': 'OrganizationRole.Updated',
'OrganizationScope.Data.Updated': 'OrganizationScope.Updated',
};

// This alteration script filters all the hook's events jsonb column to replace all the old schema update events with the new schema update events.

const isOldSchemaUpdateEvent = (event: string): event is OldSchemaUpdateEvent =>
// eslint-disable-next-line no-restricted-syntax
oldSchemaUpdateEvents.includes(event as OldSchemaUpdateEvent);

const isNewSchemaUpdateEvent = (event: string): event is NewSchemaUpdateEvent =>
// eslint-disable-next-line no-restricted-syntax
newSchemaUpdateEvents.includes(event as NewSchemaUpdateEvent);

const alteration: AlterationScript = {
up: async (pool) => {
const { rows: hooks } = await pool.query<{ id: string; events: string[] }>(sql`
select id, events
from hooks
`);

const hooksToBeUpdate = hooks.filter(({ events }) => {
return oldSchemaUpdateEvents.some((oldEvent) => events.includes(oldEvent));
});

await Promise.all(
hooksToBeUpdate.map(async ({ id, events }) => {
const updateEvents = events.reduce<string[]>((accumulator, event) => {
if (isOldSchemaUpdateEvent(event)) {
return [...accumulator, updateMap[event]];
}
return [...accumulator, event];
}, []);

await pool.query(sql`
update hooks
set events = ${JSON.stringify(updateEvents)}
where id = ${id};
`);
})
);
},
down: async (pool) => {
const { rows: hooks } = await pool.query<{ id: string; events: string[] }>(sql`
select id, events
from hooks
`);

const hooksToBeUpdate = hooks.filter(({ events }) => {
return newSchemaUpdateEvents.some((newEvent) => events.includes(newEvent));
});

await Promise.all(
hooksToBeUpdate.map(async ({ id, events }) => {
const updateEvents = events.reduce<string[]>((accumulator, event) => {
if (isNewSchemaUpdateEvent(event)) {
return [...accumulator, reverseMap[event]];
}
return [...accumulator, event];
}, []);

await pool.query(sql`
update hooks
set events = ${JSON.stringify(updateEvents)}
where id = ${id};
`);
})
);
},
};

export default alteration;
Loading
Loading