Skip to content

Commit 303e5a3

Browse files
authored
Merge pull request #3284 from SeedCompany/edgedb/polymorphic-queries-and-engagements
[EdgeDB] Polymorphic queries & Engagement Repo
2 parents c54e329 + 8919871 commit 303e5a3

21 files changed

+345
-83
lines changed

dbschema/engagement.esdl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module default {
2424
.<engagement[is Engagement::Ceremony]
2525
));
2626

27-
completedDate: cal::local_date {
27+
completeDate: cal::local_date {
2828
annotation description := "Translation / Growth Plan complete date";
2929
}
3030
disbursementCompleteDate: cal::local_date;
@@ -51,7 +51,20 @@ module default {
5151
property firstScripture := (
5252
exists .language.firstScriptureEngagement
5353
);
54-
54+
55+
trigger denyDuplicateFirstScriptureBasedOnExternal after insert, update for each do (
56+
assert(
57+
not __new__.firstScripture or not exists __new__.language.hasExternalFirstScripture,
58+
message := "First scripture has already been marked as having been done externally"
59+
)
60+
);
61+
trigger denyDuplicateFirstScriptureBasedOnOtherEngagement after insert, update for each do (
62+
assert(
63+
not exists (select __new__.language.engagements filter .firstScripture),
64+
message := "Another engagement has already been marked as having done the first scripture"
65+
)
66+
);
67+
5568
required lukePartnership: bool {
5669
default := false;
5770
};

dbschema/migrations/00005-m1yeyjr.edgeql

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"cypher-query-builder": "patch:cypher-query-builder@npm%3A6.0.4#~/.yarn/patches/cypher-query-builder-npm-6.0.4-e8707a5e8e.patch",
6363
"dotenv": "^16.3.1",
6464
"dotenv-expand": "^10.0.0",
65-
"edgedb": "^1.6.0-canary.20240506T235920",
65+
"edgedb": "^1.6.0-canary.20240827T111834",
6666
"execa": "^8.0.1",
6767
"express": "^4.18.2",
6868
"extensionless": "^1.7.0",
@@ -107,7 +107,7 @@
107107
"yaml": "^2.3.3"
108108
},
109109
"devDependencies": {
110-
"@edgedb/generate": "^0.6.0-canary.20240506T235941",
110+
"@edgedb/generate": "github:CarsonF/edgedb-js#workspace=@edgedb/generate&head=temp-host",
111111
"@nestjs/cli": "^10.2.1",
112112
"@nestjs/schematics": "^10.0.3",
113113
"@nestjs/testing": "^10.2.7",

src/common/id-field.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ import { applyDecorators } from '@nestjs/common';
22
import { Field, FieldOptions, ID as IDType } from '@nestjs/graphql';
33
import { ValidationOptions } from 'class-validator';
44
import { IsAny, IsNever, Tagged } from 'type-fest';
5-
import type {
6-
AllResourceDBNames,
7-
ResourceName,
8-
ResourceNameLike,
9-
} from '~/core';
5+
import type { ResourceName, ResourceNameLike } from '~/core';
106
import { IsId } from './validators';
117

128
export const IdField = ({
@@ -40,4 +36,4 @@ type IDTag<Kind> = IsAny<Kind> extends true
4036
: Kind
4137
: never;
4238

43-
type IDKindLike = ResourceNameLike | AllResourceDBNames | object;
39+
type IDKindLike = ResourceNameLike | object;

src/common/resource.dto.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { LazyGetter as Once } from 'lazy-get-decorator';
44
import { DateTime } from 'luxon';
55
import { keys as keysOf } from 'ts-transformer-keys';
66
import { inspect } from 'util';
7-
import type { ResourceDBMap, ResourceName } from '~/core';
7+
import type {
8+
ResourceDBMap,
9+
ResourceLike,
10+
ResourceName,
11+
ResourcesHost,
12+
} from '~/core';
813
import { $, e } from '~/core/edgedb/reexports';
914
import { ScopedRole } from '../components/authorization/dto';
1015
import { CalculatedSymbol } from './calculated.decorator';
@@ -27,7 +32,7 @@ const hasTypename = (value: unknown): value is { __typename: string } =>
2732
export const resolveByTypename =
2833
(interfaceName: string) => (value: unknown) => {
2934
if (hasTypename(value)) {
30-
return value.__typename;
35+
return EnhancedResource.resolve(value.__typename).name;
3136
}
3237
throw new ServerException(`Cannot resolve ${interfaceName} type`);
3338
};
@@ -87,13 +92,22 @@ export class EnhancedResource<T extends ResourceShape<any>> {
8792
static readonly dbTypes = new WeakMap<ResourceShape<any>, $.$expr_PathNode>();
8893
/** @internal */
8994
static readonly dbSkipAccessPolicies = new Set<string>();
95+
/** @internal */
96+
static resourcesHost?: ResourcesHost;
9097

9198
private constructor(readonly type: T) {}
9299
private static readonly refs = new WeakMap<
93100
ResourceShape<any>,
94101
EnhancedResource<any>
95102
>();
96103

104+
static resolve(ref: ResourceLike) {
105+
if (!EnhancedResource.resourcesHost) {
106+
throw new ServerException('Cannot resolve without ResourcesHost');
107+
}
108+
return EnhancedResource.resourcesHost.enhance(ref);
109+
}
110+
97111
static of<T extends ResourceShape<any>>(
98112
resource: T | EnhancedResource<T>,
99113
): EnhancedResource<T> {
@@ -305,7 +319,17 @@ export type DBType<TResourceStatic extends ResourceShape<any>> =
305319
: never
306320
: never;
307321

322+
/**
323+
* The name of the EdgeDB type, it could be abstract.
324+
*/
308325
export type DBName<T extends $.TypeSet> = T['__element__']['__name__'];
326+
/**
327+
* The name(s) of the concrete EdgeDB types.
328+
* If the type is abstract, then it is a string union of the concrete type's names.
329+
* If the type is concrete, then it is just the name, just as {@link DBName}.
330+
*/
331+
export type DBNames<T extends $.ObjectTypeSet> =
332+
T['__element__']['__polyTypenames__'];
309333

310334
export type MaybeUnsecuredInstance<TResourceStatic extends ResourceShape<any>> =
311335
MaybeSecured<InstanceType<TResourceStatic>>;

src/components/ceremony/handlers/create-engagement-default-ceremony.handler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { node, relation } from 'cypher-query-builder';
22
import { DateTime } from 'luxon';
33
import { ConfigService, EventsHandler, IEventHandler } from '~/core';
44
import { DatabaseService } from '~/core/database';
5+
import { LanguageEngagement } from '../../engagement/dto';
56
import { EngagementCreatedEvent } from '../../engagement/events';
67
import { CeremonyService } from '../ceremony.service';
78
import { CeremonyType } from '../dto';
@@ -20,7 +21,7 @@ export class CreateEngagementDefaultCeremonyHandler
2021
const { engagement } = event;
2122
const input = {
2223
type:
23-
engagement.__typename === 'LanguageEngagement'
24+
LanguageEngagement.resolve(engagement) === LanguageEngagement
2425
? CeremonyType.Dedication
2526
: CeremonyType.Certification,
2627
};

src/components/engagement/dto/engagement.dto.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
DateInterval,
88
DateTimeField,
99
DbLabel,
10+
DBNames,
1011
IntersectTypes,
1112
parentIdMiddleware,
1213
Resource,
@@ -47,7 +48,7 @@ export type AnyEngagement = MergeExclusive<
4748
const Interfaces = IntersectTypes(Resource, ChangesetAware);
4849

4950
export const resolveEngagementType = (val: Pick<AnyEngagement, '__typename'>) =>
50-
val.__typename === 'LanguageEngagement'
51+
val.__typename === 'default::LanguageEngagement'
5152
? LanguageEngagement
5253
: InternshipEngagement;
5354

@@ -63,8 +64,9 @@ class Engagement extends Interfaces {
6364
static readonly Props: string[] = keysOf<Engagement>();
6465
static readonly SecuredProps: string[] = keysOf<SecuredProps<Engagement>>();
6566
static readonly Parent = import('../../project/dto').then((m) => m.IProject);
67+
static readonly resolve = resolveEngagementType;
6668

67-
declare readonly __typename: 'LanguageEngagement' | 'InternshipEngagement';
69+
declare readonly __typename: DBNames<typeof e.Engagement>;
6870

6971
readonly project: LinkTo<'Project'> & Pick<IProject, 'status' | 'type'>;
7072

@@ -154,7 +156,7 @@ export class LanguageEngagement extends Engagement {
154156
(m) => m.TranslationProject,
155157
);
156158

157-
declare readonly __typename: 'LanguageEngagement';
159+
declare readonly __typename: DBNames<typeof e.LanguageEngagement>;
158160

159161
@Field(() => TranslationProject)
160162
declare readonly parent: BaseNode;
@@ -195,7 +197,7 @@ export class InternshipEngagement extends Engagement {
195197
(m) => m.InternshipProject,
196198
);
197199

198-
declare readonly __typename: 'InternshipEngagement';
200+
declare readonly __typename: DBNames<typeof e.InternshipEngagement>;
199201

200202
@Field(() => InternshipProject)
201203
declare readonly parent: BaseNode;
@@ -220,6 +222,11 @@ export class InternshipEngagement extends Engagement {
220222
export const engagementRange = (engagement: UnsecuredDto<Engagement>) =>
221223
DateInterval.tryFrom(engagement.startDate, engagement.endDate);
222224

225+
export const EngagementConcretes = {
226+
LanguageEngagement,
227+
InternshipEngagement,
228+
};
229+
223230
declare module '~/core/resources/map' {
224231
interface ResourceMap {
225232
Engagement: typeof Engagement;

0 commit comments

Comments
 (0)