0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

refactor(core): refactor

This commit is contained in:
Darcy Ye 2024-03-06 19:31:42 +08:00
parent 0f0247ce2f
commit f4a812ae8a
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
7 changed files with 57 additions and 35 deletions

View file

@ -34,6 +34,7 @@ const logtoConfigs: LogtoConfigLibrary = {
resource: 'resource', resource: 'resource',
}), }),
getOidcConfigs: jest.fn(), getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(),
}; };
describe('getAccessToken()', () => { describe('getAccessToken()', () => {

View file

@ -1,10 +1,11 @@
import type { LogtoOidcConfigType } from '@logto/schemas';
import { import {
cloudApiIndicator, cloudApiIndicator,
cloudConnectionDataGuard, cloudConnectionDataGuard,
logtoOidcConfigGuard, logtoOidcConfigGuard,
LogtoOidcConfigKey, LogtoOidcConfigKey,
jwtCustomizerConfigGuard,
} from '@logto/schemas'; } from '@logto/schemas';
import type { LogtoOidcConfigType, LogtoJwtTokenKey } from '@logto/schemas';
import chalk from 'chalk'; import chalk from 'chalk';
import { z, ZodError } from 'zod'; import { z, ZodError } from 'zod';
@ -14,7 +15,11 @@ import { consoleLog } from '#src/utils/console.js';
export type LogtoConfigLibrary = ReturnType<typeof createLogtoConfigLibrary>; export type LogtoConfigLibrary = ReturnType<typeof createLogtoConfigLibrary>;
export const createLogtoConfigLibrary = ({ export const createLogtoConfigLibrary = ({
logtoConfigs: { getRowsByKeys, getCloudConnectionData: queryCloudConnectionData }, logtoConfigs: {
getRowsByKeys,
getCloudConnectionData: queryCloudConnectionData,
upsertJwtCustomizer: queryUpsertJwtCustomizer,
},
}: Pick<Queries, 'logtoConfigs'>) => { }: Pick<Queries, 'logtoConfigs'>) => {
const getOidcConfigs = async (): Promise<LogtoOidcConfigType> => { const getOidcConfigs = async (): Promise<LogtoOidcConfigType> => {
try { try {
@ -59,5 +64,18 @@ export const createLogtoConfigLibrary = ({
}; };
}; };
return { getOidcConfigs, getCloudConnectionData }; // Can not narrow down the type of value if we utilize `buildInsertIntoWithPool` method.
const upsertJwtCustomizer = async <T extends LogtoJwtTokenKey>(
key: T,
value: z.infer<(typeof jwtCustomizerConfigGuard)[T]>
) => {
const { value: rawValue } = await queryUpsertJwtCustomizer(key, value);
return {
key,
value: jwtCustomizerConfigGuard[key].parse(rawValue),
};
};
return { getOidcConfigs, getCloudConnectionData, upsertJwtCustomizer };
}; };

View file

@ -57,6 +57,7 @@ const cloudConnection = createCloudConnectionLibrary({
resource: 'resource', resource: 'resource',
}), }),
getOidcConfigs: jest.fn(), getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(),
}); });
const getLogtoConnectors = jest.spyOn(connectorLibrary, 'getLogtoConnectors'); const getLogtoConnectors = jest.spyOn(connectorLibrary, 'getLogtoConnectors');

View file

