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

Merge pull request #2907 from logto-io/gao-log-5161-core-remove-deprecated-query-functions

refactor(core): remove deprecated query functions
This commit is contained in:
Gao Sun 2023-01-11 21:58:36 +08:00 committed by GitHub
commit a5467e265b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 300 additions and 353 deletions

View file

@ -50,8 +50,8 @@ export const createUserLibrary = (queries: Queries) => {
const {
pool,
roles: { findRolesByRoleNames, insertRoles, findRoleByRoleName },
users: { hasUser, hasUserWithEmail, hasUserWithId, hasUserWithPhone },
usersRoles: { insertUsersRoles },
users: { hasUser, hasUserWithEmail, hasUserWithId, hasUserWithPhone, findUsersByIds },
usersRoles: { insertUsersRoles, findUsersRolesByRoleId },
} = queries;
const generateUserId = async (retries = 500) =>
@ -140,9 +140,26 @@ export const createUserLibrary = (queries: Queries) => {
}
};
const findUsersByRoleName = async (roleName: string) => {
const role = await findRoleByRoleName(roleName);
if (!role) {
return [];
}
const usersRoles = await findUsersRolesByRoleId(role.id);
if (usersRoles.length === 0) {
return [];
}
return findUsersByIds(usersRoles.map(({ userId }) => userId));
};
return {
generateUserId,
insertUser,
checkIdentifierCollision,
findUsersByRoleName,
};
};

View file

