Skip to content

Commit 6743b08

Browse files
authored
Merge pull request #2986 from SeedCompany/edgedb/repos
2 parents 4894fe3 + 292205e commit 6743b08

File tree

10 files changed

+381
-82
lines changed

10 files changed

+381
-82
lines changed

src/components/project/project-member/project-member.repository.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ export class ProjectMemberRepository extends DtoRepository<
117117
node('user', 'User'),
118118
])
119119
.subQuery('user', (sub) =>
120-
sub.with('user as node').apply(this.users.hydrate(session.userId)),
120+
sub
121+
.with('user as node')
122+
.apply(this.users.hydrateAsNeo4j(session.userId)),
121123
)
122124
.return<{ dto: UnsecuredDto<ProjectMember> }>(
123125
merge('props', { user: 'dto' }).as('dto'),
Lines changed: 27 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,43 @@
11
import { Injectable } from '@nestjs/common';
2-
import { ID, NotFoundException, Session } from '~/common';
3-
import { e, EdgeDB } from '~/core/edgedb';
4-
import { CreatePerson, User, UserListInput } from './dto';
5-
import { UserRepository } from './user.repository';
6-
7-
const hydrate = e.shape(e.User, (user) => ({
8-
...user['*'],
9-
// Other links if needed
10-
}));
2+
import { NotImplementedException, PublicOf } from '~/common';
3+
import { e, RepoFor, ScopeOf } from '~/core/edgedb';
4+
import {
5+
AssignOrganizationToUser,
6+
RemoveOrganizationFromUser,
7+
User,
8+
UserListInput,
9+
} from './dto';
10+
import type { UserRepository } from './user.repository';
1111

1212
@Injectable()
13-
export class UserEdgedbRepository extends UserRepository {
14-
constructor(private readonly edgedb: EdgeDB) {
15-
super();
16-
}
17-
18-
async readOne(id: ID, _session: Session | ID) {
19-
const query = e.select(e.User, (user) => ({
20-
...hydrate(user),
21-
filter_single: { id },
22-
}));
23-
const user = await this.edgedb.run(query);
24-
if (!user) {
25-
throw new NotFoundException('Could not find user');
26-
}
27-
return user;
28-
}
29-
30-
async readMany(ids: readonly ID[], _session: Session | ID) {
31-
const query = e.params({ ids: e.array(e.uuid) }, ({ ids }) =>
32-
e.select(e.User, (user) => ({
33-
...hydrate(user),
34-
filter: e.op(user.id, 'in', e.array_unpack(ids)),
35-
})),
36-
);
37-
const users = await this.edgedb.run(query, { ids });
38-
return users;
39-
}
40-
13+
export class UserEdgeDBRepository
14+
extends RepoFor(User).withDefaults()
15+
implements PublicOf<UserRepository>
16+
{
4117
async doesEmailAddressExist(email: string) {
4218
const query = e.select(e.User, () => ({
4319
filter_single: { email },
4420
}));
45-
const result = await this.edgedb.run(query);
21+
const result = await this.db.run(query);
4622
return !!result;
4723
}
4824

49-
async list(input: UserListInput, _session: Session) {
50-
const sortKey = input.sort as keyof (typeof e.User)['*'];
51-
const all = e.select(e.User, (user) => ({
52-
filter: e.all(
53-
input.filter.pinned != null
54-
? e.op(user.pinned, '=', input.filter.pinned)
55-
: true,
56-
// More filters here when needed...
57-
),
58-
order_by: {
59-
expression: user[sortKey],
60-
direction: input.order,
61-
},
62-
}));
63-
const thisPage = e.select(all, () => ({
64-
offset: (input.page - 1) * input.count,
65-
limit: input.count + 1,
66-
}));
67-
const query = e.select({
68-
items: e.select(thisPage, (user) => ({
69-
...hydrate(user),
70-
limit: input.count,
71-
})),
72-
total: e.count(all),
73-
hasMore: e.op(e.count(thisPage), '>', input.count),
74-
});
75-
return await this.edgedb.run(query);
25+
protected listFilters(user: ScopeOf<typeof e.User>, input: UserListInput) {
26+
return [
27+
input.filter.pinned != null &&
28+
e.op(user.pinned, '=', input.filter.pinned),
29+
// More filters here when needed...
30+
];
7631
}
7732

78-
async create(input: CreatePerson) {
79-
const query = e.insert(e.User, { ...input });
80-
const result = await this.edgedb.run(query);
81-
return result.id;
33+
assignOrganizationToUser(args: AssignOrganizationToUser): Promise<void> {
34+
throw new NotImplementedException().with(args);
35+
}
36+
removeOrganizationFromUser(args: RemoveOrganizationFromUser): Promise<void> {
37+
throw new NotImplementedException().with(args);
8238
}
8339

84-
async delete(id: ID, _session: Session, _object: User): Promise<void> {
85-
const query = e.delete(e.User, () => ({
86-
filter_single: { id },
87-
}));
88-
await this.edgedb.run(query);
40+
hydrateAsNeo4j(_session: unknown): any {
41+
throw new NotImplementedException();
8942
}
9043
}

src/components/user/user.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { EducationModule } from './education/education.module';
1212
import { KnownLanguageRepository } from './known-language.repository';
1313
import { KnownLanguageResolver } from './known-language.resolver';
1414
import { UnavailabilityModule } from './unavailability/unavailability.module';
15-
import { UserEdgedbRepository } from './user.edgedb.repository';
15+
import { UserEdgeDBRepository } from './user.edgedb.repository';
1616
import { UserLoader } from './user.loader';
1717
import { UserRepository } from './user.repository';
1818
import { UserResolver } from './user.resolver';
@@ -36,7 +36,7 @@ import { UserService } from './user.service';
3636
AssignableRolesResolver,
3737
UserLoader,
3838
UserService,
39-
splitDb(UserRepository, UserEdgedbRepository),
39+
splitDb(UserRepository, UserEdgeDBRepository),
4040
KnownLanguageRepository,
4141
],
4242
exports: [UserService, UserRepository, EducationModule, UnavailabilityModule],

src/components/user/user.repository.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,13 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
8484
if (!result) {
8585
throw new ServerException('Failed to create user');
8686
}
87-
return result.id;
87+
return result;
8888
}
8989

90-
async update(existing: User, changes: ChangesOf<User, UpdateUser>) {
90+
async update(
91+
existing: User,
92+
changes: ChangesOf<User, UpdateUser>,
93+
): Promise<unknown> {
9194
const { roles, email, ...simpleChanges } = changes;
9295

9396
await this.updateProperties(existing, simpleChanges);
@@ -97,9 +100,11 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
97100
if (roles) {
98101
await this.updateRoles(existing, roles);
99102
}
103+
104+
return undefined;
100105
}
101106

102-
hydrate(requestingUserId: Session | ID) {
107+
protected hydrate(requestingUserId: Session | ID) {
103108
return (query: Query) =>
104109
query
105110
.subQuery('node', (sub) =>
@@ -342,4 +347,8 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
342347
await removePrimary.first();
343348
}
344349
}
350+
351+
hydrateAsNeo4j(session: Session | ID) {
352+
return this.hydrate(session);
353+
}
345354
}

src/components/user/user.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class UserService {
7979
this.verifyRolesAreAssignable(session, input.roles);
8080
}
8181

82-
const id = await this.userRepo.create(input);
82+
const { id } = await this.userRepo.create(input);
8383
return id;
8484
}
8585

src/core/database/dto.repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { DbTypeOf } from './db-type';
1818
import { OnIndex } from './indexer';
1919
import { matchProps } from './query';
2020

21-
export const privileges = Symbol('DtoRepository.privileges');
21+
export const privileges = Symbol.for('DtoRepository.privileges');
2222

2323
/**
2424
* A repository for a simple DTO. This provides a few methods out of the box.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Inject, Injectable } from '@nestjs/common';
2+
import { EnhancedResource, ID, isIdLike, PublicOf } from '~/common';
3+
import type { CommonRepository as Neo4jCommonRepository } from '~/core/database';
4+
import { ResourceLike, ResourcesHost } from '~/core/resources/resources.host';
5+
import type { BaseNode } from '../database/results';
6+
import { EdgeDB } from './edgedb.service';
7+
import { e } from './reexports';
8+
9+
/**
10+
* This provides a few methods out of the box.
11+
*/
12+
@Injectable()
13+
export class CommonRepository implements PublicOf<Neo4jCommonRepository> {
14+
@Inject(EdgeDB)
15+
protected readonly db: EdgeDB;
16+
@Inject(ResourcesHost)
17+
protected readonly resources: ResourcesHost;
18+
19+
/**
20+
* Here for compatibility with the Neo4j version.
21+
* @deprecated this should be replaced with a different output shape,
22+
* after we finish migration.
23+
*/
24+
async getBaseNode(id: ID, fqn?: ResourceLike): Promise<BaseNode | undefined> {
25+
const res = await this.getBaseNodes([id], fqn);
26+
return res[0];
27+
}
28+
29+
/**
30+
* Here for compatibility with the Neo4j version.
31+
* @deprecated this should be replaced with a different output shape,
32+
* after we finish migration.
33+
*/
34+
async getBaseNodes(
35+
ids: readonly ID[],
36+
fqn?: ResourceLike,
37+
): Promise<readonly BaseNode[]> {
38+
const res = fqn
39+
? typeof fqn === 'string'
40+
? await this.resources.getByEdgeDB(fqn)
41+
: EnhancedResource.of(fqn)
42+
: undefined;
43+
const query = e.params({ ids: e.array(e.uuid) }, ({ ids }) =>
44+
e.select((res?.db ?? e.Object) as typeof e.Object, (obj) => ({
45+
id: true,
46+
// eslint-disable-next-line @typescript-eslint/naming-convention
47+
_typeFQN_: obj.__type__.name,
48+
createdAt: obj.is(e.Mixin.Timestamped).createdAt,
49+
filter: e.op(obj.id, 'in', e.array_unpack(ids)),
50+
})),
51+
);
52+
const nodes = await this.db.run(query, { ids });
53+
54+
return await Promise.all(
55+
nodes.map(async (node): Promise<BaseNode> => {
56+
const res = await this.resources.getByEdgeDB(node._typeFQN_);
57+
return {
58+
identity: node.id,
59+
labels: [res.dbLabel],
60+
properties: {
61+
id: node.id,
62+
createdAt: node.createdAt,
63+
},
64+
};
65+
}),
66+
);
67+
}
68+
69+
async deleteNode(objectOrId: { id: ID } | ID, _changeset?: ID) {
70+
const id = isIdLike(objectOrId) ? objectOrId : objectOrId.id;
71+
const query = e.delete(e.Object, () => ({
72+
filter_single: { id },
73+
}));
74+
await this.db.run(query);
75+
}
76+
}

0 commit comments

Comments
 (0)