@ -92,27 +92,34 @@ export const isGuardMiddleware = <Type extends IMiddleware>(
): function_ is WithGuardConfig<Type> => ): function_ is WithGuardConfig<Type> =>
function_.name === 'guardMiddleware' && has(function_, 'config'); function_.name === 'guardMiddleware' && has(function_, 'config');
export function tryParse<Output, Definition extends ZodTypeDef, Input>( /**
* Previous `tryParse` function's output type was `Output | undefined`.
* It can not properly infer the output type to be `Output` even if the guard is provided,
* which brings additional but unnecessary type checks.
*/
export const parse = <Output, Definition extends ZodTypeDef, Input>(
type: 'query' | 'body' | 'params' | 'files', type: 'query' | 'body' | 'params' | 'files',
guard: ZodType<Output, Definition, Input>, guard: ZodType<Output, Definition, Input>,
data: unknown data: unknown
): Output; ) => {
export function tryParse<Output, Definition extends ZodTypeDef, Input>(
type: 'query' | 'body' | 'params' | 'files',
guard: undefined,
data: unknown
): undefined;
export function tryParse<Output, Definition extends ZodTypeDef, Input>(
type: 'query' | 'body' | 'params' | 'files',
guard: Optional<ZodType<Output, Definition, Input>>,
data: unknown
) {
try { try {
return guard?.parse(data); return guard.parse(data);
} catch (error: unknown) { } catch (error: unknown) {
throw new RequestError({ code: 'guard.invalid_input', type }, error); throw new RequestError({ code: 'guard.invalid_input', type }, error);
} }
} };
const tryParse = <Output, Definition extends ZodTypeDef, Input>(
type: 'query' | 'body' | 'params' | 'files',
guard: Optional<ZodType<Output, Definition, Input>>,
data: unknown
) => {
if (!guard) {
return;
}
return parse(type, guard, data);
};
export default function koaGuard< export default function koaGuard<
StateT, StateT,

View file

