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

Feature/740 fix mappings from prisma models to domain objects #775

Merged
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,5 @@ pycrypto

# Ignore did git install package
did

.cosine
25 changes: 14 additions & 11 deletions examples/bri-3/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions examples/bri-3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
"snarkjs:circuit": "./zeroKnowledgeArtifacts/scripts/circuits/$PROTOCOL.sh"
},
"dependencies": {
"@automapper/classes": "^8.7.6",
"@automapper/core": "^8.7.6",
"@automapper/nestjs": "^8.7.6",
"@automapper/classes": "^8.7.7",
"@automapper/core": "^8.7.7",
"@automapper/nestjs": "^8.7.7",
"@casl/ability": "^6.3.3",
"@casl/prisma": "^1.3.3",
"@ethereumjs/util": "^8.1.0",
Expand Down Expand Up @@ -128,4 +128,4 @@
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}
}
37 changes: 37 additions & 0 deletions examples/bri-3/prisma/prisma.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Injectable } from '@nestjs/common';
import { BpiSubject as BpiSubjectPrismaModel, BpiSubjectRole as BpiSubjectRolePrismaModel } from '@prisma/client';
import { BpiSubject } from '../src/bri/identity/bpiSubjects/models/bpiSubject';
import { BpiSubjectRole } from '../src/bri/identity/bpiSubjects/models/bpiSubjectRole';

// We do mapping from prisma models to domain objects using simple Object.assign
// since automapper is not happy working with types, which is how Prisma generates database entities.
// For these mappings to work, the convention is that the domain objects have the same properties as the
// prisma models. In case there is a need to do something custom during mapping, it can be implemented
// in the appropriate method below.

interface IConstructor<T> {
new (...args: any[]): T;
}

@Injectable()
export class PrismaMapper {
public mapBpiSubjectPrismaModelToDomainObject(source: BpiSubjectPrismaModel): BpiSubject {
const target = this.activator(BpiSubject);

Object.assign(target, source);

return target;
}

public mapBpiSubjectRolePrismaModelToDomainObject(source: BpiSubjectRolePrismaModel): BpiSubjectRole {
const target = this.activator(BpiSubjectRole);

Object.assign(target, source);

return target;
}

private activator<T>(type: IConstructor<T>): T {
return new type();
}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaMapper } from '../../../../../prisma/prisma.mapper';
import { PrismaService } from '../../../../shared/prisma/prisma.service';
import { NOT_FOUND_ERR_MESSAGE } from '../api/err.messages';
import { BpiSubject } from '../models/bpiSubject';
import { InjectMapper } from '@automapper/nestjs';
import { Mapper } from '@automapper/core';
import { BpiSubjectRole, BpiSubjectRoleName } from '../models/bpiSubjectRole';
import { PrismaService } from '../../../../shared/prisma/prisma.service';

// Repositories are the only places that talk the Prisma language of models.
// They are always mapped to and from domain objects so that the business layer of the application
// does not have to care about the ORM.
@Injectable()
export class BpiSubjectStorageAgent {
export class BpiSubjectStorageAgent extends PrismaService {
constructor(
@InjectMapper() private mapper: Mapper,
private mapper: PrismaMapper,
private readonly prisma: PrismaService,
) {}
) {
super();
}

async getBpiSubjectById(id: string): Promise<BpiSubject | undefined> {
const bpiSubjectModel = await this.prisma.bpiSubject.findUnique({
where: { id },
include: {
roles: true,
},
});

if (!bpiSubjectModel) {
return undefined;
}

return this.mapper.map(bpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(bpiSubjectModel);
}

async getAllBpiSubjects(): Promise<BpiSubject[]> {
const bpiSubjectModels = await this.prisma.bpiSubject.findMany();
return bpiSubjectModels.map((bpiSubjectModel) => {
return this.mapper.map(bpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(
bpiSubjectModel,
);
});
}

Expand All @@ -43,7 +49,9 @@ export class BpiSubjectStorageAgent {
include: { roles: true },
});
return bpiSubjectModels.map((bpiSubjectModel) => {
return this.mapper.map(bpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(
bpiSubjectModel,
);
});
}

Expand All @@ -57,7 +65,9 @@ export class BpiSubjectStorageAgent {
if (!bpiSubjectRole) {
throw new NotFoundException(NOT_FOUND_ERR_MESSAGE);
}
return this.mapper.map(bpiSubjectRole, BpiSubjectRole, BpiSubjectRole);
return this.mapper.mapBpiSubjectRolePrismaModelToDomainObject(
bpiSubjectRole,
);
}

async storeNewBpiSubject(bpiSubject: BpiSubject): Promise<BpiSubject> {
Expand All @@ -75,7 +85,9 @@ export class BpiSubjectStorageAgent {
},
});

return this.mapper.map(newBpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(
newBpiSubjectModel,
);
}

async updateBpiSubject(bpiSubject: BpiSubject): Promise<BpiSubject> {
Expand All @@ -92,7 +104,9 @@ export class BpiSubjectStorageAgent {
},
},
});
return this.mapper.map(updatedBpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(
updatedBpiSubjectModel,
);
}

