Skip to content

Commit

Permalink
BC-4663 - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
SevenWaysDP committed Sep 15, 2023
1 parent 718ccaf commit 5bbb8f8
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/server/src/modules/user/domain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './user';
78 changes: 78 additions & 0 deletions apps/server/src/modules/user/domain/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { EntityId, LanguageType } from '@shared/domain';
import { AuthorizableObject, DomainObject } from '@shared/domain/domain-object';

export interface UserProps extends AuthorizableObject {
createdAt?: Date;
updatedAt?: Date;
email: string;
firstName: string;
lastName: string;
roles: EntityId[];
schoolId: EntityId;
ldapDn?: string;
externalId?: string;
importHash?: string;
firstNameSearchValues?: string[];
lastNameSearchValues?: string[];
emailSearchValues?: string[];
language?: LanguageType;
forcePasswordChange?: boolean;
preferences?: Record<string, unknown>;
lastLoginSystemChange?: Date;
outdatedSince?: Date;
previousExternalId?: string;
}

export class User extends DomainObject<UserProps> {
get email(): string {
return this.props.email;
}

get firstName(): string {
return this.props.firstName;
}

get lastName(): string {
return this.props.lastName;
}

get schoolId(): string {
return this.props.schoolId;
}

get roles(): EntityId[] {
return this.props.roles;
}

get ldapDn(): string | undefined {
return this.props.ldapDn;
}

get externalId(): string | undefined {
return this.props.externalId;
}

get language(): LanguageType | undefined {
return this.props.language;
}

get forcePasswordChange(): boolean | undefined {
return this.props.forcePasswordChange;
}

get preferences(): Record<string, unknown> | undefined {
return this.props.preferences;
}

get lastLoginSystemChange(): Date | undefined {
return this.props.lastLoginSystemChange;
}

get outdatedSince(): Date | undefined {
return this.props.outdatedSince;
}

get previousExternalId(): string | undefined {
return this.props.previousExternalId;
}
}
2 changes: 2 additions & 0 deletions apps/server/src/modules/user/repo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './mapper';
export * from './user.repo';
1 change: 1 addition & 0 deletions apps/server/src/modules/user/repo/mapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './user.mapper';
24 changes: 24 additions & 0 deletions apps/server/src/modules/user/repo/mapper/school.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { School as SchoolEntity } from '@shared/domain';

export class SchoolMapper {
static mapToDO(entity: SchoolEntity): School {
return new School({

Check failure on line 5 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe return of an `any` typed value

Check failure on line 5 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe construction of an any type value
id: entity.id,
createdAt: entity.createdAt,

Check failure on line 7 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe assignment of an `any` value
updatedAt: entity.updatedAt,

Check failure on line 8 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe assignment of an `any` value
email: entity.email,

Check failure on line 9 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe assignment of an `any` value
});
}

static mapToEntity(domainObject: School): SchoolEntity {

Check failure on line 13 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'domainObject' is defined but never used
return new SchoolEntity({});
}

static mapToDOs(entities: SchoolEntity[]): School[] {
return entities.map((entity) => this.mapToDO(entity));

Check failure on line 18 in apps/server/src/modules/user/repo/mapper/school.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe return of an `any` typed value
}

static mapToEntities(domainObjects: School[]): SchoolEntity[] {
return domainObjects.map((domainObject) => this.mapToEntity(domainObject));
}
}
56 changes: 56 additions & 0 deletions apps/server/src/modules/user/repo/mapper/user.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { User as UserEntity } from '@shared/domain';
import { User } from '../../domain';

export class UserMapper {
static mapToDO(entity: UserEntity): User {
return new User({
id: entity.id,
createdAt: entity.createdAt,
updatedAt: entity.updatedAt,
email: entity.email,
firstName: entity.firstName,
lastName: entity.lastName,
roles: entity.roles.map((role) => role.id),

Check failure on line 14 in apps/server/src/modules/user/repo/mapper/user.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe assignment of an `any` value

Check failure on line 14 in apps/server/src/modules/user/repo/mapper/user.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe call of an `any` typed value

Check failure on line 14 in apps/server/src/modules/user/repo/mapper/user.mapper.ts

View workflow job for this annotation

GitHub Actions / nest_lint

Unsafe member access .id on an `any` value
school: SchoolMapper.mapToDO(entity.school),
ldapDn: entity.ldapDn,
externalId: entity.externalId,
importHash: entity.importHash,
firstNameSearchValues: entity.firstNameSearchValues,
lastNameSearchValues: entity.lastNameSearchValues,
emailSearchValues: entity.emailSearchValues,
language: entity.language,
forcePasswordChange: entity.forcePasswordChange,
preferences: entity.preferences,
lastLoginSystemChange: entity.lastLoginSystemChange,
outdatedSince: entity.outdatedSince,
previousExternalId: entity.previousExternalId,
});
}

static mapToEntity(domainObject: User): UserEntity {
return new UserEntity({
email: domainObject.email,
firstName: domainObject.firstName,
lastName: domainObject.lastName,
school: new ObjectId(domainObject.school),
roles: domainObject.roles.map((roleId) => new ObjectId(roleId)),
ldapDn: domainObject.ldapDn,
externalId: domainObject.externalId,
language: domainObject.language,
forcePasswordChange: domainObject.forcePasswordChange,
preferences: domainObject.preferences,
lastLoginSystemChange: domainObject.lastLoginSystemChange,
outdatedSince: domainObject.outdatedSince,
previousExternalId: domainObject.previousExternalId,
});
}

static mapToDOs(entities: UserEntity[]): User[] {
return entities.map((entity) => this.mapToDO(entity));
}

static mapToEntities(domainObjects: User[]): UserEntity[] {
return domainObjects.map((domainObject) => this.mapToEntity(domainObject));
}
}
134 changes: 134 additions & 0 deletions apps/server/src/modules/user/repo/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Collection, Entity, Index, ManyToMany, ManyToOne, Property } from '@mikro-orm/core';
import { IEntityWithSchool } from '../interface';
import { BaseEntityWithTimestamps } from './base.entity';
import { Role } from './role.entity';
import type { School } from './school.entity';