@ -8,7 +8,6 @@ import {
type LogtoOidcConfigKey, type LogtoOidcConfigKey,
type OidcConfigKey, type OidcConfigKey,
type LogtoJwtTokenKey, type LogtoJwtTokenKey,
type JwtCustomizerType,
} from '@logto/schemas'; } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared'; import { convertToIdentifiers } from '@logto/shared';
import type { CommonQueryMethods } from 'slonik'; import type { CommonQueryMethods } from 'slonik';
@ -57,7 +56,7 @@ export const createLogtoConfigQueries = (pool: CommonQueryMethods) => {
key: T, key: T,
value: z.infer<(typeof jwtCustomizerConfigGuard)[T]> value: z.infer<(typeof jwtCustomizerConfigGuard)[T]>
) => ) =>
pool.one<{ key: T; value: JwtCustomizerType[T] }>( pool.one<{ key: T; value: Record<string, string> }>(
sql` sql`
insert into ${table} (${fields.key}, ${fields.value}) insert into ${table} (${fields.key}, ${fields.value})
values (${key}, ${sql.jsonb(value)}) values (${key}, ${sql.jsonb(value)})

View file

@ -54,7 +54,7 @@ const logtoConfigQueries = {
}), }),
updateOidcConfigsByKey: jest.fn(), updateOidcConfigsByKey: jest.fn(),
getRowsByKeys: jest.fn(async () => mockLogtoConfigRows), getRowsByKeys: jest.fn(async () => mockLogtoConfigRows),
upsertJwtCustomizer: jest.fn(), // UpsertJwtCustomizer: jest.fn(),
}; };
const logtoConfigLibraries = { const logtoConfigLibraries = {
@ -62,6 +62,7 @@ const logtoConfigLibraries = {
[LogtoOidcConfigKey.PrivateKeys]: mockPrivateKeys, [LogtoOidcConfigKey.PrivateKeys]: mockPrivateKeys,
[LogtoOidcConfigKey.CookieKeys]: mockCookieKeys, [LogtoOidcConfigKey.CookieKeys]: mockCookieKeys,
})), })),
upsertJwtCustomizer: jest.fn(),
}; };
const settingRoutes = await pickDefault(import('./logto-config.js')); const settingRoutes = await pickDefault(import('./logto-config.js'));
@ -232,13 +233,13 @@ describe('configs routes', () => {
rows: [], rows: [],
rowCount: 0, rowCount: 0,
}); });
logtoConfigQueries.upsertJwtCustomizer.mockResolvedValueOnce( logtoConfigLibraries.upsertJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken mockJwtCustomizerConfigForAccessToken
); );
const response = await routeRequester const response = await routeRequester
.put(`/configs/jwt-customizer/access-token`) .put(`/configs/jwt-customizer/access-token`)
.send(mockJwtCustomizerConfigForAccessToken.value); .send(mockJwtCustomizerConfigForAccessToken.value);
expect(logtoConfigQueries.upsertJwtCustomizer).toHaveBeenCalledWith( expect(logtoConfigLibraries.upsertJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.AccessToken, LogtoJwtTokenKey.AccessToken,
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
@ -252,13 +253,13 @@ describe('configs routes', () => {
rows: [mockJwtCustomizerConfigForAccessToken], rows: [mockJwtCustomizerConfigForAccessToken],
rowCount: 1, rowCount: 1,
}); });
logtoConfigQueries.upsertJwtCustomizer.mockResolvedValueOnce( logtoConfigLibraries.upsertJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken mockJwtCustomizerConfigForAccessToken
); );
const response = await routeRequester const response = await routeRequester
.put('/configs/jwt-customizer/access-token') .put('/configs/jwt-customizer/access-token')
.send(mockJwtCustomizerConfigForAccessToken.value); .send(mockJwtCustomizerConfigForAccessToken.value);
expect(logtoConfigQueries.upsertJwtCustomizer).toHaveBeenCalledWith( expect(logtoConfigLibraries.upsertJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.AccessToken, LogtoJwtTokenKey.AccessToken,
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );

View file

@ -19,7 +19,7 @@ import {
import { z } from 'zod'; import { z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js'; import RequestError from '#src/errors/RequestError/index.js';
import koaGuard, { tryParse } from '#src/middleware/koa-guard.js'; import koaGuard, { parse } from '#src/middleware/koa-guard.js';
import { exportJWK } from '#src/utils/jwks.js'; import { exportJWK } from '#src/utils/jwks.js';
import type { AuthedRouter, RouterInitArgs } from './types.js'; import type { AuthedRouter, RouterInitArgs } from './types.js';
@ -41,12 +41,12 @@ const getJwtTokenKeyAndBody = (tokenPath: LogtoJwtTokenPath, body: unknown) => {
if (tokenPath === LogtoJwtTokenPath.AccessToken) { if (tokenPath === LogtoJwtTokenPath.AccessToken) {
return { return {
key: LogtoJwtTokenKey.AccessToken, key: LogtoJwtTokenKey.AccessToken,
body: tryParse('body', jwtCustomizerAccessTokenGuard, body), body: parse('body', jwtCustomizerAccessTokenGuard, body),
}; };
} }
return { return {
key: LogtoJwtTokenKey.ClientCredentials, key: LogtoJwtTokenKey.ClientCredentials,
body: tryParse('body', jwtCustomizerClientCredentialsGuard, body), body: parse('body', jwtCustomizerClientCredentialsGuard, body),
}; };
}; };
@ -81,14 +81,9 @@ const getRedactedOidcKeyResponse = async (
export default function logtoConfigRoutes<T extends AuthedRouter>( export default function logtoConfigRoutes<T extends AuthedRouter>(
...[router, { queries, logtoConfigs, invalidateCache }]: RouterInitArgs<T> ...[router, { queries, logtoConfigs, invalidateCache }]: RouterInitArgs<T>
) { ) {
const { const { getAdminConsoleConfig, getRowsByKeys, updateAdminConsoleConfig, updateOidcConfigsByKey } =
getAdminConsoleConfig, queries.logtoConfigs;
getRowsByKeys, const { getOidcConfigs, upsertJwtCustomizer } = logtoConfigs;
upsertJwtCustomizer,
updateAdminConsoleConfig,
updateOidcConfigsByKey,
} = queries.logtoConfigs;
const { getOidcConfigs } = logtoConfigs;
router.get( router.get(
'/configs/admin-console', '/configs/admin-console',