Skip to content

Commit 5514f56

Browse files
authored
Merge pull request #3166 from SeedCompany/0940-EdgeDB-User-Unavailability-Queries
2 parents 08986c2 + 3367488 commit 5514f56

File tree

5 files changed

+110
-73
lines changed

5 files changed

+110
-73
lines changed

src/components/user/unavailability/dto/unavailability.dto.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { Field, 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
Resource,
75
SecuredDateTime,
86
SecuredProps,
97
SecuredString,
10-
} from '../../../../common';
8+
} from '~/common';
9+
import { e } from '~/core/edgedb';
10+
import { RegisterResource } from '~/core/resources';
1111

1212
@RegisterResource({ db: e.User.Unavailability })
1313
@ObjectType({
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { Range } from 'edgedb';
3+
import { ID, PublicOf } from '~/common';
4+
import { e, RepoFor } from '~/core/edgedb';
5+
import {
6+
CreateUnavailability,
7+
Unavailability,
8+
UpdateUnavailability,
9+
} from './dto';
10+
import { UnavailabilityRepository } from './unavailability.repository';
11+
12+
@Injectable()
13+
export class UnavailabilityEdgeDBRepository
14+
extends RepoFor(Unavailability, {
15+
hydrate: (unavailability) => ({
16+
...unavailability['*'],
17+
start: e.assert_exists(e.range_get_lower(unavailability.dates)),
18+
end: e.assert_exists(e.range_get_upper(unavailability.dates)),
19+
}),
20+
}).customize((cls) => {
21+
return class extends cls {
22+
async create(input: CreateUnavailability) {
23+
const user = e.cast(e.User, e.uuid(input.userId));
24+
const inserted = e.insert(e.User.Unavailability, {
25+
description: input.description,
26+
dates: new Range(input.start, input.end),
27+
});
28+
const updatedUser = e.update(user, () => ({
29+
set: {
30+
unavailabilities: { '+=': inserted },
31+
},
32+
}));
33+
const query = e.select(inserted, (u) => ({
34+
...this.hydrate(u),
35+
updatedUser, // Attach to query, so it is executed.
36+
}));
37+
return await this.db.run(query);
38+
}
39+
40+
async update(input: UpdateUnavailability) {
41+
const unavailability = e.cast(e.User.Unavailability, e.uuid(input.id));
42+
const updated = e.update(unavailability, () => ({
43+
set: {
44+
description: input.description,
45+
dates: e.cast(
46+
e.range(e.datetime),
47+
e.range(
48+
input.start ?? e.range_get_lower(unavailability.dates),
49+
input.end ?? e.range_get_upper(unavailability.dates),
50+
),
51+
),
52+
},
53+
}));
54+
const query = e.select(updated, this.hydrate);
55+
return await this.db.run(query);
56+
}
57+
};
58+
})
59+
implements PublicOf<UnavailabilityRepository>
60+
{
61+
async getUserIdByUnavailability(id: ID) {
62+
const unavailability = e.cast(e.User.Unavailability, e.uuid(id));
63+
const query = e.assert_exists(
64+
e.select(e.User, (user) => ({
65+
filter_single: e.op(unavailability, 'in', user.unavailabilities),
66+
})),
67+
);
68+
return await this.db.run(query);
69+
}
70+
}

src/components/user/unavailability/unavailability.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { forwardRef, Module } from '@nestjs/common';
2+
import { splitDb } from '~/core/database';
23
import { AuthorizationModule } from '../../authorization/authorization.module';
4+
import { UnavailabilityEdgeDBRepository } from './unavailability.edgedb.repository';
35
import { UnavailabilityLoader } from './unavailability.loader';
46
import { UnavailabilityRepository } from './unavailability.repository';
57
import { UnavailabilityResolver } from './unavailability.resolver';
@@ -10,7 +12,7 @@ import { UnavailabilityService } from './unavailability.service';
1012
providers: [
1113
UnavailabilityResolver,
1214
UnavailabilityService,
13-
UnavailabilityRepository,
15+
splitDb(UnavailabilityRepository, UnavailabilityEdgeDBRepository),
1416
UnavailabilityLoader,
1517
],
1618
exports: [UnavailabilityService],

src/components/user/unavailability/unavailability.repository.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { Injectable } from '@nestjs/common';
22
import { node, relation } from 'cypher-query-builder';
3-
import { ChangesOf } from '~/core/database/changes';
4-
import { ID, ServerException } from '../../../common';
5-
import { DtoRepository } from '../../../core';
3+
import { ID, NotFoundException, ServerException } from '~/common';
4+
import { DtoRepository } from '~/core';
65
import {
76
ACTIVE,
87
createNode,
98
createRelationships,
109
paginate,
1110
sorting,
12-
} from '../../../core/database/query';
11+
} from '~/core/database/query';
1312
import {
1413
CreateUnavailability,
1514
Unavailability,
@@ -38,18 +37,17 @@ export class UnavailabilityRepository extends DtoRepository(Unavailability) {
3837
if (!result) {
3938
throw new ServerException('Could not create unavailability');
4039
}
41-
return result.id;
40+
return await this.readOne(result.id);
4241
}
4342

44-
async update(
45-
existing: Unavailability,
46-
changes: ChangesOf<Unavailability, UpdateUnavailability>,
47-
) {
48-
return await this.updateProperties(existing, changes);
43+
async update(changes: UpdateUnavailability) {
44+
const { id, ...simpleChanges } = changes;
45+
await this.updateProperties({ id }, simpleChanges);
46+
return await this.readOne(id);
4947
}
5048

5149
async getUserIdByUnavailability(id: ID) {
52-
return await this.db
50+
const result = await this.db
5351
.query()
5452
.match([
5553
node('user', 'User'),
@@ -58,6 +56,14 @@ export class UnavailabilityRepository extends DtoRepository(Unavailability) {
5856
])
5957
.return<{ id: ID }>('user.id as id')
6058
.first();
59+
60+
if (!result) {
61+
throw new NotFoundException(
62+
'Could not find user associated with unavailability',
63+
'user.unavailability',
64+
);
65+
}
66+
return result;
6167
}
6268

6369
async list(input: UnavailabilityListInput) {
Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import { Injectable } from '@nestjs/common';
2-
import {
3-
ID,
4-
NotFoundException,
5-
ObjectView,
6-
ServerException,
7-
Session,
8-
UnsecuredDto,
9-
} from '../../../common';
10-
import { HandleIdLookup, ILogger, Logger } from '../../../core';
11-
import { mapListResults } from '../../../core/database/results';
2+
import { ID, ObjectView, Session, UnsecuredDto } from '~/common';
3+
import { HandleIdLookup } from '~/core';
124
import { Privileges } from '../../authorization';
135
import {
146
CreateUnavailability,
@@ -22,7 +14,6 @@ import { UnavailabilityRepository } from './unavailability.repository';
2214
@Injectable()
2315
export class UnavailabilityService {
2416
constructor(
25-
@Logger('unavailability:service') private readonly logger: ILogger,
2617
private readonly privileges: Privileges,
2718
private readonly repo: UnavailabilityRepository,
2819
) {}
@@ -31,24 +22,9 @@ export class UnavailabilityService {
3122
input: CreateUnavailability,
3223
session: Session,
3324
): Promise<Unavailability> {
34-
try {
35-
this.privileges.for(session, Unavailability).verifyCan('create');
36-
37-
// create and connect the Unavailability to the User.
38-
const id = await this.repo.create(input);
39-
40-
this.logger.debug(`Created user unavailability`, {
41-
id,
42-
userId: input.userId,
43-
});
44-
45-
return await this.readOne(id, session);
46-
} catch {
47-
this.logger.error(`Could not create unavailability`, {
48-
userId: input.userId,
49-
});
50-
throw new ServerException('Could not create unavailability');
51-
}
25+
this.privileges.for(session, Unavailability).verifyCan('create');
26+
const result = await this.repo.create(input);
27+
return this.secure(result, session);
5228
}
5329

5430
@HandleIdLookup(Unavailability)
@@ -58,57 +34,37 @@ export class UnavailabilityService {
5834
_view?: ObjectView,
5935
): Promise<Unavailability> {
6036
const result = await this.repo.readOne(id);
61-
return await this.secure(result, session);
37+
return this.secure(result, session);
6238
}
6339

6440
async readMany(ids: readonly ID[], session: Session) {
6541
const unavailabilities = await this.repo.readMany(ids);
66-
return await Promise.all(
67-
unavailabilities.map((dto) => this.secure(dto, session)),
68-
);
42+
return unavailabilities.map((dto) => this.secure(dto, session));
6943
}
7044

71-
private async secure(
72-
dto: UnsecuredDto<Unavailability>,
73-
session: Session,
74-
): Promise<Unavailability> {
45+
private secure(dto: UnsecuredDto<Unavailability>, session: Session) {
7546
return this.privileges.for(session, Unavailability).secure(dto);
7647
}
7748

7849
async update(
7950
input: UpdateUnavailability,
8051
session: Session,
8152
): Promise<Unavailability> {
82-
const unavailability = await this.readOne(input.id, session);
83-
53+
const unavailability = await this.repo.readOne(input.id);
8454
const result = await this.repo.getUserIdByUnavailability(input.id);
85-
if (!result) {
86-
throw new NotFoundException(
87-
'Could not find user associated with unavailability',
88-
'user.unavailability',
89-
);
90-
}
91-
9255
const changes = this.repo.getActualChanges(unavailability, input);
93-
9456
// TODO move this condition into policies
9557
if (result.id !== session.userId) {
9658
this.privileges
9759
.for(session, Unavailability, unavailability)
9860
.verifyChanges(changes);
9961
}
100-
return await this.repo.update(unavailability, changes);
62+
const updated = await this.repo.update({ id: input.id, ...changes });
63+
return this.secure(updated, session);
10164
}
10265

103-
async delete(id: ID, session: Session): Promise<void> {
104-
this.logger.debug(`mutation delete unavailability`);
105-
const ua = await this.readOne(id, session);
106-
if (!ua) {
107-
throw new NotFoundException(
108-
'Unavailability not found',
109-
'unavailability.id',
110-
);
111-
}
66+
async delete(id: ID, _session: Session): Promise<void> {
67+
const ua = await this.repo.readOne(id);
11268
await this.repo.deleteNode(ua);
11369
}
11470

@@ -117,6 +73,9 @@ export class UnavailabilityService {
11773
session: Session,
11874
): Promise<UnavailabilityListOutput> {
11975
const results = await this.repo.list(input);
120-
return await mapListResults(results, (dto) => this.secure(dto, session));
76+
return {
77+
...results,
78+
items: results.items.map((dto) => this.secure(dto, session)),
79+
};
12180
}
12281
}

0 commit comments

Comments
 (0)