-
-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: init management api hook middleware function
- Loading branch information
Showing
53 changed files
with
334 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@logto/schemas": minor | ||
--- | ||
|
||
add Management API hook types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { InteractionEvent, InteractionHookEvent, type ManagementHookEvent } from '@logto/schemas'; | ||
|
||
type ManagementHookContext = { | ||
event: ManagementHookEvent; | ||
data: unknown; | ||
}; | ||
|
||
type ManagementHookMetadata = { | ||
userAgent?: string; | ||
ip: string; | ||
}; | ||
|
||
/** | ||
* The class for managing Management API hook contexts. | ||
*/ | ||
export class ManagementHookContextManager { | ||
contextArray: ManagementHookContext[] = []; | ||
|
||
constructor(public metadata: ManagementHookMetadata) {} | ||
|
||
appendContext(context: ManagementHookContext) { | ||
// eslint-disable-next-line @silverhand/fp/no-mutating-methods | ||
this.contextArray.push(context); | ||
} | ||
} | ||
|
||
/** | ||
* The context for triggering interaction hooks by `triggerInteractionHooks`. | ||
* In the `koaInteractionHooks` middleware, | ||
* we will store the context before processing the interaction and consume it after the interaction is processed if needed. | ||
*/ | ||
export type InteractionHookContext = { | ||
event: InteractionEvent; | ||
sessionId?: string; | ||
applicationId?: string; | ||
userIp?: string; | ||
}; | ||
|
||
/** | ||
* The interaction hook result for triggering interaction hooks by `triggerInteractionHooks`. | ||
* In the `koaInteractionHooks` middleware, | ||
* if we get an interaction hook result after the interaction is processed, related hooks will be triggered. | ||
*/ | ||
export type InteractionHookResult = { | ||
userId: string; | ||
}; | ||
|
||
export const eventToHook: Record<InteractionEvent, InteractionHookEvent> = { | ||
[InteractionEvent.Register]: InteractionHookEvent.PostRegister, | ||
[InteractionEvent.SignIn]: InteractionHookEvent.PostSignIn, | ||
[InteractionEvent.ForgotPassword]: InteractionHookEvent.PostResetPassword, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
packages/core/src/middleware/koa-management-api-hooks.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { type ParameterizedContext } from 'koa'; | ||
|
||
import type Libraries from '#src/tenants/Libraries.js'; | ||
import { createContextWithRouteParameters } from '#src/utils/test-utils.js'; | ||
|
||
import { type WithHookContext, koaManagementApiHooks } from './koa-management-api-hooks.js'; | ||
|
||
const { jest } = import.meta; | ||
|
||
const notToBeCalled = () => { | ||
throw new Error('Should not be called'); | ||
}; | ||
|
||
describe('koaManagementApiHooks', () => { | ||
const next = jest.fn(); | ||
const triggerManagementHooks = jest.fn(); | ||
// @ts-expect-error mock | ||
const mockHooksLibrary: Libraries['hooks'] = { | ||
triggerManagementHooks, | ||
}; | ||
|
||
it("should do nothing if there's no hook context", async () => { | ||
const ctx = { | ||
...createContextWithRouteParameters(), | ||
header: {}, | ||
appendHookContext: notToBeCalled, | ||
}; | ||
await koaManagementApiHooks(mockHooksLibrary)(ctx, next); | ||
expect(triggerManagementHooks).not.toBeCalled(); | ||
}); | ||
|
||
it('should trigger management hooks', async () => { | ||
const ctx: ParameterizedContext<unknown, WithHookContext> = { | ||
...createContextWithRouteParameters(), | ||
header: {}, | ||
appendHookContext: notToBeCalled, | ||
}; | ||
next.mockImplementation(() => { | ||
ctx.appendHookContext({ event: 'Role.Created', data: { id: '123' } }); | ||
}); | ||
|
||
await koaManagementApiHooks(mockHooksLibrary)(ctx, next); | ||
expect(triggerManagementHooks).toBeCalledTimes(1); | ||
expect(triggerManagementHooks).toBeCalledWith( | ||
expect.objectContaining({ | ||
contextArray: [ | ||
{ | ||
event: 'Role.Created', | ||
data: { id: '123' }, | ||
}, | ||
], | ||
}) | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { trySafe } from '@silverhand/essentials'; | ||
import { type MiddlewareType } from 'koa'; | ||
import { type IRouterParamContext } from 'koa-router'; | ||
|
||
import { ManagementHookContextManager } from '#src/libraries/hook/types.js'; | ||
import type Libraries from '#src/tenants/Libraries.js'; | ||
|
||
export type WithHookContext<ContextT extends IRouterParamContext = IRouterParamContext> = | ||
ContextT & { appendHookContext: ManagementHookContextManager['appendContext'] }; | ||
|
||
/** | ||
* The factory to create a new management hook middleware function. | ||
* | ||
* To trigger management hooks, use `appendHookContext` to append the context. | ||
* | ||
* @param hooks The hooks library. | ||
* @returns The middleware function. | ||
*/ | ||
export const koaManagementApiHooks = <StateT, ContextT extends IRouterParamContext, ResponseT>( | ||
hooks: Libraries['hooks'] | ||
): MiddlewareType<StateT, WithHookContext<ContextT>, ResponseT> => { | ||
return async (ctx, next) => { | ||
const { | ||
header: { 'user-agent': userAgent }, | ||
ip, | ||
} = ctx; | ||
const managementHooks = new ManagementHookContextManager({ userAgent, ip }); | ||
|
||
/** | ||
* Append a hook context to trigger management hooks. If multiple contexts are appended, all of | ||
* them will be triggered. | ||
*/ | ||
ctx.appendHookContext = managementHooks.appendContext.bind(managementHooks); | ||
|
||
await next(); | ||
|
||
if (managementHooks.contextArray.length > 0) { | ||
// Hooks should not crash the app | ||
void trySafe(hooks.triggerManagementHooks(managementHooks)); | ||
} | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.