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:
commit
5beb7aa5ce
6 changed files with 74 additions and 54 deletions
|
@ -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": {
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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 }),
|
||||||
|
|
|
@ -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) {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue