Skip to content

Commit 6270495

Browse files
authored
Migrate to SC metadata discovery (#3546)
2 parents 373df4e + 7c13f77 commit 6270495

28 files changed

+218
-263
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@seedcompany/cache": "^3.0.2",
5151
"@seedcompany/common": ">=0.19.1 <1",
5252
"@seedcompany/data-loader": "^2.0.1",
53-
"@seedcompany/nest": "^1.8.0",
53+
"@seedcompany/nest": "^1.12.0",
5454
"@seedcompany/nestjs-email": "^4.3.0",
5555
"@seedcompany/scripture": ">=0.8.0",
5656
"argon2": "^0.43.0",

src/common/discovery-unique-methods.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { type DiscoveryService } from '@golevelup/nestjs-discovery';
2-
import { SetMetadata } from '@nestjs/common';
1+
import { createMetadataDecorator } from '@seedcompany/nest';
32
import { type EnhancedResource, type Many, type ResourceShape } from '~/common';
43
import { type ResourceGranter } from './resource-granter';
54

@@ -27,25 +26,12 @@ import { type ResourceGranter } from './resource-granter';
2726
* }
2827
* }
2928
*/
30-
export const Granter = (
31-
resources: GranterMetadata['resources'],
32-
factory?: GranterMetadata['factory'],
33-
): ClassDecorator =>
34-
SetMetadata<any, GranterMetadata>(GRANTER_FACTORY_METADATA_KEY, {
35-
resources,
36-
factory,
37-
});
38-
39-
export const discover = (discovery: DiscoveryService) =>
40-
discovery.providersWithMetaAtKey<GranterMetadata>(
41-
GRANTER_FACTORY_METADATA_KEY,
42-
);
43-
44-
const GRANTER_FACTORY_METADATA_KEY = Symbol('GranterFactory');
45-
46-
interface GranterMetadata {
47-
resources: Many<ResourceShape<any>>;
48-
factory?: <TResourceStatic extends ResourceShape<any>>(
49-
resource: EnhancedResource<TResourceStatic>,
50-
) => ResourceGranter<TResourceStatic>;
51-
}
29+
export const Granter = createMetadataDecorator({
30+
setter: (
31+
resources: Many<ResourceShape<any>>,
32+
factory?: <TResourceStatic extends ResourceShape<any>>(
33+
resource: EnhancedResource<TResourceStatic>,
34+
) => ResourceGranter<TResourceStatic>,
35+
) => ({ resources, factory }),
36+
types: ['class'],
37+
});
Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SetMetadata } from '@nestjs/common';
1+
import { createMetadataDecorator } from '@seedcompany/nest';
22
import type { ValueOf } from 'type-fest';
33
import { type Many, type Role } from '~/common';
44
import type { ResourcesGranter } from '../granters';
@@ -7,18 +7,13 @@ type ResourceGranterFn = (
77
resourcesGranter: ResourcesGranter,
88
) => Many<Many<ValueOf<ResourcesGranter>>>;
99

10-
export const POLICY_METADATA_KEY = Symbol('Policy');
11-
12-
export const Policy = (
13-
role: Many<Role> | 'all',
14-
privilegesForResources: ResourceGranterFn,
15-
): ClassDecorator =>
16-
SetMetadata<any, PolicyMetadata>(POLICY_METADATA_KEY, {
10+
export const Policy = createMetadataDecorator({
11+
setter: (
12+
role: Many<Role> | 'all',
13+
privilegesForResources: ResourceGranterFn,
14+
) => ({
1715
role,
1816
def: privilegesForResources,
19-
});
20-
21-
export interface PolicyMetadata {
22-
role: Many<Role> | 'all';
23-
def: ResourceGranterFn;
24-
}
17+
}),
18+
types: ['class'],
19+
});

src/components/authorization/policy/conditions/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ export * from './missing-context.exception';
33
export * from './aggregate.condition';
44
export * from './calculated.condition';
55
export * from './eql.util';
6-
export * from './optimizer.interface';
6+
export { Optimizer } from './optimizer.interface';
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import {
2-
applyDecorators,
3-
Injectable,
4-
SetMetadata,
5-
type Type,
6-
} from '@nestjs/common';
1+
import { Injectable, type Type } from '@nestjs/common';
2+
import { createMetadataDecorator } from '@seedcompany/nest';
73
import { type Condition } from './condition.interface';
84