@ -1,13 +1,13 @@
import { demoAppApplicationId } from '@logto/schemas';
import type { MiddlewareType } from 'koa';
import { findApplicationById } from '#src/queries/application.js';
import type Queries from '#src/tenants/Queries.js';
export default function koaCheckDemoApp<StateT, ContextT, ResponseBodyT>(
queries: Queries
): MiddlewareType<StateT, ContextT, ResponseBodyT> {
const { findApplicationById } = queries.applications;
export default function koaCheckDemoApp<StateT, ContextT, ResponseBodyT>(): MiddlewareType<
StateT,
ContextT,
ResponseBodyT
> {
return async (ctx, next) => {
try {
await findApplicationById(demoAppApplicationId);

View file

@ -3,6 +3,7 @@ import { createMockUtils } from '@logto/shared/esm';
import snakecaseKeys from 'snakecase-keys';
import { mockApplication } from '#src/__mocks__/index.js';
import { MockQueries } from '#src/test-utils/tenant.js';
import { getConstantClientMetadata } from './utils.js';
@ -10,19 +11,6 @@ const { jest } = import.meta;
const { mockEsm } = createMockUtils(jest);
mockEsm('#src/queries/application.js', () => ({
findApplicationById: jest.fn(async (): Promise<Application> => mockApplication),
}));
mockEsm('#src/queries/oidc-model-instance.js', () => ({
upsertInstance: jest.fn(),
findPayloadById: jest.fn(),
findPayloadByPayloadField: jest.fn(),
consumeInstanceById: jest.fn(),
destroyInstanceById: jest.fn(),
revokeInstanceByGrantId: jest.fn(),
}));
mockEsm(
'date-fns',
jest.fn(() => ({
@ -31,6 +19,15 @@ mockEsm(
);
const { default: postgresAdapter } = await import('./adapter.js');
const oidcModelInstances = {
upsertInstance: jest.fn(),
findPayloadById: jest.fn(),
findPayloadByPayloadField: jest.fn(),
consumeInstanceById: jest.fn(),
destroyInstanceById: jest.fn(),
revokeInstanceByGrantId: jest.fn(),
};
const {
consumeInstanceById,
destroyInstanceById,
@ -38,14 +35,19 @@ const {
findPayloadByPayloadField,
revokeInstanceByGrantId,
upsertInstance,
} = await import('#src/queries/oidc-model-instance.js');
} = oidcModelInstances;
const queries = new MockQueries({
applications: { findApplicationById: jest.fn(async (): Promise<Application> => mockApplication) },
oidcModelInstances,
});
const now = Date.now();
describe('postgres Adapter', () => {
it('Client Modal', async () => {
const rejectError = new Error('Not implemented');
const adapter = postgresAdapter('Client');
const adapter = postgresAdapter(queries, 'Client');
await expect(adapter.upsert('client', {}, 0)).rejects.toMatchError(rejectError);
await expect(adapter.findByUserCode('foo')).rejects.toMatchError(rejectError);
@ -82,7 +84,7 @@ describe('postgres Adapter', () => {
const id = 'fooId';
const grantId = 'grantId';
const expireAt = 60;
const adapter = postgresAdapter(modelName);
const adapter = postgresAdapter(queries, modelName);
await adapter.upsert(id, { uid, userCode }, expireAt);
expect(upsertInstance).toBeCalledWith({

View file

@ -8,15 +8,7 @@ import { errors } from 'oidc-provider';
import snakecaseKeys from 'snakecase-keys';
import envSet, { MountedApps } from '#src/env-set/index.js';
import { findApplicationById } from '#src/queries/application.js';
import {
consumeInstanceById,
destroyInstanceById,
findPayloadById,
findPayloadByPayloadField,
revokeInstanceByGrantId,
upsertInstance,
} from '#src/queries/oidc-model-instance.js';
import type Queries from '#src/tenants/Queries.js';
import { appendPath } from '#src/utils/url.js';
import { getConstantClientMetadata } from './utils.js';
@ -54,7 +46,22 @@ const buildDemoAppUris = (
return data;
};
export default function postgresAdapter(modelName: string): ReturnType<AdapterFactory> {
export default function postgresAdapter(
queries: Queries,
modelName: string
): ReturnType<AdapterFactory> {
const {
applications: { findApplicationById },
oidcModelInstances: {
consumeInstanceById,
destroyInstanceById,
findPayloadById,
findPayloadByPayloadField,
revokeInstanceByGrantId,
upsertInstance,
},
} = queries;
if (modelName === 'Client') {
const reject = async () => {
throw new Error('Not implemented');

View file

@ -45,7 +45,7 @@ export default function initOidc(queries: Queries): Provider {
} as const);
const oidc = new Provider(issuer, {
adapter: postgresAdapter,
adapter: postgresAdapter.bind(null, queries),
renderError: (_ctx, _out, error) => {
console.error(error);

View file

@ -9,7 +9,6 @@ import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { getTotalRowCountWithPool } from '#src/database/row-count.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(Applications);
@ -65,14 +64,3 @@ export const createApplicationQueries = (pool: CommonQueryMethods) => {
deleteApplicationById,
};
};
/** @deprecated Will be removed soon. Use createApplicationQueries() factory instead. */
export const {
findTotalNumberOfApplications,
findAllApplications,
findApplicationById,
insertApplication,
updateApplication,
updateApplicationById,
deleteApplicationById,
} = createApplicationQueries(envSet.pool);

View file

@ -6,7 +6,6 @@ import { sql } from 'slonik';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(Connectors);
@ -72,14 +71,3 @@ export const createConnectorQueries = (pool: CommonQueryMethods) => {
updateConnector,
};
};
/** @deprecated Will be removed soon. Use createConnectorQueries() factory instead. */
export const {
findAllConnectors,
findConnectorById,
countConnectorByConnectorId,
deleteConnectorById,
deleteConnectorByIds,
insertConnector,
updateConnector,
} = createConnectorQueries(envSet.pool);

View file

@ -5,7 +5,6 @@ import type { CommonQueryMethods } from 'slonik';
import { sql } from 'slonik';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(CustomPhrases);
@ -69,12 +68,3 @@ export const createCustomPhraseQueries = (pool: CommonQueryMethods) => {
deleteCustomPhraseByLanguageTag,
};
};
/** @deprecated Will be removed soon. Use createCustomPhraseQueries() factory instead. */
export const {
findAllCustomLanguageTags,
findAllCustomPhrases,
findCustomPhraseByLanguageTag,
upsertCustomPhrase,
deleteCustomPhraseByLanguageTag,
} = createCustomPhraseQueries(envSet.pool);

View file

@ -6,7 +6,6 @@ import { sql } from 'slonik';
import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import envSet from '#src/env-set/index.js';
const { table, fields } = convertToIdentifiers(Logs);
@ -88,13 +87,3 @@ export const createLogQueries = (pool: CommonQueryMethods) => {
countActiveUsersByTimeInterval,
};
};
/** @deprecated Will be removed soon. Use createLogQueries() factory instead. */
export const {
insertLog,
countLogs,
findLogs,
findLogById,
getDailyActiveUserCountsByTimeInterval,
countActiveUsersByTimeInterval,
} = createLogQueries(envSet.pool);

View file

@ -131,14 +131,3 @@ export const createOidcModelInstanceQueries = (pool: CommonQueryMethods) => {
revokeInstanceByUserId,
};
};
/** @deprecated Will be removed soon. Use createOidcModelInstanceQueries() factory instead. */
export const {
upsertInstance,
findPayloadById,
findPayloadByPayloadField,
consumeInstanceById,
destroyInstanceById,
revokeInstanceByGrantId,
revokeInstanceByUserId,
} = createOidcModelInstanceQueries(envSet.pool);

View file

@ -6,7 +6,6 @@ import type { CommonQueryMethods } from 'slonik';
import { sql } from 'slonik';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(Passcodes);
@ -82,14 +81,3 @@ export const createPasscodeQueries = (pool: CommonQueryMethods) => {
deletePasscodesByIds,
};
};
/** @deprecated Will be removed soon. Use createPasscodeQueries() factory instead. */
export const {
findUnconsumedPasscodeByJtiAndType,
findUnconsumedPasscodesByJtiAndType,
insertPasscode,
consumePasscode,
increasePasscodeTryCount,
deletePasscodeById,
deletePasscodesByIds,
} = createPasscodeQueries(envSet.pool);

View file

@ -9,7 +9,6 @@ import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { getTotalRowCountWithPool } from '#src/database/row-count.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(Resources);
@ -80,16 +79,3 @@ export const createResourceQueries = (pool: CommonQueryMethods) => {
deleteResourceById,
};
};
/** @deprecated Will be removed soon. Use createResourceQueries() factory instead. */
export const {
findTotalNumberOfResources,
findAllResources,
findResourceByIndicator,
findResourceById,
findResourcesByIds,
insertResource,
updateResource,
updateResourceById,
deleteResourceById,
} = createResourceQueries(envSet.pool);

View file

@ -4,7 +4,6 @@ import { convertToIdentifiers } from '@logto/shared';
import type { CommonQueryMethods } from 'slonik';
import { sql } from 'slonik';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
const { table, fields } = convertToIdentifiers(RolesScopes);
@ -39,7 +38,3 @@ export const createRolesScopesQueries = (pool: CommonQueryMethods) => {
return { insertRolesScopes, findRolesScopesByRoleId, deleteRolesScope };
};
/** @deprecated Will be removed soon. Use createRolesScopesQueries() factory instead. */
export const { insertRolesScopes, findRolesScopesByRoleId, deleteRolesScope } =
createRolesScopesQueries(envSet.pool);

View file

@ -8,7 +8,6 @@ import { sql } from 'slonik';
import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import type { Search } from '#src/utils/search.js';
import { buildConditionsFromSearch } from '#src/utils/search.js';
@ -117,18 +116,3 @@ export const createScopeQueries = (pool: CommonQueryMethods) => {
deleteScopeById,
};
};
/** @deprecated Will be removed soon. Use createScopeQueries() factory instead. */
export const {
findScopes,
countScopes,
findScopeByNameAndResourceId,
findScopesByResourceId,
findScopesByResourceIds,
findScopesByIds,
insertScope,
findScopeById,
updateScope,
updateScopeById,
deleteScopeById,
} = createScopeQueries(envSet.pool);

View file

@ -5,7 +5,6 @@ import type { CommonQueryMethods } from 'slonik';
import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
export const defaultSettingId = 'default';
@ -23,6 +22,3 @@ export const createSettingQueries = (pool: CommonQueryMethods) => {
return { getSetting, updateSetting };
};
/** @deprecated Will be removed soon. Use createSettingQueries() factory instead. */
export const { getSetting, updateSetting } = createSettingQueries(envSet.pool);

View file

@ -4,7 +4,6 @@ import type { CommonQueryMethods } from 'slonik';
import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import envSet from '#src/env-set/index.js';
const id = 'default';
@ -24,7 +23,3 @@ export const createSignInExperienceQueries = (pool: CommonQueryMethods) => {
return { updateDefaultSignInExperience, findDefaultSignInExperience };
};
/** @deprecated Will be removed soon. Use createSignInExperienceQueries() factory instead. */
export const { updateDefaultSignInExperience, findDefaultSignInExperience } =
createSignInExperienceQueries(envSet.pool);

View file

@ -11,8 +11,9 @@ import { DeletionError } from '#src/errors/SlonikError/index.js';
import type { Search } from '#src/utils/search.js';
import { buildConditionsFromSearch } from '#src/utils/search.js';
import { findRoleByRoleName, findRolesByRoleIds } from './roles.js';
import { findUsersRolesByRoleId, findUsersRolesByUserId } from './users-roles.js';
// TODO: @sijie remove this
import { findRolesByRoleIds } from './roles.js';
import { findUsersRolesByUserId } from './users-roles.js';
const { table, fields } = convertToIdentifiers(Users);
@ -215,22 +216,6 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
group by date(${fields.createdAt})
`);
const findUsersByRoleName = async (roleName: string) => {
const role = await findRoleByRoleName(roleName);
if (!role) {
return [];
}
const usersRoles = await findUsersRolesByRoleId(role.id);
if (usersRoles.length === 0) {
return [];
}
return findUsersByIds(usersRoles.map(({ userId }) => userId));
};
return {
findUserByUsername,
findUserByEmail,
@ -250,7 +235,6 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
deleteUserIdentity,
hasActiveUsers,
getDailyNewUserCountsByTimeInterval,
findUsersByRoleName,
};
};
@ -274,5 +258,4 @@ export const {
deleteUserIdentity,
hasActiveUsers,
getDailyNewUserCountsByTimeInterval,
findUsersByRoleName,
} = createUserQueries(envSet.pool);

View file

@ -1,29 +1,32 @@
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { pickDefault } from '@logto/shared/esm';
import { mockRole, mockUser } from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta;
const { mockEsmWithActual } = createMockUtils(jest);
const users = { findUserById: jest.fn() };
await mockEsmWithActual('#src/queries/user.js', () => ({
findUserById: jest.fn(),
}));
const { findRolesByRoleIds } = await mockEsmWithActual('#src/queries/roles.js', () => ({
const roles = {
findRolesByRoleIds: jest.fn(),
findRoleById: jest.fn(),
}));
const { findUsersRolesByUserId, insertUsersRoles, deleteUsersRolesByUserIdAndRoleId } =
await mockEsmWithActual('#src/queries/users-roles.js', () => ({
findUsersRolesByUserId: jest.fn(),
insertUsersRoles: jest.fn(),
deleteUsersRolesByUserIdAndRoleId: jest.fn(),
}));
};
const { findRolesByRoleIds } = roles;
const usersRoles = {
findUsersRolesByUserId: jest.fn(),
insertUsersRoles: jest.fn(),
deleteUsersRolesByUserIdAndRoleId: jest.fn(),
};
const { findUsersRolesByUserId, insertUsersRoles, deleteUsersRolesByUserIdAndRoleId } = usersRoles;
const tenantContext = new MockTenant(undefined, { usersRoles, users, roles });
const roleRoutes = await pickDefault(import('./admin-user-role.js'));
describe('user role routes', () => {
const roleRequester = createRequester({ authedRoutes: roleRoutes });
const roleRequester = createRequester({ authedRoutes: roleRoutes, tenantContext });
it('GET /users/:id/roles', async () => {
findUsersRolesByUserId.mockResolvedValueOnce([]);

View file

@ -2,20 +2,19 @@ import { object, string } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { findRolesByRoleIds, findRoleById } from '#src/queries/roles.js';
import { findUserById } from '#src/queries/user.js';
import {
deleteUsersRolesByUserIdAndRoleId,
findUsersRolesByUserId,
insertUsersRoles,
} from '#src/queries/users-roles.js';
import assertThat from '#src/utils/assert-that.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
export default function adminUserRoleRoutes<T extends AuthedRouter>(
...[router]: RouterInitArgs<T>
...[router, { queries }]: RouterInitArgs<T>
) {
const {
roles: { findRolesByRoleIds, findRoleById },
users: { findUserById },
usersRoles: { deleteUsersRolesByUserIdAndRoleId, findUsersRolesByUserId, insertUsersRoles },
} = queries;
router.get(
'/users/:userId/roles',
koaGuard({

View file

@ -30,12 +30,11 @@ export default function adminUserRoutes<T extends AuthedRouter>(
updateUserById,
hasUserWithEmail,
hasUserWithPhone,
findUsersByRoleName,
},
usersRoles: { deleteUsersRolesByUserIdAndRoleId, findUsersRolesByRoleId, insertUsersRoles },
} = queries;
const {
users: { checkIdentifierCollision, generateUserId, insertUser },
users: { checkIdentifierCollision, generateUserId, insertUser, findUsersByRoleName },
} = libraries;
router.get('/users', koaPagination(), async (ctx, next) => {

View file

@ -3,32 +3,12 @@ import { ApplicationType } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { mockApplication } from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
const { jest } = import.meta;
const { mockEsm, mockEsmWithActual } = createMockUtils(jest);
const { mockEsmWithActual } = createMockUtils(jest);
const { findApplicationById } = await mockEsmWithActual('#src/queries/application.js', () => ({
findTotalNumberOfApplications: jest.fn(async () => ({ count: 10 })),
findAllApplications: jest.fn(async () => [mockApplication]),
findApplicationById: jest.fn(async () => mockApplication),
deleteApplicationById: jest.fn(),
insertApplication: jest.fn(
async (body: CreateApplication): Promise<Application> => ({
...mockApplication,
...body,
oidcClientMetadata: {
...mockApplication.oidcClientMetadata,
...body.oidcClientMetadata,
},
})
),
updateApplicationById: jest.fn(
async (_, data: Partial<CreateApplication>): Promise<Application> => ({
...mockApplication,
...data,
})
),
}));
const findApplicationById = jest.fn(async () => mockApplication);
await mockEsmWithActual('@logto/core-kit', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping
@ -36,6 +16,31 @@ await mockEsmWithActual('@logto/core-kit', () => ({
generateStandardId: () => 'randomId',
}));
const tenantContext = new MockTenant(undefined, {
applications: {
findTotalNumberOfApplications: jest.fn(async () => ({ count: 10 })),
findAllApplications: jest.fn(async () => [mockApplication]),
findApplicationById,
deleteApplicationById: jest.fn(),
insertApplication: jest.fn(
async (body: CreateApplication): Promise<Application> => ({
...mockApplication,
...body,
oidcClientMetadata: {
...mockApplication.oidcClientMetadata,
...body.oidcClientMetadata,
},
})
),
updateApplicationById: jest.fn(
async (_, data: Partial<CreateApplication>): Promise<Application> => ({
...mockApplication,
...data,
})
),
},
});
const { createRequester } = await import('#src/utils/test-utils.js');
const applicationRoutes = await pickDefault(import('./application.js'));
@ -46,7 +51,7 @@ const customClientMetadata = {
};
describe('application route', () => {
const applicationRequest = createRequester({ authedRoutes: applicationRoutes });
const applicationRequest = createRequester({ authedRoutes: applicationRoutes, tenantContext });
it('GET /applications', async () => {
const response = await applicationRequest.get('/applications');

View file

@ -5,20 +5,23 @@ import { object, string } from 'zod';
import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js';
import { buildOidcClientMetadata } from '#src/oidc/utils.js';
import {
deleteApplicationById,
findApplicationById,
findAllApplications,
insertApplication,
updateApplicationById,
findTotalNumberOfApplications,
} from '#src/queries/application.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
const applicationId = buildIdGenerator(21);
export default function applicationRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function applicationRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const {
deleteApplicationById,
findApplicationById,
findAllApplications,
insertApplication,
updateApplicationById,
findTotalNumberOfApplications,
} = queries.applications;
router.get('/applications', koaPagination(), async (ctx, next) => {
const { limit, offset } = ctx.pagination;

View file

@ -5,6 +5,7 @@ import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { mockZhCnCustomPhrase, trTrTag, zhCnTag } from '#src/__mocks__/custom-phrase.js';
import { mockSignInExperience } from '#src/__mocks__/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta;
@ -16,12 +17,7 @@ const mockCustomPhrases: Record<string, CustomPhrase> = {
[mockLanguageTag]: mockPhrase,
};
const {
deleteCustomPhraseByLanguageTag,
findAllCustomPhrases,
findCustomPhraseByLanguageTag,
upsertCustomPhrase,
} = mockEsm('#src/queries/custom-phrase.js', () => ({
const customPhrases = {
deleteCustomPhraseByLanguageTag: jest.fn(async (languageTag: string) => {
if (!mockCustomPhrases[languageTag]) {
throw new RequestError({ code: 'entity.not_found', status: 404 });
@ -38,15 +34,15 @@ const {
return mockCustomPhrase;
}),
upsertCustomPhrase: jest.fn(async () => mockPhrase),
}));
};
const {
deleteCustomPhraseByLanguageTag,
findAllCustomPhrases,
findCustomPhraseByLanguageTag,
upsertCustomPhrase,
} = customPhrases;
const { isStrictlyPartial } = mockEsm('#src/utils/translation.js', () => ({
isStrictlyPartial: jest.fn(() => true),
}));
const mockFallbackLanguage = trTrTag;
mockEsm('#src/queries/sign-in-experience.js', () => ({
const signInExperiences = {
findDefaultSignInExperience: jest.fn(
async (): Promise<SignInExperience> => ({
...mockSignInExperience,
@ -56,10 +52,18 @@ mockEsm('#src/queries/sign-in-experience.js', () => ({
},
})
),
};
const { isStrictlyPartial } = mockEsm('#src/utils/translation.js', () => ({
isStrictlyPartial: jest.fn(() => true),
}));
const mockFallbackLanguage = trTrTag;
const tenantContext = new MockTenant(undefined, { customPhrases, signInExperiences });
const customPhraseRoutes = await pickDefault(import('./custom-phrase.js'));
const customPhraseRequest = createRequester({ authedRoutes: customPhraseRoutes });
const customPhraseRequest = createRequester({ authedRoutes: customPhraseRoutes, tenantContext });
describe('customPhraseRoutes', () => {
afterEach(() => {

View file

@ -7,13 +7,6 @@ import { object } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import {
deleteCustomPhraseByLanguageTag,
findAllCustomPhrases,
findCustomPhraseByLanguageTag,
upsertCustomPhrase,
} from '#src/queries/custom-phrase.js';
import { findDefaultSignInExperience } from '#src/queries/sign-in-experience.js';
import assertThat from '#src/utils/assert-that.js';
import { isStrictlyPartial } from '#src/utils/translation.js';
@ -24,7 +17,19 @@ const cleanDeepTranslation = (translation: Translation) =>
// eslint-disable-next-line no-restricted-syntax
cleanDeep(translation) as Translation;
export default function customPhraseRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function customPhraseRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const {
customPhrases: {
deleteCustomPhraseByLanguageTag,
findAllCustomPhrases,
findCustomPhraseByLanguageTag,
upsertCustomPhrase,
},
signInExperiences: { findDefaultSignInExperience },
} = queries;
router.get(
'/custom-phrases',
koaGuard({

View file

@ -1,24 +1,18 @@
// The FP version works better for `format()`
/* eslint-disable import/no-duplicates */
import { pickDefault } from '@logto/shared/esm';
import { createMockUtils } from '@logto/shared/esm';
import { endOfDay, subDays } from 'date-fns';
import { format } from 'date-fns/fp';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
/* eslint-enable import/no-duplicates */
const { jest } = import.meta;
const { mockEsm } = createMockUtils(jest);
const totalUserCount = 1000;
const formatToQueryDate = format('yyyy-MM-dd');
const { countUsers, getDailyNewUserCountsByTimeInterval } = mockEsm('#src/queries/user.js', () => ({
countUsers: jest.fn(async () => ({ count: totalUserCount })),
getDailyNewUserCountsByTimeInterval: jest.fn(async () => mockDailyNewUserCounts),
}));
const mockDailyNewUserCounts = [
{ date: '2022-05-01', count: 1 },
{ date: '2022-05-02', count: 2 },
@ -41,17 +35,23 @@ const mockDailyActiveUserCounts = [
const mockActiveUserCount = 1000;
const { getDailyActiveUserCountsByTimeInterval, countActiveUsersByTimeInterval } = mockEsm(
'#src/queries/log.js',
() => ({
getDailyActiveUserCountsByTimeInterval: jest.fn().mockResolvedValue(mockDailyActiveUserCounts),
countActiveUsersByTimeInterval: jest.fn().mockResolvedValue({ count: mockActiveUserCount }),
})
);
const users = {
countUsers: jest.fn(async () => ({ count: totalUserCount })),
getDailyNewUserCountsByTimeInterval: jest.fn(async () => mockDailyNewUserCounts),
};
const { countUsers, getDailyNewUserCountsByTimeInterval } = users;
const logs = {
getDailyActiveUserCountsByTimeInterval: jest.fn().mockResolvedValue(mockDailyActiveUserCounts),
countActiveUsersByTimeInterval: jest.fn().mockResolvedValue({ count: mockActiveUserCount }),
};
const { getDailyActiveUserCountsByTimeInterval, countActiveUsersByTimeInterval } = logs;
const tenantContext = new MockTenant(undefined, { logs, users });
const dashboardRoutes = await pickDefault(import('./dashboard.js'));
describe('dashboardRoutes', () => {
const logRequest = createRequester({ authedRoutes: dashboardRoutes });
const logRequest = createRequester({ authedRoutes: dashboardRoutes, tenantContext });
afterEach(() => {
jest.clearAllMocks();

View file

@ -3,11 +3,6 @@ import { endOfDay, format, subDays } from 'date-fns';
import { object, string } from 'zod';
import koaGuard from '#src/middleware/koa-guard.js';
import {
countActiveUsersByTimeInterval,
getDailyActiveUserCountsByTimeInterval,
} from '#src/queries/log.js';
import { countUsers, getDailyNewUserCountsByTimeInterval } from '#src/queries/user.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
@ -17,7 +12,14 @@ const indices = (length: number) => [...Array.from({ length }).keys()];
const getEndOfDayTimestamp = (date: Date | number) => endOfDay(date).valueOf();
export default function dashboardRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function dashboardRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const {
logs: { countActiveUsersByTimeInterval, getDailyActiveUserCountsByTimeInterval },
users: { countUsers, getDailyNewUserCountsByTimeInterval },
} = queries;
router.get('/dashboard/users/total', async (ctx, next) => {
const { count: totalUserCount } = await countUsers();
ctx.body = { totalUserCount };

View file

@ -1,27 +1,31 @@
import { LogResult } from '@logto/schemas';
import type { Log } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { pickDefault } from '@logto/shared/esm';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta;
const { mockEsm } = createMockUtils(jest);
const mockBody = { key: 'a', payload: { key: 'a', result: LogResult.Success }, createdAt: 123 };
const mockLog: Log = { id: '1', ...mockBody };
const mockLogs = [mockLog, { id: '2', ...mockBody }];
const { countLogs, findLogs, findLogById } = mockEsm('#src/queries/log.js', () => ({
const logs = {
countLogs: jest.fn().mockResolvedValue({
count: mockLogs.length,
}),
findLogs: jest.fn().mockResolvedValue(mockLogs),
findLogById: jest.fn().mockResolvedValue(mockLog),
}));
};
const { countLogs, findLogs, findLogById } = logs;
const logRoutes = await pickDefault(import('./log.js'));
describe('logRoutes', () => {
const logRequest = createRequester({ authedRoutes: logRoutes });
const logRequest = createRequester({
authedRoutes: logRoutes,
tenantContext: new MockTenant(undefined, { logs }),
});
afterEach(() => {
jest.clearAllMocks();

View file

@ -3,11 +3,14 @@ import { object, string } from 'zod';
import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js';
import { countLogs, findLogById, findLogs } from '#src/queries/log.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
export default function logRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function logRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const { countLogs, findLogById, findLogs } = queries.logs;
router.get(
'/logs',
koaPagination(),

View file

@ -1,68 +1,82 @@
import type { Role } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { pickDefault } from '@logto/shared/esm';
import { mockRole, mockScope, mockUser, mockResource } from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta;
const { mockEsm, mockEsmWithActual } = createMockUtils(jest);
const roles = {
findRoles: jest.fn(async (): Promise<Role[]> => [mockRole]),
countRoles: jest.fn(async () => ({ count: 10 })),
// eslint-disable-next-line @typescript-eslint/ban-types
findRoleByRoleName: jest.fn(async (): Promise<Role | null> => null),
insertRole: jest.fn(async (data) => ({
...data,
id: mockRole.id,
})),
deleteRoleById: jest.fn(),
findRoleById: jest.fn(),
updateRoleById: jest.fn(async (id, data) => ({
...mockRole,
...data,
})),
findRolesByRoleIds: jest.fn(),
};
const { findRoleByRoleName, findRoleById, deleteRoleById } = roles;
const { findRoleByRoleName, findRoleById, deleteRoleById } = mockEsm(
'#src/queries/roles.js',
() => ({
findRoles: jest.fn(async (): Promise<Role[]> => [mockRole]),
countRoles: jest.fn(async () => ({ count: 10 })),
findRoleByRoleName: jest.fn(async (): Promise<Role | undefined> => undefined),
insertRole: jest.fn(async (data) => ({
...data,
id: mockRole.id,
})),
deleteRoleById: jest.fn(),
findRoleById: jest.fn(),
updateRoleById: jest.fn(async (id, data) => ({
...mockRole,
...data,
})),
findRolesByRoleIds: jest.fn(),
})
);
const { findScopeById, findScopesByIds } = await mockEsmWithActual('#src/queries/scope.js', () => ({
const scopes = {
findScopeById: jest.fn(),
findScopesByIds: jest.fn(),
}));
await mockEsmWithActual('#src/queries/resource.js', () => ({
};
const { findScopeById, findScopesByIds } = scopes;
const resources = {
findResourcesByIds: jest.fn(async () => [mockResource]),
}));
const { insertRolesScopes, findRolesScopesByRoleId } = await mockEsmWithActual(
'#src/queries/roles-scopes.js',
() => ({
insertRolesScopes: jest.fn(),
findRolesScopesByRoleId: jest.fn(),
deleteRolesScope: jest.fn(),
})
);
const { findUsersByIds } = await mockEsmWithActual('#src/queries/user.js', () => ({
};
const rolesScopes = {
insertRolesScopes: jest.fn(),
findRolesScopesByRoleId: jest.fn(),
deleteRolesScope: jest.fn(),
};
const { insertRolesScopes, findRolesScopesByRoleId } = rolesScopes;
const users = {
findUsersByIds: jest.fn(),
findUserById: jest.fn(),
}));
};
const { findUsersByIds } = users;
const usersRoles = {
insertUsersRoles: jest.fn(),
countUsersRolesByRoleId: jest.fn(),
findUsersRolesByRoleId: jest.fn(),
findFirstUsersRolesByRoleIdAndUserIds: jest.fn(),
deleteUsersRolesByUserIdAndRoleId: jest.fn(),
};
const {
insertUsersRoles,
findUsersRolesByRoleId,
deleteUsersRolesByUserIdAndRoleId,
findFirstUsersRolesByRoleIdAndUserIds,
countUsersRolesByRoleId,
} = await mockEsmWithActual('#src/queries/users-roles.js', () => ({
insertUsersRoles: jest.fn(),
countUsersRolesByRoleId: jest.fn(),
findUsersRolesByRoleId: jest.fn(),
findFirstUsersRolesByRoleIdAndUserIds: jest.fn(),
deleteUsersRolesByUserIdAndRoleId: jest.fn(),
}));
} = usersRoles;
const roleRoutes = await pickDefault(import('./role.js'));
const tenantContext = new MockTenant(undefined, {
usersRoles,
users,
rolesScopes,
resources,
scopes,
roles,
});
describe('role routes', () => {
const roleRequester = createRequester({ authedRoutes: roleRoutes });
const roleRequester = createRequester({ authedRoutes: roleRoutes, tenantContext });
it('GET /roles?page=1', async () => {
countUsersRolesByRoleId.mockResolvedValueOnce({ count: 1 });

View file

@ -8,31 +8,6 @@ import { object, string, z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js';
import { findResourcesByIds } from '#src/queries/resource.js';
import {
deleteRolesScope,
findRolesScopesByRoleId,
insertRolesScopes,
} from '#src/queries/roles-scopes.js';
import {
countRoles,
deleteRoleById,
findRoleById,
findRoleByRoleName,
findRoles,
insertRole,
updateRoleById,
} from '#src/queries/roles.js';
import { findScopeById, findScopesByIds } from '#src/queries/scope.js';
import { findUserById, findUsersByIds } from '#src/queries/user.js';
import {
countUsersRolesByRoleId,
deleteUsersRolesByUserIdAndRoleId,
findFirstUsersRolesByRoleIdAndUserIds,
findUsersRolesByRoleId,
findUsersRolesByUserId,
insertUsersRoles,
} from '#src/queries/users-roles.js';
import assertThat from '#src/utils/assert-that.js';
import { parseSearchParamsForSearch } from '#src/utils/search.js';
@ -40,7 +15,33 @@ import type { AuthedRouter, RouterInitArgs } from './types.js';
const roleId = buildIdGenerator(21);
export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function roleRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const {
resources: { findResourcesByIds },
rolesScopes: { deleteRolesScope, findRolesScopesByRoleId, insertRolesScopes },
roles: {
countRoles,
deleteRoleById,
findRoleById,
findRoleByRoleName,
findRoles,
insertRole,
updateRoleById,
},
scopes: { findScopeById, findScopesByIds },
users: { findUserById, findUsersByIds },
usersRoles: {
countUsersRolesByRoleId,
deleteUsersRolesByUserIdAndRoleId,
findFirstUsersRolesByRoleIdAndUserIds,
findUsersRolesByRoleId,
findUsersRolesByUserId,
insertUsersRoles,
},
} = queries;
router.get('/roles', koaPagination({ isOptional: true }), async (ctx, next) => {
const { limit, offset, disabled } = ctx.pagination;
const { searchParams } = ctx.request.URL;

View file

@ -1,23 +1,25 @@
import type { Setting, CreateSetting } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { pickDefault } from '@logto/shared/esm';
import { mockSetting } from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js';
const { mockEsm } = createMockUtils(import.meta.jest);
mockEsm('#src/queries/setting.js', () => ({
const settings = {
getSetting: async (): Promise<Setting> => mockSetting,
updateSetting: async (data: Partial<CreateSetting>): Promise<Setting> => ({
...mockSetting,
...data,
}),
}));
};
const settingRoutes = await pickDefault(import('./setting.js'));
describe('settings routes', () => {
const roleRequester = createRequester({ authedRoutes: settingRoutes });
const roleRequester = createRequester({
authedRoutes: settingRoutes,
tenantContext: new MockTenant(undefined, { settings }),
});
it('GET /settings', async () => {
const response = await roleRequester.get('/settings');

View file

@ -1,11 +1,14 @@
import { Settings } from '@logto/schemas';
import koaGuard from '#src/middleware/koa-guard.js';
import { getSetting, updateSetting } from '#src/queries/setting.js';
import type { AuthedRouter, RouterInitArgs } from './types.js';
export default function settingRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) {
export default function settingRoutes<T extends AuthedRouter>(
...[router, { queries }]: RouterInitArgs<T>
) {
const { getSetting, updateSetting } = queries.settings;
router.get('/settings', async (ctx, next) => {
const { id, ...rest } = await getSetting();
ctx.body = rest;

View file

@ -73,7 +73,10 @@ export default class Tenant implements TenantContext {
app.use(
mount(
'/' + MountedApps.DemoApp,
compose([koaCheckDemoApp(), koaSpaProxy(MountedApps.DemoApp, 5003, MountedApps.DemoApp)])
compose([
koaCheckDemoApp(this.queries),
koaSpaProxy(MountedApps.DemoApp, 5003, MountedApps.DemoApp),
])
)
);