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

Merge pull request #2887 from logto-io/gao-log-5128-core-library-factory-resource

refactor(core): migrate resource library to factory mode
This commit is contained in:
Gao Sun 2023-01-10 14:38:04 +08:00 committed by GitHub
commit 5beb7aa5ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 54 deletions

View file

@ -21,7 +21,7 @@
"start": "NODE_ENV=production node build/index.js", "start": "NODE_ENV=production node build/index.js",
"test:only": "NODE_OPTIONS=--experimental-vm-modules jest", "test:only": "NODE_OPTIONS=--experimental-vm-modules jest",
"test": "pnpm build:test && pnpm test:only", "test": "pnpm build:test && pnpm test:only",
"test:ci": "pnpm test:only --coverage --silent", "test:ci": "pnpm test:only --coverage --silent --maxWorkers=50%",
"test:report": "codecov -F core" "test:report": "codecov -F core"
}, },
"dependencies": { "dependencies": {

View file

@ -1,19 +1,19 @@
import { createMockUtils } from '@logto/shared/esm';
import { mockResource, mockResource2, mockResource3, mockScope } from '#src/__mocks__/index.js'; import { mockResource, mockResource2, mockResource3, mockScope } from '#src/__mocks__/index.js';
import { MockQueries } from '#src/test-utils/tenant.js';
const { jest } = import.meta; const { jest } = import.meta;
const { mockEsmWithActual } = createMockUtils(jest);
const { findScopesByResourceIds } = await mockEsmWithActual('#src/queries/scope.js', () => ({ const findScopesByResourceIds = jest.fn(async () => [
findScopesByResourceIds: jest.fn(async () => [ { ...mockScope, id: '1', resourceId: mockResource.id },
{ ...mockScope, id: '1', resourceId: mockResource.id }, { ...mockScope, id: '2', resourceId: mockResource.id },
{ ...mockScope, id: '2', resourceId: mockResource.id }, { ...mockScope, id: '3', resourceId: mockResource2.id },
{ ...mockScope, id: '3', resourceId: mockResource2.id }, ]);
]),
}));
const { attachScopesToResources } = await import('./resource.js'); const { createResourceLibrary } = await import('./resource.js');
const { attachScopesToResources } = createResourceLibrary(
new MockQueries({ scopes: { findScopesByResourceIds } })
);
describe('attachScopesToResources', () => { describe('attachScopesToResources', () => {
beforeEach(() => { beforeEach(() => {

View file

@ -1,15 +1,21 @@
import type { Resource, ResourceResponse } from '@logto/schemas'; import type { Resource, ResourceResponse } from '@logto/schemas';
import { findScopesByResourceIds } from '#src/queries/scope.js'; import type Queries from '#src/tenants/Queries.js';
export const attachScopesToResources = async ( export const createResourceLibrary = (queries: Queries) => {
resources: readonly Resource[] const { findScopesByResourceIds } = queries.scopes;
): Promise<ResourceResponse[]> => {
const resourceIds = resources.map(({ id }) => id);
const scopes = await findScopesByResourceIds(resourceIds);
return resources.map((resource) => ({ const attachScopesToResources = async (
...resource, resources: readonly Resource[]
scopes: scopes.filter(({ resourceId }) => resourceId === resource.id), ): Promise<ResourceResponse[]> => {
})); const resourceIds = resources.map(({ id }) => id);
const scopes = await findScopesByResourceIds(resourceIds);
return resources.map((resource) => ({
...resource,
scopes: scopes.filter(({ resourceId }) => resourceId === resource.id),
}));
};
return { attachScopesToResources };
}; };

View file

@ -2,21 +2,14 @@ import type { Resource, CreateResource } from '@logto/schemas';
import { pickDefault, createMockUtils } from '@logto/shared/esm'; import { pickDefault, createMockUtils } from '@logto/shared/esm';
import { mockResource, mockScope } from '#src/__mocks__/index.js'; import { mockResource, mockScope } from '#src/__mocks__/index.js';
import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js'; import { createRequester } from '#src/utils/test-utils.js';
const { jest } = import.meta; const { jest } = import.meta;
const { mockEsm, mockEsmWithActual } = createMockUtils(jest); const { mockEsm } = createMockUtils(jest);
await mockEsmWithActual('#src/libraries/resource.js', () => ({ const resources = {
attachScopesToResources: async (resources: Resource[]) =>
resources.map((resource) => ({
...resource,
scopes: [],
})),
}));
const { findResourceById } = mockEsm('#src/queries/resource.js', () => ({
findTotalNumberOfResources: async () => ({ count: 10 }), findTotalNumberOfResources: async () => ({ count: 10 }),
findAllResources: async (): Promise<Resource[]> => [mockResource], findAllResources: async (): Promise<Resource[]> => [mockResource],
findResourceById: jest.fn(async (): Promise<Resource> => mockResource), findResourceById: jest.fn(async (): Promise<Resource> => mockResource),
@ -30,26 +23,40 @@ const { findResourceById } = mockEsm('#src/queries/resource.js', () => ({
}), }),
deleteResourceById: jest.fn(), deleteResourceById: jest.fn(),
findScopesByResourceId: async () => [mockScope], findScopesByResourceId: async () => [mockScope],
})); };
const { findResourceById } = resources;
const { insertScope, updateScopeById } = await mockEsmWithActual('#src/queries/scope.js', () => ({ const scopes = {
findScopesByResourceId: async () => [mockScope], findScopesByResourceId: async () => [mockScope],
findScopes: async () => [mockScope], findScopes: async () => [mockScope],
insertScope: jest.fn(async () => mockScope), insertScope: jest.fn(async () => mockScope),
updateScopeById: jest.fn(async () => mockScope), updateScopeById: jest.fn(async () => mockScope),
deleteScopeById: jest.fn(), deleteScopeById: jest.fn(),
findScopeByNameAndResourceId: jest.fn(), findScopeByNameAndResourceId: jest.fn(),
})); };
const { insertScope, updateScopeById } = scopes;
const libraries = {
resources: {
attachScopesToResources: async (resources: readonly Resource[]) =>
resources.map((resource) => ({
...resource,
scopes: [],
})),
},
};
mockEsm('@logto/core-kit', () => ({ mockEsm('@logto/core-kit', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping // eslint-disable-next-line unicorn/consistent-function-scoping
buildIdGenerator: () => () => 'randomId', buildIdGenerator: () => () => 'randomId',
})); }));
const tenantContext = new MockTenant(undefined, { scopes, resources }, libraries);
const resourceRoutes = await pickDefault(import('./resource.js')); const resourceRoutes = await pickDefault(import('./resource.js'));
describe('resource routes', () => { describe('resource routes', () => {
const resourceRequest = createRequester({ authedRoutes: resourceRoutes }); const resourceRequest = createRequester({ authedRoutes: resourceRoutes, tenantContext });
it('GET /resources', async () => { it('GET /resources', async () => {
const response = await resourceRequest.get('/resources'); const response = await resourceRequest.get('/resources');

View file

@ -5,25 +5,8 @@ import { object, string } from 'zod';
import { isTrue } from '#src/env-set/parameters.js'; import { isTrue } from '#src/env-set/parameters.js';
import RequestError from '#src/errors/RequestError/index.js'; import RequestError from '#src/errors/RequestError/index.js';
import { attachScopesToResources } from '#src/libraries/resource.js';
import koaGuard from '#src/middleware/koa-guard.js'; import koaGuard from '#src/middleware/koa-guard.js';
import koaPagination from '#src/middleware/koa-pagination.js'; import koaPagination from '#src/middleware/koa-pagination.js';
import {
findTotalNumberOfResources,
findAllResources,
findResourceById,
insertResource,
updateResourceById,
deleteResourceById,
} from '#src/queries/resource.js';
import {
countScopes,
deleteScopeById,
findScopeByNameAndResourceId,
findScopes,
insertScope,
updateScopeById,
} from '#src/queries/scope.js';
import assertThat from '#src/utils/assert-that.js'; import assertThat from '#src/utils/assert-that.js';
import { parseSearchParamsForSearch } from '#src/utils/search.js'; import { parseSearchParamsForSearch } from '#src/utils/search.js';
@ -32,7 +15,29 @@ import type { AuthedRouter, RouterInitArgs } from './types.js';
const resourceId = buildIdGenerator(21); const resourceId = buildIdGenerator(21);
const scopeId = resourceId; const scopeId = resourceId;
export default function resourceRoutes<T extends AuthedRouter>(...[router]: RouterInitArgs<T>) { export default function resourceRoutes<T extends AuthedRouter>(
...[router, { queries, libraries }]: RouterInitArgs<T>
) {
const {
resources: {
findTotalNumberOfResources,
findAllResources,
findResourceById,
insertResource,
updateResourceById,
deleteResourceById,
},
scopes: {
countScopes,
deleteScopeById,
findScopes,
findScopeByNameAndResourceId,
insertScope,
updateScopeById,
},
} = queries;
const { attachScopesToResources } = libraries.resources;
router.get( router.get(
'/resources', '/resources',
koaPagination({ isOptional: true }), koaPagination({ isOptional: true }),

View file

@ -1,5 +1,6 @@
import { createConnectorLibrary } from '#src/libraries/connector.js'; import { createConnectorLibrary } from '#src/libraries/connector.js';
import { createPhraseLibrary } from '#src/libraries/phrase.js'; import { createPhraseLibrary } from '#src/libraries/phrase.js';
import { createResourceLibrary } from '#src/libraries/resource.js';
import { createSignInExperienceLibrary } from '#src/libraries/sign-in-experience/index.js'; import { createSignInExperienceLibrary } from '#src/libraries/sign-in-experience/index.js';
import { createUserLibrary } from '#src/libraries/user.js'; import { createUserLibrary } from '#src/libraries/user.js';
@ -10,6 +11,7 @@ export default class Libraries {
users = createUserLibrary(this.queries); users = createUserLibrary(this.queries);
signInExperiences = createSignInExperienceLibrary(this.queries, this.connectors); signInExperiences = createSignInExperienceLibrary(this.queries, this.connectors);
phrases = createPhraseLibrary(this.queries); phrases = createPhraseLibrary(this.queries);
resources = createResourceLibrary(this.queries);
constructor(public readonly queries: Queries) {} constructor(public readonly queries: Queries) {}
} }