Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Repo Refactors / KnownLanguage Repo #2984

Merged
merged 3 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions src/components/user/known-language.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Injectable } from '@nestjs/common';
import { node, relation } from 'cypher-query-builder';
import { DateTime } from 'luxon';
import { ID } from '~/common';
import { DtoRepository } from '~/core';
import { ACTIVE } from '~/core/database/query';
import { KnownLanguage, ModifyKnownLanguageArgs } from './dto';

@Injectable()
export class KnownLanguageRepository extends DtoRepository(KnownLanguage) {
async create({
userId,
languageId,
languageProficiency,
}: ModifyKnownLanguageArgs) {
await this.delete({ userId, languageId, languageProficiency });

await this.db
.query()
.matchNode('user', 'User', { id: userId })
.matchNode('language', 'Language', { id: languageId })
.create([
node('user'),
relation('out', '', 'knownLanguage', {
active: true,
createdAt: DateTime.local(),
value: languageProficiency,
}),
node('language'),
])
.run();
}

async delete({
userId,
languageId,
languageProficiency,
}: ModifyKnownLanguageArgs) {
await this.db
.query()
.matchNode('user', 'User', { id: userId })
.matchNode('language', 'Language', { id: languageId })
.match([
[
node('user'),
relation('out', 'rel', 'knownLanguage', {
active: true,
value: languageProficiency,
}),
node('language'),
],
])
.setValues({
'rel.active': false,
})
.run();
}

async list(userId: ID) {
const results = await this.db
.query()
.match([
node('node', 'Language'),
relation('in', 'knownLanguageRel', 'knownLanguage', ACTIVE),
node('user', 'User', { id: userId }),
])
.with('collect(distinct user) as users, node, knownLanguageRel')
.raw(`unwind users as user`)
.return<KnownLanguage>([
'knownLanguageRel.value as proficiency',
'node.id as language',
])
.run();
return results;
}
}
31 changes: 0 additions & 31 deletions src/components/user/user.edgedb.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
DuplicateException,
ID,
NotFoundException,
Role,
ServerException,
Session,
} from '~/common';
Expand Down Expand Up @@ -99,36 +98,6 @@ export class UserEdgedbRepository extends UserRepository {
}
}

async updateEmail(
user: User,
email: string | null | undefined,
): Promise<void> {
const query = e.update(e.User, () => ({
filter_single: { id: user.id },
set: { email },
}));
try {
await this.edgedb.run(query);
} catch (e) {
if (isExclusivityViolation(e, 'email')) {
throw new DuplicateException(
'person.email',
'Email address is already in use',
e,
);
}
throw e;
}
}

async updateRoles(user: User, roles: Role[]): Promise<void> {
const query = e.update(e.User, () => ({
filter_single: { id: user.id },
set: { roles },
}));
await this.edgedb.run(query);
}

async delete(id: ID, _session: Session, _object: User): Promise<void> {
const query = e.delete(e.User, () => ({
filter_single: { id },
Expand Down
2 changes: 2 additions & 0 deletions src/components/user/user.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PartnerModule } from '../partner/partner.module';
import { TimeZoneModule } from '../timezone';
import { AssignableRolesResolver } from './assignable-roles.resolver';
import { EducationModule } from './education/education.module';
import { KnownLanguageRepository } from './known-language.repository';
import { KnownLanguageResolver } from './known-language.resolver';
import { UnavailabilityModule } from './unavailability/unavailability.module';
import { UserEdgedbRepository } from './user.edgedb.repository';
Expand Down Expand Up @@ -36,6 +37,7 @@ import { UserService } from './user.service';
UserLoader,
UserService,
splitDb(UserRepository, UserEdgedbRepository),
KnownLanguageRepository,
],
exports: [UserService, UserRepository, EducationModule, UnavailabilityModule],
})
Expand Down
68 changes: 2 additions & 66 deletions src/components/user/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import { Role } from '../authorization';
import {
AssignOrganizationToUser,
CreatePerson,
KnownLanguage,
LanguageProficiency,
RemoveOrganizationFromUser,
UpdateUser,
User,
Expand Down Expand Up @@ -123,7 +121,7 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
);
}

async updateEmail(
private async updateEmail(
user: User,
email: string | null | undefined,
): Promise<void> {
Expand Down Expand Up @@ -153,7 +151,7 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
}
}