async deleteBpiSubject(bpiSubject: BpiSubject): Promise<void> {
Expand All @@ -113,6 +127,6 @@ export class BpiSubjectStorageAgent {
if (!bpiSubjectModel) {
throw new NotFoundException(NOT_FOUND_ERR_MESSAGE);
}
return this.mapper.map(bpiSubjectModel, BpiSubject, BpiSubject);
return this.mapper.mapBpiSubjectPrismaModelToDomainObject(bpiSubjectModel);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AutoMap } from '@automapper/classes';
import { BpiSubjectRoleDto } from './bpiSubjectRole.dto';

export class BpiSubjectDto {
@AutoMap()
Expand All @@ -12,4 +13,7 @@ export class BpiSubjectDto {

@AutoMap()
publicKey: string;

@AutoMap(() => [BpiSubjectRoleDto])
roles: BpiSubjectRoleDto[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AutoMap } from '@automapper/classes';

export class BpiSubjectRoleDto {
@AutoMap()
name: string;

@AutoMap()
description: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export class BpiSubject {

@AutoMap(() => [BpiSubjectRole])
roles: BpiSubjectRole[];

constructor(
id: string,
name: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { PrismaMapper } from '../../../../prisma/prisma.mapper';
import { PrismaModule } from '../../../shared/prisma/prisma.module';
import { AuthzModule } from '../../authz/authz.module';
import { BpiSubjectAgent } from './agents/bpiSubjects.agent';
import { BpiSubjectStorageAgent } from './agents/bpiSubjectsStorage.agent';
import { SubjectController } from './api/subjects.controller';
import { CreateBpiSubjectCommandHandler } from './capabilities/createBpiSubject/createBpiSubjectCommand.handler';
import { DeleteBpiSubjectCommandHandler } from './capabilities/deleteBpiSubject/deleteBpiSubjectCommand.handler';
import { GetAllBpiSubjectsQueryHandler } from './capabilities/getAllBpiSubjects/getAllBpiSubjectsQuery.handler';
import { GetBpiSubjectByIdQueryHandler } from './capabilities/getBpiSubjectById/getBpiSubjectByIdQuery.handler';
import { UpdateBpiSubjectCommandHandler } from './capabilities/updateBpiSubject/updateBpiSubjectCommand.handler';
import { BpiSubjectStorageAgent } from './agents/bpiSubjectsStorage.agent';
import { SubjectsProfile } from './subjects.profile';
import { AuthzModule } from '../../authz/authz.module';
import { PrismaModule } from '../../../shared/prisma/prisma.module';

export const CommandHandlers = [
CreateBpiSubjectCommandHandler,
Expand All @@ -31,6 +32,7 @@ export const QueryHandlers = [
BpiSubjectAgent,
BpiSubjectStorageAgent,
SubjectsProfile,
PrismaMapper,
],
exports: [BpiSubjectAgent, BpiSubjectStorageAgent],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Mapper, createMap } from '@automapper/core';
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
import { createMap, Mapper } from '@automapper/core';
import { Injectable } from '@nestjs/common';
import { BpiSubject } from './models/bpiSubject';
import { BpiSubjectDto } from './api/dtos/response/bpiSubject.dto';
import { BpiSubject } from './models/bpiSubject';
import { BpiSubjectRole } from './models/bpiSubjectRole';
import { BpiSubjectRoleDto } from './api/dtos/response/bpiSubjectRole.dto';

@Injectable()
export class SubjectsProfile extends AutomapperProfile {
Expand All @@ -14,8 +15,7 @@ export class SubjectsProfile extends AutomapperProfile {
override get profile() {
return (mapper) => {
createMap(mapper, BpiSubject, BpiSubjectDto);
createMap(mapper, BpiSubject, BpiSubject);
createMap(mapper, BpiSubjectRole, BpiSubjectRole);
createMap(mapper, BpiSubjectRole, BpiSubjectRoleDto);
};
}
}
Loading