Skip to content

Commit 4c2a91a

Browse files
authored
[EdgeDB] Create Field Region queries (#3052)
1 parent ae6c9fb commit 4c2a91a

File tree

6 files changed

+116
-55
lines changed

6 files changed

+116
-55
lines changed

src/components/field-region/dto/field-region.dto.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import { ObjectType } from '@nestjs/graphql';
22
import { keys as keysOf } from 'ts-transformer-keys';
3-
import { e } from '~/core/edgedb';
4-
import { RegisterResource } from '~/core/resources';
53
import {
64
DbUnique,
7-
ID,
85
NameField,
96
Resource,
107
Secured,
118
SecuredProperty,
129
SecuredPropertyList,
1310
SecuredProps,
1411
SecuredString,
15-
} from '../../../common';
12+
} from '~/common';
13+
import { e } from '~/core/edgedb';
14+
import { LinkTo, RegisterResource } from '~/core/resources';
1615

1716
@RegisterResource()
1817
@ObjectType({
@@ -27,9 +26,9 @@ export class FieldRegion extends Resource {
2726
@DbUnique()
2827
readonly name: SecuredString;
2928

30-
readonly fieldZone: Secured<ID>;
29+
readonly fieldZone: Secured<LinkTo<'FieldZone'>>;
3130

32-
readonly director: Secured<ID>;
31+
readonly director: Secured<LinkTo<'User'>>;
3332
}
3433

3534
@ObjectType({
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { PublicOf } from '../../common';
3+
import { ChangesOf } from '../../core/database/changes';
4+
import { e, RepoFor } from '../../core/edgedb';
5+
import { CreateFieldRegion, FieldRegion, UpdateFieldRegion } from './dto';
6+
import { FieldRegionRepository } from './field-region.repository';
7+
8+
@Injectable()
9+
export class FieldRegionEdgeDBRepository
10+
extends RepoFor(FieldRegion, {
11+
hydrate: (region) => ({
12+
...region['*'],
13+
director: true,
14+
fieldZone: true,
15+
}),
16+
}).customize((cls) => {
17+
return class extends cls {
18+
async create(input: CreateFieldRegion) {
19+
const created = e.insert(e.FieldRegion, {
20+
name: input.name,
21+
director: e.cast(e.User, e.cast(e.uuid, input.directorId)),
22+
fieldZone: e.cast(e.FieldZone, e.cast(e.uuid, input.fieldZoneId)),
23+
});
24+
const query = e.select(created, this.hydrate);
25+
return await this.db.run(query);
26+
}
27+
28+
async update(
29+
{ id }: Pick<FieldRegion, 'id'>,
30+
changes: ChangesOf<FieldRegion, UpdateFieldRegion>,
31+
) {
32+
const region = e.cast(e.FieldRegion, e.cast(e.uuid, id));
33+
const updated = e.update(region, () => ({
34+
set: {
35+
...(changes.name && { name: changes.name }),
36+
...(changes.directorId && {
37+
director: e.cast(e.User, e.cast(e.uuid, changes.directorId)),
38+
}),
39+
...(changes.fieldZoneId && {
40+
fieldZone: e.cast(
41+
e.FieldZone,
42+
e.cast(e.uuid, changes.fieldZoneId),
43+
),
44+
}),
45+
},
46+
}));
47+
const query = e.select(updated, this.hydrate);
48+
return await this.db.run(query);
49+
}
50+
};
51+
})
52+
implements PublicOf<FieldRegionRepository> {}

src/components/field-region/field-region.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { forwardRef, Module } from '@nestjs/common';
2+
import { splitDb } from '~/core';
23
import { AuthorizationModule } from '../authorization/authorization.module';
34
import { FieldZoneModule } from '../field-zone/field-zone.module';
45
import { UserModule } from '../user/user.module';
6+
import { FieldRegionEdgeDBRepository } from './field-region.edgedb.repository';
57
import { FieldRegionLoader } from './field-region.loader';
68
import { FieldRegionRepository } from './field-region.repository';
79
import { FieldRegionResolver } from './field-region.resolver';
@@ -16,7 +18,7 @@ import { FieldRegionService } from './field-region.service';
1618
providers: [
1719
FieldRegionResolver,
1820
FieldRegionService,
19-
FieldRegionRepository,
21+
splitDb(FieldRegionRepository, FieldRegionEdgeDBRepository),
2022
FieldRegionLoader,
2123
],
2224
exports: [FieldRegionService],

src/components/field-region/field-region.repository.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { Injectable } from '@nestjs/common';
22
import { node, Query, relation } from 'cypher-query-builder';
3+
import {
4+
DuplicateException,
5+
ID,
6+
SecuredList,
7+
ServerException,
8+
Session,
9+
UnsecuredDto,
10+
} from '~/common';
11+
import { DtoRepository } from '~/core';
312
import { ChangesOf } from '~/core/database/changes';
4-
import { ID, Session, UnsecuredDto } from '../../common';
5-
import { DtoRepository } from '../../core';
613
import {
714
ACTIVE,
815
createNode,
@@ -13,7 +20,7 @@ import {
1320
paginate,
1421
requestingUser,
1522
sorting,
16-
} from '../../core/database/query';
23+
} from '~/core/database/query';
1724
import {
1825
CreateFieldRegion,
1926
FieldRegion,
@@ -24,6 +31,13 @@ import {
2431
@Injectable()
2532
export class FieldRegionRepository extends DtoRepository(FieldRegion) {
2633
async create(input: CreateFieldRegion, session: Session) {
34+
if (!(await this.isUnique(input.name))) {
35+
throw new DuplicateException(
36+
'fieldRegion.name',
37+
'FieldRegion with this name already exists.',
38+
);
39+
}
40+
2741
const initialProps = {
2842
name: input.name,
2943
canDelete: true,
@@ -42,20 +56,31 @@ export class FieldRegionRepository extends DtoRepository(FieldRegion) {
4256
)
4357
.return<{ id: ID }>('node.id as id');
4458

45-
return await query.first();
59+
const result = await query.first();
60+
if (!result) {
61+
throw new ServerException('failed to create field region');
62+
}
63+
64+
return await this.readOne(result.id);
4665
}
4766

4867
async update(
49-
existing: FieldRegion,
68+
existing: Pick<FieldRegion, 'id'>,
5069
changes: ChangesOf<FieldRegion, UpdateFieldRegion>,
5170
) {
52-
const { directorId, ...simpleChanges } = changes;
71+
const { directorId, fieldZoneId, ...simpleChanges } = changes;
5372

5473
if (directorId) {
5574
// TODO update director - lol this was never implemented
5675
}
5776

77+
if (fieldZoneId) {
78+
// TODO update field zone - neither was this
79+
}
80+
5881
await this.updateProperties(existing, simpleChanges);
82+
83+
return await this.readOne(existing.id);
5984
}
6085

6186
protected hydrate() {
@@ -74,13 +99,16 @@ export class FieldRegionRepository extends DtoRepository(FieldRegion) {
7499
])
75100
.return<{ dto: UnsecuredDto<FieldRegion> }>(
76101
merge('props', {
77-
director: 'director.id',
78-
fieldZone: 'fieldZone.id',
102+
director: 'director { .id }',
103+
fieldZone: 'fieldZone { .id }',
79104
}).as('dto'),
80105
);
81106
}
82107

83108
async list({ filter, ...input }: FieldRegionListInput, session: Session) {
109+
if (!this.privileges.forUser(session).can('read')) {
110+
return SecuredList.Redacted;
111+
}
84112
const result = await this.db
85113
.query()
86114
.match(requestingUser(session))

src/components/field-region/field-region.resolver.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,17 @@ export class FieldRegionResolver {
6363
@Parent() fieldRegion: FieldRegion,
6464
@Loader(UserLoader) users: LoaderOf<UserLoader>,
6565
): Promise<SecuredUser> {
66-
return await mapSecuredValue(fieldRegion.director, (id) => users.load(id));
66+
return await mapSecuredValue(fieldRegion.director, ({ id }) =>
67+
users.load(id),
68+
);
6769
}
6870

6971
@ResolveField(() => SecuredFieldZone)
7072
async fieldZone(
7173
@Parent() fieldRegion: FieldRegion,
7274
@Loader(FieldZoneLoader) fieldZones: LoaderOf<FieldZoneLoader>,
7375
): Promise<SecuredFieldZone> {
74-
return await mapSecuredValue(fieldRegion.fieldZone, (id) =>
76+
return await mapSecuredValue(fieldRegion.fieldZone, ({ id }) =>
7577
fieldZones.load(id),
7678
);
7779
}
Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { Injectable } from '@nestjs/common';
22
import {
3-
DuplicateException,
43
ID,
54
ObjectView,
6-
SecuredList,
75
ServerException,
86
Session,
97
UnsecuredDto,
10-
} from '../../common';
11-
import { HandleIdLookup, ILogger, Logger } from '../../core';
12-
import { mapListResults } from '../../core/database/results';
8+
} from '~/common';
9+
import { HandleIdLookup, ILogger, Logger } from '~/core';
1310
import { Privileges } from '../authorization';
1411
import {
1512
CreateFieldRegion,
@@ -33,21 +30,8 @@ export class FieldRegionService {
3330
session: Session,
3431
): Promise<FieldRegion> {
3532
this.privileges.for(session, FieldRegion).verifyCan('create');
36-
if (!(await this.repo.isUnique(input.name))) {
37-
throw new DuplicateException(
38-
'fieldRegion.name',
39-
'FieldRegion with this name already exists.',
40-
);
41-
}
42-
43-
const result = await this.repo.create(input, session);
44-
45-
if (!result) {
46-
throw new ServerException('failed to create field region');
47-
}
48-
49-
this.logger.debug(`field region created`, { id: result.id });
50-
return await this.readOne(result.id, session);
33+
const dto = await this.repo.create(input, session);
34+
return this.secure(dto, session);
5135
}
5236

5337
@HandleIdLookup(FieldRegion)
@@ -62,36 +46,31 @@ export class FieldRegionService {
6246
});
6347

6448
const result = await this.repo.readOne(id);
65-
return await this.secure(result, session);
49+
return this.secure(result, session);
6650
}
6751

6852
async readMany(ids: readonly ID[], session: Session) {
6953
const fieldRegions = await this.repo.readMany(ids);
70-
return await Promise.all(
71-
fieldRegions.map((dto) => this.secure(dto, session)),
72-
);
54+
return fieldRegions.map((dto) => this.secure(dto, session));
7355
}
7456

75-
private async secure(
76-
dto: UnsecuredDto<FieldRegion>,
77-
session: Session,
78-
): Promise<FieldRegion> {
57+
private secure(dto: UnsecuredDto<FieldRegion>, session: Session) {
7958
return this.privileges.for(session, FieldRegion).secure(dto);
8059
}
8160

8261
async update(
8362
input: UpdateFieldRegion,
8463
session: Session,
8564
): Promise<FieldRegion> {
86-
const fieldRegion = await this.readOne(input.id, session);
65+
const fieldRegion = await this.repo.readOne(input.id);
66+
8767
const changes = this.repo.getActualChanges(fieldRegion, input);
8868
this.privileges
8969
.for(session, FieldRegion, fieldRegion)
9070
.verifyChanges(changes);
9171

92-
await this.repo.update(fieldRegion, changes);
93-
94-
return await this.readOne(input.id, session);
72+
const updated = await this.repo.update(fieldRegion, changes);
73+
return this.secure(updated, session);
9574
}
9675

9776
async delete(id: ID, session: Session): Promise<void> {
@@ -111,11 +90,10 @@ export class FieldRegionService {
11190
input: FieldRegionListInput,
11291
session: Session,
11392
): Promise<FieldRegionListOutput> {
114-
if (this.privileges.for(session, FieldRegion).can('read')) {
115-
const results = await this.repo.list(input, session);
116-
return await mapListResults(results, (dto) => this.secure(dto, session));
117-
} else {
118-
return SecuredList.Redacted;
119-
}
93+
const results = await this.repo.list(input, session);
94+
return {
95+
...results,
96+
items: results.items.map((dto) => this.secure(dto, session)),
97+
};
12098
}
12199
}

0 commit comments

Comments
 (0)