5+
export const OptimizerWatermark = createMetadataDecorator({
6+
types: ['class'],
7+
additionalDecorators: [Injectable()],
8+
});
9+
910
export abstract class Optimizer {
10-
static register = () => (cls: Type<Optimizer>) =>
11-
applyDecorators(Injectable(), SetMetadata(Optimizer, true))(cls);
11+
static register =
12+
() =>
13+
<Cls extends Type<Optimizer>>(cls: Cls) =>
14+
OptimizerWatermark()(cls);
1215

1316
abstract optimize(condition: Condition): Condition;
1417
}

src/components/authorization/policy/executor/condition-optimizer.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
import { DiscoveryService } from '@golevelup/nestjs-discovery';
21
import { Injectable, type OnModuleInit } from '@nestjs/common';
2+
import { MetadataDiscovery } from '~/core/discovery';
33
import {
44
AggregateConditions,
55
AndConditions,
66
type Condition,
7-
Optimizer,
7+
type Optimizer,
88
OrConditions,
99
} from '../conditions';
10+
import { OptimizerWatermark } from '../conditions/optimizer.interface';
1011

1112
@Injectable()
1213
export class ConditionOptimizer implements OnModuleInit {
1314
private optimizers: Optimizer[];
1415

15-
constructor(private readonly discovery: DiscoveryService) {}
16+
constructor(private readonly discovery: MetadataDiscovery) {}
1617

1718
async onModuleInit() {
18-
const found = await this.discovery.providersWithMetaAtKey(Optimizer as any);
19-
this.optimizers = found.map((p) => p.discoveredClass.instance as Optimizer);
19+
this.optimizers = this.discovery
20+
.discover(OptimizerWatermark)
21+
.classes<Optimizer>()
22+
.map((discovery) => discovery.instance);
2023
}
2124

2225
optimize(condition: Condition) {

src/components/authorization/policy/granters.factory.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { DiscoveryService } from '@golevelup/nestjs-discovery';
2-
import { Injectable } from '@nestjs/common';
1+
import { Injectable, type Type } from '@nestjs/common';
32
import { many, mapEntries } from '@seedcompany/common';
43
import { mapValues } from 'lodash';
5-
import { EnhancedResource } from '~/common';
4+
import { EnhancedResource, type ResourceShape } from '~/common';
5+
import { MetadataDiscovery } from '~/core/discovery';
66
import { ResourcesHost } from '~/core/resources';
7-
import { discover } from './builder/granter.decorator';
7+
import { Granter } from './builder/granter.decorator';
88
import {
99
DefaultResourceGranter,
1010
ResourceGranter,
@@ -14,21 +14,23 @@ import { type ResourcesGranter } from './granters';
1414
@Injectable()
1515
export class GrantersFactory {
1616
constructor(
17-
private readonly discovery: DiscoveryService,
17+
private readonly discovery: MetadataDiscovery,
1818
private readonly resourcesHost: ResourcesHost,
1919
) {}
2020

2121
async makeGranters() {
22-
const discoveredGranters = await discover(this.discovery);
22+
const discoveredGranters = this.discovery
23+
.discover(Granter)
24+
.classes<ResourceGranter<ResourceShape<any>>>();
2325

2426
const custom = Object.assign(
2527
{},
2628
...discoveredGranters.map(
27-
({ meta: { resources, factory }, discoveredClass }) =>
29+
({ meta: { resources, factory }, instance }) =>
2830
mapEntries(many(resources), (raw) => {
2931
const res = EnhancedResource.of(raw);
30-
const granter =
31-
factory?.(res) ?? new discoveredClass.dependencyType(res);
32+
const GranterImpl = instance.constructor as Type<ResourcesGranter>;
33+
const granter = factory?.(res) ?? new GranterImpl(res);
3234
if (!(granter instanceof ResourceGranter)) {
3335
throw new Error(
3436
`Granter for ${res.name} must extend ResourceGranter class`,

src/components/authorization/policy/policy.factory.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import {
2-
type DiscoveredClassWithMeta,
3-
DiscoveryService,
4-
} from '@golevelup/nestjs-discovery';
51
import { Injectable, type OnModuleInit } from '@nestjs/common';
62
import { entries, mapEntries, mapValues, setOf } from '@seedcompany/common';
73
import { pick, startCase } from 'lodash';
84
import { type DeepWritable, type Writable } from 'ts-essentials';
95
import { type EnhancedResource, many, type Role } from '~/common';
6+
import { type DiscoveredClass, MetadataDiscovery } from '~/core/discovery';
107
import { ResourcesHost } from '~/core/resources';
118
import { Power } from '../dto';
129
import { ChildListAction, ChildSingleAction } from './actions';
@@ -15,10 +12,7 @@ import {
1512
type Permission,
1613
type Permissions,
1714
} from './builder/perm-granter';
18-
import {
19-
POLICY_METADATA_KEY,
20-
type PolicyMetadata,
21-
} from './builder/policy.decorator';
15+
import { Policy as PolicyMetadata } from './builder/policy.decorator';
2216
import { all, any, Condition } from './conditions';
2317
import { type ResourcesGranter } from './granters';
2418
import { GrantersFactory } from './granters.factory';
@@ -53,7 +47,7 @@ export class PolicyFactory implements OnModuleInit {
5347

5448
constructor(
5549
private readonly grantersFactory: GrantersFactory,
56-
private readonly discovery: DiscoveryService,
50+
private readonly discovery: MetadataDiscovery,
5751
private readonly resourcesHost: ResourcesHost,
5852
) {}
5953

@@ -72,10 +66,9 @@ export class PolicyFactory implements OnModuleInit {
7266
}
7367

7468
async onModuleInit() {
75-
const discoveredPolicies =
76-
await this.discovery.providersWithMetaAtKey<PolicyMetadata>(
77-
POLICY_METADATA_KEY,
78-
);
69+
const discoveredPolicies = this.discovery
70+
.discover(PolicyMetadata)
71+
.classes();
7972

8073
const resGranter = await this.grantersFactory.makeGranters();
8174

@@ -99,9 +92,9 @@ export class PolicyFactory implements OnModuleInit {
9992

10093
private buildPlainPolicy(
10194
resGranter: ResourcesGranter,
102-
{ meta, discoveredClass }: DiscoveredClassWithMeta<PolicyMetadata>,
95+
{ meta, instance }: DiscoveredClass<(typeof PolicyMetadata)['$value']>,
10396
): PlainPolicy {
104-
const name = startCase(discoveredClass.name.replace(/Policy$/, ''));
97+
const name = startCase(instance.constructor.name.replace(/Policy$/, ''));
10598

10699
const roles = meta.role === 'all' ? undefined : setOf(many(meta.role));
107100

src/components/notifications/notification.service.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { DiscoveryService } from '@golevelup/nestjs-discovery';
21
import {
32
forwardRef,
43
Inject,
@@ -13,6 +12,7 @@ import {
1312
ServerException,
1413
type UnsecuredDto,
1514
} from '~/common';
15+
import { MetadataDiscovery } from '~/core/discovery';
1616
import {
1717
type MarkNotificationReadArgs,
1818
type Notification,
@@ -62,7 +62,7 @@ export class NotificationServiceImpl
6262
>;
6363
readonly ready = new ((Event as any).default as typeof Event)();
6464

65-
constructor(private readonly discovery: DiscoveryService) {
65+
constructor(private readonly discovery: MetadataDiscovery) {
6666
super();
6767
}
6868

@@ -88,11 +88,8 @@ export class NotificationServiceImpl
8888
}
8989

9090
async onModuleInit() {
91-
const discovered = await this.discovery.providersWithMetaAtKey<
92-
ResourceShape<Notification>
93-
>(NotificationStrategy.KEY);
94-
this.strategyMap = mapEntries(discovered, ({ meta, discoveredClass }) => {
95-
const { instance } = discoveredClass;
91+
const discovered = this.discovery.discover(NotificationStrategy).classes();
92+
this.strategyMap = mapEntries(discovered, ({ meta, instance }) => {
9693
if (!(instance instanceof INotificationStrategy)) {
9794
throw new ServerException(
9895
`Strategy for ${meta.name} does not implement INotificationStrategy`,

0 commit comments

Comments
 (0)