async updateRoles(user: User, roles: Role[]): Promise<void> {
private async updateRoles(user: User, roles: Role[]): Promise<void> {
const removals = difference(user.roles.value, roles);
const additions = difference(roles, user.roles.value);

Expand Down Expand Up @@ -220,68 +218,6 @@ export class UserRepository extends DtoRepository<typeof User, [Session | ID]>(
return result!; // result from paginate() will always have 1 row.
}

async createKnownLanguage(
userId: ID,
languageId: ID,
languageProficiency: LanguageProficiency,
): Promise<void> {
await this.db
.query()
.matchNode('user', 'User', { id: userId })
.matchNode('language', 'Language', { id: languageId })
.create([
node('user'),
relation('out', '', 'knownLanguage', {
active: true,
createdAt: DateTime.local(),
value: languageProficiency,
}),
node('language'),
])
.run();
}

async deleteKnownLanguage(
userId: ID,
languageId: ID,
languageProficiency: LanguageProficiency,
): Promise<void> {
await this.db
.query()
.matchNode('user', 'User', { id: userId })
.matchNode('language', 'Language', { id: languageId })
.match([
[
node('user'),
relation('out', 'rel', 'knownLanguage', {
active: true,
value: languageProficiency,
}),
node('language'),
],
])
.setValues({
'rel.active': false,
})
.run();
}

async listKnownLanguages(userId: ID, _session: Session) {
const results = await this.db
.query()
.match([
node('node', 'Language'),
relation('in', 'knownLanguageRel', 'knownLanguage', ACTIVE),
node('user', 'User', { id: userId }),
])
.with('collect(distinct user) as users, node, knownLanguageRel')
.raw(`unwind users as user`)
.return(['knownLanguageRel.value as proficiency', 'node.id as language'])
.asResult<KnownLanguage>()
.run();
return results;
}

async doesEmailAddressExist(email: string) {
const result = await this.db
.query()
Expand Down
24 changes: 6 additions & 18 deletions src/components/user/user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,32 +295,20 @@ export class UserResolver {
})
async createKnownLanguage(
@LoggedInSession() session: Session,
@Args()
{ userId, languageId, languageProficiency }: ModifyKnownLanguageArgs,
@Args() args: ModifyKnownLanguageArgs,
): Promise<User> {
await this.userService.createKnownLanguage(
userId,
languageId,
languageProficiency,
session,
);
return await this.userService.readOne(userId, session);
await this.userService.createKnownLanguage(args);
return await this.userService.readOne(args.userId, session);
}

@Mutation(() => User, {
description: 'Delete known language from user',
})
async deleteKnownLanguage(
@LoggedInSession() session: Session,
@Args()
{ userId, languageId, languageProficiency }: ModifyKnownLanguageArgs,
@Args() args: ModifyKnownLanguageArgs,
): Promise<User> {
await this.userService.deleteKnownLanguage(
userId,
languageId,
languageProficiency,
session,
);
return await this.userService.readOne(userId, session);
await this.userService.deleteKnownLanguage(args);
return await this.userService.readOne(args.userId, session);
}
}
54 changes: 9 additions & 45 deletions src/components/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { property } from '../../core/database/query';
import { mapListResults } from '../../core/database/results';
import { Privileges, Role } from '../authorization';
import { AssignableRoles } from '../authorization/dto/assignable-roles';
import { LanguageService } from '../language';
import {
LocationListInput,
LocationService,
Expand All @@ -33,19 +32,19 @@ import {
import {
AssignOrganizationToUser,
CreatePerson,
KnownLanguage,
ModifyKnownLanguageArgs,
RemoveOrganizationFromUser,
UpdateUser,
User,
UserListInput,
UserListOutput,
} from './dto';
import { LanguageProficiency } from './dto/language-proficiency.enum';
import {
EducationListInput,
EducationService,
SecuredEducationList,
} from './education';
import { KnownLanguageRepository } from './known-language.repository';
import {
SecuredUnavailabilityList,
UnavailabilityListInput,
Expand All @@ -63,7 +62,7 @@ export class UserService {
private readonly unavailabilities: UnavailabilityService,
private readonly privileges: Privileges,
private readonly locationService: LocationService,
private readonly languageService: LanguageService,
private readonly knownLanguages: KnownLanguageRepository,
private readonly userRepo: UserRepository,
@Logger('user:service') private readonly logger: ILogger,
) {}
Expand Down Expand Up @@ -303,56 +302,21 @@ export class UserService {
);
}

async createKnownLanguage(
userId: ID,
languageId: ID,
languageProficiency: LanguageProficiency,
_session: Session,
): Promise<void> {
try {
await this.deleteKnownLanguage(
userId,
languageId,
languageProficiency,
_session,
);
await this.userRepo.createKnownLanguage(
userId,
languageId,
languageProficiency,
);
} catch (e) {
throw new ServerException('Could not create known language', e);
}
async createKnownLanguage(args: ModifyKnownLanguageArgs) {
await this.knownLanguages.create(args);
}

async deleteKnownLanguage(
userId: ID,
languageId: ID,
languageProficiency: LanguageProficiency,
_session: Session,
): Promise<void> {
try {
await this.userRepo.deleteKnownLanguage(
userId,
languageId,
languageProficiency,
);
} catch (e) {
throw new ServerException('Could not delete known language', e);
}
async deleteKnownLanguage(args: ModifyKnownLanguageArgs) {
await this.knownLanguages.delete(args);
}

async listKnownLanguages(
userId: ID,
session: Session,
): Promise<readonly KnownLanguage[]> {
async listKnownLanguages(userId: ID, session: Session) {
const user = await this.userRepo.readOne(userId, session);
const perms = this.privileges.for(session, User, user).all.knownLanguage;
if (!perms.read) {
return [];
}
return await this.userRepo.listKnownLanguages(userId, session);
return await this.knownLanguages.list(userId);
}

async checkEmail(email: string): Promise<boolean> {
Expand Down
Loading