export enum LanguageType {
DE = 'de',
EN = 'en',
ES = 'es',
UK = 'uk',
}

export interface IUserProperties {
email: string;
firstName: string;
lastName: string;
school: School;
roles: Role[];
ldapDn?: string;
externalId?: string;
language?: LanguageType;
forcePasswordChange?: boolean;
preferences?: Record<string, unknown>;
deletedAt?: Date;
lastLoginSystemChange?: Date;
outdatedSince?: Date;
previousExternalId?: string;
}

@Entity({ tableName: 'users' })
@Index({ properties: ['id', 'email'] })
@Index({ properties: ['firstName', 'lastName'] })
@Index({ properties: ['externalId', 'school'] })
@Index({ properties: ['school', 'ldapDn'] })
@Index({ properties: ['school', 'roles'] })
export class User extends BaseEntityWithTimestamps implements IEntityWithSchool {
@Property()
@Index()
// @Unique()
email: string;

@Property()
firstName: string;

@Property()
lastName: string;

@Index()
@ManyToMany({ fieldName: 'roles', entity: () => Role })
roles = new Collection<Role>(this);

@Index()
@ManyToOne('School', { fieldName: 'schoolId' })
school: School;

@Property({ nullable: true })
@Index()
ldapDn?: string;

@Property({ nullable: true, fieldName: 'ldapId' })
externalId?: string;

@Property({ nullable: true })
previousExternalId?: string;

@Property({ nullable: true })
@Index()
importHash?: string;

@Property({ nullable: true })
firstNameSearchValues?: string[];

@Property({ nullable: true })
lastNameSearchValues?: string[];

@Property({ nullable: true })
emailSearchValues?: string[];

@Property({ nullable: true })
language?: LanguageType;

@Property({ nullable: true })
forcePasswordChange?: boolean;

@Property({ nullable: true })
preferences?: Record<string, unknown>;

@Property({ nullable: true })
@Index()
deletedAt?: Date;

@Property({ nullable: true })
lastLoginSystemChange?: Date;

@Property({ nullable: true })
outdatedSince?: Date;

constructor(props: IUserProperties) {
super();
this.firstName = props.firstName;
this.lastName = props.lastName;
this.email = props.email;
this.school = props.school;
this.roles.set(props.roles);
this.ldapDn = props.ldapDn;
this.externalId = props.externalId;
this.forcePasswordChange = props.forcePasswordChange;
this.language = props.language;
this.preferences = props.preferences ?? {};
this.deletedAt = props.deletedAt;
this.lastLoginSystemChange = props.lastLoginSystemChange;
this.outdatedSince = props.outdatedSince;
this.previousExternalId = props.previousExternalId;
}

public resolvePermissions(): string[] {
if (!this.roles.isInitialized(true)) {
throw new Error('Roles items are not loaded.');
}

let permissions: string[] = [];

const roles = this.roles.getItems();
roles.forEach((role) => {
const rolePermissions = role.resolvePermissions();
permissions = [...permissions, ...rolePermissions];
});

const uniquePermissions = [...new Set(permissions)];

return uniquePermissions;
}
}
19 changes: 19 additions & 0 deletions apps/server/src/modules/user/repo/user.repo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { Injectable } from '@nestjs/common';
import { EntityId, User as UserEntity } from '@shared/domain';
import { User } from '../domain';
import { UserMapper } from './mapper';

@Injectable()
export class UserRepo {
constructor(private readonly em: EntityManager) {}

async findById(userId: EntityId): Promise<User> {
const user = await this.em.findOneOrFail(
UserEntity,
{ _id: new ObjectId(userId) },
{ populate: ['school', 'roles'] }
);
return UserMapper.mapToDO(user);
}
}

0 comments on commit 5bbb8f8

Please sign in to comment.