0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(schemas): update hook schema (#3788)

This commit is contained in:
Xiao Yijun 2023-05-09 09:25:45 +08:00 committed by GitHub
parent 1066e5c707
commit 8fc5b78def
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 29 deletions

View file

@ -21,8 +21,12 @@ const url = 'https://logto.gg';
const hook: Hook = {
tenantId: 'bar',
id: 'foo',
name: 'hook_name',
event: HookEvent.PostSignIn,
config: { headers: { bar: 'baz' }, url, retries: 3 },
events: [HookEvent.PostSignIn],
signingKey: 'signing_key',
enabled: true,
config: { headers: { bar: 'baz' }, url },
createdAt: Date.now() / 1000,
};

View file

@ -78,7 +78,7 @@ export const createHookLibrary = (queries: Queries) => {
} satisfies Omit<HookEventPayload, 'hookId'>;
await Promise.all(
rows.map(async ({ config: { url, headers, retries }, id }) => {
rows.map(async ({ config: { url, headers }, id }) => {
consoleLog.info(`\tTriggering hook ${id} due to ${hookEvent} event`);
const json: HookEventPayload = { hookId: id, ...payload };
const logEntry = new LogEntry(`TriggerHook.${hookEvent}`);
@ -90,7 +90,7 @@ export const createHookLibrary = (queries: Queries) => {
.post(url, {
headers: { 'user-agent': 'Logto (https://logto.io)', ...headers },
json,
retry: { limit: retries },
retry: { limit: 3 },
timeout: { request: 10_000 },
})
.then(async (response) => {

View file

@ -13,7 +13,6 @@ const createPayload = (event: HookEvent, url = 'not_work_url'): Partial<Hook> =>
config: {
url,
headers: { foo: 'bar' },
retries: 3,
},
});

View file

@ -0,0 +1,105 @@
import { generateStandardId } from '@logto/shared';
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
enum HookEvent {
PostRegister = 'PostRegister',
PostSignIn = 'PostSignIn',
PostResetPassword = 'PostResetPassword',
}
type HookConfig = {
url: string;
headers?: Record<string, string>;
retries?: number;
};
type Hook = {
tenantId: string;
id: string;
name: string;
event: HookEvent | null;
events: HookEvent[];
config: HookConfig;
signingKey: string;
enabled: boolean;
createdAt: number;
};
const alteration: AlterationScript = {
up: async (pool) => {
await pool.query(sql`
alter table hooks
add column name varchar(256) not null default '',
add column events jsonb not null default '[]'::jsonb,
add column signing_key varchar(64) not null default '',
add column enabled boolean not null default true,
alter column event drop not null;
drop index hooks__event;
`);
},
down: async (pool) => {
await pool.query(sql`
delete from hooks where enabled = false;
`);
const { rows: hooks } = await pool.query<Hook>(sql`
select * from hooks;
`);
/* eslint-disable no-await-in-loop */
for (const { id, tenantId, events, config } of hooks) {
const { retries, ...rest } = config;
const updatedConfig = {
...rest,
retries: retries ?? 3,
};
if (events.length === 0) {
await pool.query(sql`
update hooks
set config = ${JSON.stringify(updatedConfig)}
where id = ${id} and tenant_id = ${tenantId};
`);
continue;
}
for (const [index, event] of events.entries()) {
if (index === 0) {
await pool.query(sql`
update hooks
set event = ${event},
config = ${JSON.stringify(updatedConfig)}
where id = ${id} and tenant_id = ${tenantId};
`);
continue;
}
// Create new hook when there are multiple events
const hookId = generateStandardId();
await pool.query(sql`
insert into hooks (id, tenant_id, event, config)
values (${hookId}, ${tenantId}, ${event}, ${JSON.stringify(updatedConfig)});
`);
}
}
/* eslint-enable no-await-in-loop */
await pool.query(sql`
alter table hooks
alter column event set not null,
drop column name,
drop column events,
drop column signing_key,
drop column enabled;
create index hooks__event on hooks (tenant_id, event);
`);
},
};
export default alteration;

View file

@ -200,23 +200,17 @@ export enum HookEvent {
export const hookEventGuard: z.ZodType<HookEvent> = z.nativeEnum(HookEvent);
export type HookConfig = {
export const hookEventsGuard = hookEventGuard.array();
export type HookEvents = z.infer<typeof hookEventsGuard>;
export const hookConfigGuard = z.object({
/** We don't need `type` since v1 only has web hook */
// type: 'web';
/** Method fixed to `POST` */
url: string;
/** Additional headers that attach to the request */
headers?: Record<string, string>;
/**
* Retry times when hook response status >= 500.
*
* Must be less than or equal to `3`. Use `0` to disable retry.
**/
retries: number;
};
export const hookConfigGuard: z.ZodType<HookConfig> = z.object({
url: z.string(),
/** Additional headers that attach to the request */
headers: z.record(z.string()).optional(),
retries: z.number().gte(0).lte(3),
});
export type HookConfig = z.infer<typeof hookConfigGuard>;

View file

@ -1,13 +1,15 @@
create table hooks (
tenant_id varchar(21) not null
references tenants (id) on update cascade on delete cascade,
id varchar(21) not null,
event varchar(128) /* @use HookEvent */ not null,
config jsonb /* @use HookConfig */ not null,
created_at timestamptz not null default(now()),
primary key (id)
);
tenant_id varchar(21) not null
references tenants (id) on update cascade on delete cascade,
id varchar(21) not null,
name varchar(256) not null default '',
event varchar(128) /* @use HookEvent */,
events jsonb /* @use HookEvents */ not null default '[]'::jsonb,
config jsonb /* @use HookConfig */ not null,
signing_key varchar(64) not null default '',
enabled boolean not null default true,
created_at timestamptz not null default(now()),
primary key (id)
);
create index hooks__id on hooks (tenant_id, id);
create index hooks__event on hooks (tenant_id, event);
create index hooks__id on hooks (tenant_id, id);