Skip to content

Commit e630d92

Browse files
authored
Merge pull request #3413 from SeedCompany/no-ts-keys-of
2 parents b47812d + 18527ee commit e630d92

File tree

16 files changed

+115
-65
lines changed

16 files changed

+115
-65
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
"@nestjs/platform-fastify": "^11.1.0",
5050
"@patarapolw/prettyprint": "^1.0.3",
5151
"@seedcompany/cache": "^3.0.2",
52-
"@seedcompany/common": ">=0.17 <1",
52+
"@seedcompany/common": ">=0.19.1 <1",
5353
"@seedcompany/data-loader": "^2.0.1",
54-
"@seedcompany/nest": "^1.4.0",
54+
"@seedcompany/nest": "^1.6.1",
5555
"@seedcompany/nestjs-email": "^4.1.1",
5656
"@seedcompany/scripture": ">=0.8.0",
5757
"argon2": "^0.43.0",

src/common/lazy-ref.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export const lazyRef = <T extends object>(getter: () => T): T => {
2323
getPrototypeOf() {
2424
return Reflect.getPrototypeOf(getter());
2525
},
26+
getOwnPropertyDescriptor(target: T, p: string | symbol) {
27+
return Object.getOwnPropertyDescriptor(getter(), p);
28+
},
2629
});
2730
};
2831

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
1+
import { EnumType, makeEnum } from '@seedcompany/nest';
2+
13
/**
24
* Valid actions for resources
35
*/
4-
export type ResourceAction = 'read' | 'edit' | 'create' | 'delete';
6+
export type ResourceAction = EnumType<typeof ResourceAction>;
7+
export const ResourceAction = makeEnum(['read', 'edit', 'create', 'delete']);
58

69
/**
710
* Valid actions for properties
811
*/
9-
export type PropAction = 'read' | 'edit';
12+
export type PropAction = EnumType<typeof PropAction>;
13+
export const PropAction = makeEnum(['read', 'edit']);
1014

1115
/**
1216
* Valid actions for child relationships
1317
*/
14-
export type ChildRelationshipAction = 'read' | 'create' | 'delete';
18+
export type ChildRelationshipAction = EnumType<typeof ChildRelationshipAction>;
19+
export const ChildRelationshipAction = makeEnum(['read', 'create', 'delete']);
1520

1621
/**
1722
* Valid actions for child One-to-Many relationships
1823
*/
19-
export type ChildListAction = 'read' | 'create' | 'delete';
24+
export type ChildListAction = EnumType<typeof ChildListAction>;
25+
export const ChildListAction = makeEnum(['read', 'create', 'delete']);
2026

2127
/**
2228
* Valid actions for child One-to-One relationships
2329
*/
24-
export type ChildSingleAction = 'read' | 'edit';
30+
export type ChildSingleAction = EnumType<typeof ChildSingleAction>;
31+
export const ChildSingleAction = makeEnum(['read', 'edit']);
2532

2633
/**
2734
* Probably don't use directly
2835
* @internal
2936
*/
30-
export type AnyAction = ResourceAction | PropAction | ChildRelationshipAction;
37+
export type AnyAction = EnumType<typeof AnyAction>;
38+
export const AnyAction = makeEnum([
39+
...ResourceAction,
40+
...PropAction,
41+
...ChildRelationshipAction,
42+
]);

src/components/authorization/policy/executor/all-permissions-view.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mapValues } from '@seedcompany/common';
2+
import { EnumType, makeEnum } from '@seedcompany/nest';
23
import { startCase } from 'lodash';
3-
import { keys as keysOf } from 'ts-transformer-keys';
44
import { PascalCase } from 'type-fest';
55
import {
66
ChildListsKey,
@@ -40,7 +40,7 @@ export const createAllPermissionsView = <
4040
getKeys: () => [...resource.securedPropsPlusExtra, ...resource.childKeys],
4141
calculate: (propName) =>
4242
createLazyRecord<Record<CompatAction, boolean>>({
43-
getKeys: () => keysOf<Record<CompatAction, boolean>>(),
43+
getKeys: () => CompatAction.values,
4444
calculate: (actionInput, propPerms) => {
4545
const action =
4646
actionInput === 'canEdit' &&
@@ -74,22 +74,26 @@ export const createAllPermissionsOfEdgeView = <
7474
calculate: (action) => privileges.can(action),
7575
});
7676

77-
type CompatAction = AnyAction | `can${PascalCase<AnyAction>}`;
77+
const asLegacyAction = (action: AnyAction) =>
78+
`can${startCase(action)}` as `can${PascalCase<AnyAction>}`;
79+
80+
type CompatAction = EnumType<typeof CompatAction>;
81+
const CompatAction = makeEnum([
82+
...AnyAction,
83+
...[...AnyAction].map(asLegacyAction),
84+
]);
7885

7986
const compatMap = {
8087
forward: {
8188
...mapValues.fromList(
82-
keysOf<Record<CompatAction, boolean>>(),
89+
CompatAction,
8390
(action) =>
8491
(action.startsWith('can')
8592
? action.slice(3).toLowerCase()
8693
: action) as AnyAction,
8794
).asRecord,
8895
},
8996
backward: {
90-
...mapValues.fromList(
91-
keysOf<Record<AnyAction, boolean>>(),
92-
(action) => `can${startCase(action)}` as CompatAction,
93-
).asRecord,
97+
...mapValues.fromList(AnyAction, asLegacyAction).asRecord,
9498
},
9599
};

src/components/authorization/policy/executor/policy-dumper.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { Command, Option } from 'clipanion';
1717
import { startCase } from 'lodash';
1818
import { DateTime } from 'luxon';
1919
import fs from 'node:fs/promises';
20-
import { keys as keysOf } from 'ts-transformer-keys';
2120
import { LiteralUnion } from 'type-fest';
2221
import { inspect } from 'util';
2322
import xlsx from 'xlsx';
@@ -188,16 +187,14 @@ export class PolicyDumper {
188187
role,
189188
resource,
190189
edge: undefined,
191-
...mapValues.fromList(
192-
keysOf<Record<ResourceAction, boolean>>(),
193-
(action) => resolve(action),
194-
).asRecord,
190+
...mapValues.fromList(ResourceAction, (action) => resolve(action))
191+
.asRecord,
195192
},
196193
...(options.props !== false
197194
? ([
198-
[resource.securedPropsPlusExtra, keysOf<Record<PropAction, ''>>()],
199-
[resource.childSingleKeys, keysOf<Record<ChildSingleAction, ''>>()],
200-
[resource.childListKeys, keysOf<Record<ChildListAction, ''>>()],
195+
[resource.securedPropsPlusExtra, PropAction],
196+
[resource.childSingleKeys, ChildSingleAction],
197+
[resource.childListKeys, ChildListAction],
201198
] as const)
202199
: []
203200
).flatMap(([set, actions]) =>

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common';
66
import { entries, mapEntries, mapValues } from '@seedcompany/common';
77
import { pick, startCase } from 'lodash';
88
import { DeepWritable, Writable } from 'ts-essentials';
9-
import { keys as keysOf } from 'ts-transformer-keys';
109
import { EnhancedResource, many, Role } from '~/common';
1110
import { ResourcesHost } from '~/core/resources';
1211
import { Power } from '../dto';
@@ -314,13 +313,11 @@ export class PolicyFactory implements OnModuleInit {
314313
continue;
315314
}
316315

317-
const childActions = rel.list
318-
? keysOf<Record<ChildListAction, any>>()
319-
: keysOf<Record<ChildSingleAction, any>>();
316+
const childActions = rel.list ? ChildListAction : ChildSingleAction;
320317

321318
this.mergePermissions(
322319
(childRelations[rel.name] ??= {}),
323-
pick(grants.get(type)!.objectLevel, childActions),
320+
pick(grants.get(type)!.objectLevel, [...childActions]),
324321
);
325322
}
326323
}

src/components/engagement/dto/create-engagement.dto.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Field, InputType, ObjectType } from '@nestjs/graphql';
2+
import { entries } from '@seedcompany/common';
23
import { Type } from 'class-transformer';
34
import { ValidateNested } from 'class-validator';
45
import { stripIndent } from 'common-tags';
5-
import { keys as keysOf } from 'ts-transformer-keys';
6-
import { CalendarDate, DateField, ID, IdField } from '~/common';
6+
import { CalendarDate, DataObject, DateField, ID, IdField } from '~/common';
77
import { ChangesetIdField } from '../../changeset';
88
import { CreateDefinedFileVersionInput } from '../../file/dto';
99
import { LanguageMilestone } from '../../language/dto';
@@ -16,7 +16,7 @@ import { EngagementStatus } from './status.enum';
1616
@InputType({
1717
isAbstract: true,
1818
})
19-
export abstract class CreateEngagement {
19+
export abstract class CreateEngagement extends DataObject {
2020
@IdField()
2121
readonly projectId: ID;
2222

@@ -37,8 +37,11 @@ export abstract class CreateEngagement {
3737
}
3838

3939
@InputType()
40-
export abstract class CreateLanguageEngagement extends CreateEngagement {
41-
static readonly Props = keysOf<CreateLanguageEngagement>();
40+
export class CreateLanguageEngagement extends CreateEngagement {
41+
// Warning: this only works if not doing inheritance type mapping
42+
static readonly Props = entries(
43+
CreateLanguageEngagement.defaultValue(CreateLanguageEngagement),
44+
).map(([k]) => k);
4245

4346
@IdField()
4447
readonly languageId: ID;
@@ -81,8 +84,11 @@ export abstract class CreateLanguageEngagement extends CreateEngagement {
8184
}
8285

8386
@InputType()
84-
export abstract class CreateInternshipEngagement extends CreateEngagement {
85-
static readonly Props = keysOf<CreateInternshipEngagement>();
87+
export class CreateInternshipEngagement extends CreateEngagement {
88+
// Warning: this only works if not doing inheritance type mapping
89+
static readonly Props = entries(
90+
CreateInternshipEngagement.defaultValue(CreateInternshipEngagement),
91+
).map(([k]) => k);
8692

8793
@IdField()
8894
readonly internId: ID;

src/components/ethno-art/dto/ethno-art.dto.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import {
99
} from '~/common';
1010
import { e } from '~/core/gel';
1111
import { RegisterResource } from '~/core/resources';
12-
import { Producible } from '../../product/dto/producible.dto';
12+
import {
13+
Producible,
14+
ProducibleTypeEntries,
15+
} from '../../product/dto/producible.dto';
1316

17+
ProducibleTypeEntries.add('EthnoArt');
1418
declare module '../../product/dto/producible.dto' {
1519
interface ProducibleTypeEntries {
1620
EthnoArt: true;

src/components/film/dto/film.dto.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import {
99
} from '~/common';
1010
import { e } from '~/core/gel';
1111
import { RegisterResource } from '~/core/resources';
12-
import { Producible } from '../../product/dto/producible.dto';
12+
import {
13+
Producible,
14+
ProducibleTypeEntries,
15+
} from '../../product/dto/producible.dto';
1316

17+
ProducibleTypeEntries.add('Film');
1418
declare module '../../product/dto/producible.dto' {
1519
interface ProducibleTypeEntries {
1620
Film: true;

src/components/product/dto/producible.dto.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
import { Field, InterfaceType, ObjectType } from '@nestjs/graphql';
1+
import {
2+
Field,
3+
InterfaceType,
4+
ObjectType,
5+
registerEnumType,
6+
} from '@nestjs/graphql';
7+
import { MadeEnum } from '@seedcompany/nest';
28
import { stripIndent } from 'common-tags';
39
import { keys as keysOf } from 'ts-transformer-keys';
410
import {
511
EnumType,
12+
lazyRef,
613
makeEnum,
714
Resource,
815
SecuredProperty,
@@ -39,15 +46,21 @@ export abstract class Producible extends Resource {
3946
SetChangeType<'scriptureReferences', readonly ScriptureRangeInput[]>;
4047
}
4148

42-
export type ProducibleType = EnumType<typeof ProducibleType>;
43-
export const ProducibleType = makeEnum({
44-
name: 'ProducibleType',
45-
values: keysOf<ProducibleTypeEntries>(),
46-
});
47-
4849
// Augment this with each implementation of Producible via declaration merging
4950
// eslint-disable-next-line @typescript-eslint/no-empty-interface
5051
export interface ProducibleTypeEntries {}
52+
export const ProducibleTypeEntries = new Set<string>();
53+
54+
export type ProducibleType = EnumType<typeof ProducibleType>;
55+
export const ProducibleType = lazyRef(
56+
(): MadeEnum<keyof ProducibleTypeEntries> =>
57+
(realProducibleType ??= makeEnum({
58+
values: ProducibleTypeEntries as Iterable<keyof ProducibleTypeEntries>,
59+
})),
60+
);
61+
let realProducibleType: MadeEnum<keyof ProducibleTypeEntries> | undefined;
62+
// Register proxy eagerly to GQL schema
63+
registerEnumType(ProducibleType, { name: 'ProducibleType' });
5164

5265
export type ProducibleRef = UnsecuredDto<Producible> & {
5366
__typename: ProducibleType;

0 commit comments

Comments
 (0)