Refactor types service into modular services and repositories
This commit is contained in:
7
src/common/constants/custom-field.constant.ts
Normal file
7
src/common/constants/custom-field.constant.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const CUSTOM_FIELD_SELECT = {
|
||||
id: true,
|
||||
name: true,
|
||||
type: true,
|
||||
required: true,
|
||||
options: true,
|
||||
} as const;
|
||||
55
src/common/mappers/model-type.mapper.spec.ts
Normal file
55
src/common/mappers/model-type.mapper.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ModelTypeMapper } from './model-type.mapper';
|
||||
|
||||
describe('ModelTypeMapper', () => {
|
||||
it('should map component create input', () => {
|
||||
const dto = {
|
||||
name: 'Comp',
|
||||
description: 'Desc',
|
||||
customFields: [{ name: 'Field', type: 'string', required: false, options: [] }],
|
||||
} as any;
|
||||
|
||||
const input = ModelTypeMapper.toComponentCreateInput(dto, 'code');
|
||||
|
||||
expect(input).toMatchObject({
|
||||
name: 'Comp',
|
||||
code: 'code',
|
||||
description: 'Desc',
|
||||
notes: 'Desc',
|
||||
});
|
||||
expect(input.customFields?.create?.[0]).toMatchObject({ name: 'Field' });
|
||||
});
|
||||
|
||||
it('should map piece model type to DTO shape', () => {
|
||||
const mapped = ModelTypeMapper.mapPieceModelType({
|
||||
id: '1',
|
||||
name: 'Piece',
|
||||
pieceCustomFields: [{ id: 'cf' }],
|
||||
pieceModels: [{ id: 'model' }],
|
||||
pieceRequirements: [{ id: 'req' }],
|
||||
pieces: [{ id: 'piece' }],
|
||||
});
|
||||
|
||||
expect(mapped).toMatchObject({
|
||||
id: '1',
|
||||
customFields: [{ id: 'cf' }],
|
||||
models: [{ id: 'model' }],
|
||||
pieceRequirements: [{ id: 'req' }],
|
||||
pieces: [{ id: 'piece' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should map piece update input', () => {
|
||||
const input = ModelTypeMapper.toPieceUpdateInput({
|
||||
name: 'New',
|
||||
description: 'D',
|
||||
customFields: [],
|
||||
} as any);
|
||||
|
||||
expect(input).toMatchObject({
|
||||
name: 'New',
|
||||
description: 'D',
|
||||
notes: 'D',
|
||||
});
|
||||
expect(input.pieceCustomFields).toBeUndefined();
|
||||
});
|
||||
});
|
||||
170
src/common/mappers/model-type.mapper.ts
Normal file
170
src/common/mappers/model-type.mapper.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import {
|
||||
CreateTypeComposantDto,
|
||||
CreateTypePieceDto,
|
||||
UpdateTypeComposantDto,
|
||||
UpdateTypePieceDto,
|
||||
} from '../../shared/dto/type.dto';
|
||||
import { CUSTOM_FIELD_SELECT } from '../constants/custom-field.constant';
|
||||
|
||||
export const COMPONENT_TYPE_INCLUDE: Prisma.ModelTypeInclude = {
|
||||
customFields: { select: CUSTOM_FIELD_SELECT },
|
||||
composants: true,
|
||||
models: true,
|
||||
};
|
||||
|
||||
export const PIECE_TYPE_INCLUDE: Prisma.ModelTypeInclude = {
|
||||
pieceCustomFields: { select: CUSTOM_FIELD_SELECT },
|
||||
pieceModels: true,
|
||||
pieceRequirements: true,
|
||||
pieces: true,
|
||||
};
|
||||
|
||||
type ModelTypeCreateWithoutCategory = Omit<
|
||||
Prisma.ModelTypeCreateInput,
|
||||
'category'
|
||||
>;
|
||||
|
||||
export class ModelTypeMapper {
|
||||
static toComponentCreateInput(
|
||||
dto: CreateTypeComposantDto,
|
||||
code: string,
|
||||
): ModelTypeCreateWithoutCategory {
|
||||
const { customFields, description, name } = dto;
|
||||
|
||||
return {
|
||||
name,
|
||||
code,
|
||||
description: description ?? null,
|
||||
notes: description ?? null,
|
||||
customFields: customFields
|
||||
? {
|
||||
create: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static toComponentUpdateInput(
|
||||
dto: UpdateTypeComposantDto,
|
||||
): Prisma.ModelTypeUpdateInput {
|
||||
const { customFields, description, name } = dto;
|
||||
const data: Prisma.ModelTypeUpdateInput = {};
|
||||
|
||||
if (name !== undefined) {
|
||||
data.name = name;
|
||||
}
|
||||
|
||||
if (description !== undefined) {
|
||||
data.description = description;
|
||||
data.notes = description;
|
||||
}
|
||||
|
||||
if (customFields !== undefined) {
|
||||
data.customFields = undefined;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static toPieceCreateInput(
|
||||
dto: CreateTypePieceDto,
|
||||
code: string,
|
||||
): ModelTypeCreateWithoutCategory {
|
||||
const { customFields, description, name } = dto;
|
||||
|
||||
return {
|
||||
name,
|
||||
code,
|
||||
description: description ?? null,
|
||||
notes: description ?? null,
|
||||
pieceCustomFields: customFields
|
||||
? {
|
||||
create: customFields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
})),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static toPieceUpdateInput(dto: UpdateTypePieceDto): Prisma.ModelTypeUpdateInput {
|
||||
const { customFields, description, name } = dto;
|
||||
const data: Prisma.ModelTypeUpdateInput = {};
|
||||
|
||||
if (name !== undefined) {
|
||||
data.name = name;
|
||||
}
|
||||
|
||||
if (description !== undefined) {
|
||||
data.description = description;
|
||||
data.notes = description;
|
||||
}
|
||||
|
||||
if (customFields !== undefined) {
|
||||
data.pieceCustomFields = undefined;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static mapPieceModelType(modelType: any) {
|
||||
if (!modelType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
pieceCustomFields,
|
||||
pieceModels,
|
||||
pieceRequirements,
|
||||
pieces,
|
||||
...rest
|
||||
} = modelType;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
customFields: pieceCustomFields ?? [],
|
||||
models: pieceModels ?? [],
|
||||
pieceRequirements: pieceRequirements ?? [],
|
||||
pieces: pieces ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
static mapComponentCustomFieldInputs(
|
||||
fields?: CreateTypeComposantDto['customFields'],
|
||||
) {
|
||||
if (!fields || fields.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
}));
|
||||
}
|
||||
|
||||
static mapPieceCustomFieldInputs(
|
||||
fields?: CreateTypePieceDto['customFields'],
|
||||
) {
|
||||
if (!fields || fields.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
}));
|
||||
}
|
||||
}
|
||||
83
src/common/mappers/type-machine.mapper.spec.ts
Normal file
83
src/common/mappers/type-machine.mapper.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { TypeMachineMapper } from './type-machine.mapper';
|
||||
|
||||
const baseDto = {
|
||||
name: 'Machine A',
|
||||
description: 'Desc',
|
||||
customFields: [
|
||||
{ name: 'Field', type: 'string', required: true, options: ['a'] },
|
||||
],
|
||||
componentRequirements: [
|
||||
{
|
||||
label: 'Comp',
|
||||
minCount: 2,
|
||||
maxCount: 4,
|
||||
required: true,
|
||||
allowNewModels: false,
|
||||
typeComposantId: 'comp-id',
|
||||
},
|
||||
],
|
||||
pieceRequirements: [
|
||||
{
|
||||
label: 'Piece',
|
||||
minCount: 0,
|
||||
maxCount: 2,
|
||||
required: false,
|
||||
allowNewModels: true,
|
||||
typePieceId: 'piece-id',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('TypeMachineMapper', () => {
|
||||
it('should map create input with nested relations', () => {
|
||||
const input = TypeMachineMapper.toCreateInput(baseDto as any);
|
||||
|
||||
expect(input.customFields?.create).toHaveLength(1);
|
||||
expect(input.componentRequirements?.create?.[0]).toMatchObject({
|
||||
label: 'Comp',
|
||||
minCount: 2,
|
||||
maxCount: 4,
|
||||
required: true,
|
||||
allowNewModels: false,
|
||||
});
|
||||
expect(input.pieceRequirements?.create?.[0]).toMatchObject({
|
||||
label: 'Piece',
|
||||
minCount: 0,
|
||||
maxCount: 2,
|
||||
required: false,
|
||||
allowNewModels: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should map custom field inputs for create many', () => {
|
||||
const result = TypeMachineMapper.mapCustomFieldInputs(baseDto.customFields as any);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'Field',
|
||||
type: 'string',
|
||||
required: true,
|
||||
options: ['a'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map requirements for bulk insert', () => {
|
||||
const component = TypeMachineMapper.mapComponentRequirementInputs(
|
||||
baseDto.componentRequirements as any,
|
||||
);
|
||||
const piece = TypeMachineMapper.mapPieceRequirementInputs(
|
||||
baseDto.pieceRequirements as any,
|
||||
);
|
||||
|
||||
expect(component[0]).toMatchObject({
|
||||
typeComposantId: 'comp-id',
|
||||
minCount: 2,
|
||||
maxCount: 4,
|
||||
});
|
||||
expect(piece[0]).toMatchObject({
|
||||
typePieceId: 'piece-id',
|
||||
minCount: 0,
|
||||
maxCount: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
188
src/common/mappers/type-machine.mapper.ts
Normal file
188
src/common/mappers/type-machine.mapper.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import {
|
||||
CreateTypeMachineDto,
|
||||
UpdateTypeMachineDto,
|
||||
} from '../../shared/dto/type.dto';
|
||||
import { CUSTOM_FIELD_SELECT } from '../constants/custom-field.constant';
|
||||
|
||||
type RequirementDto = {
|
||||
label?: string | null;
|
||||
minCount?: number | null;
|
||||
maxCount?: number | null;
|
||||
required?: boolean | null;
|
||||
allowNewModels?: boolean | null;
|
||||
typeComposantId?: string;
|
||||
typePieceId?: string;
|
||||
};
|
||||
|
||||
export const TYPE_MACHINE_DEFAULT_INCLUDE: Prisma.TypeMachineInclude = {
|
||||
customFields: { select: CUSTOM_FIELD_SELECT },
|
||||
componentRequirements: {
|
||||
include: { typeComposant: true },
|
||||
},
|
||||
pieceRequirements: {
|
||||
include: { typePiece: true },
|
||||
},
|
||||
};
|
||||
|
||||
export const TYPE_MACHINE_WITH_MACHINES_INCLUDE: Prisma.TypeMachineInclude = {
|
||||
...TYPE_MACHINE_DEFAULT_INCLUDE,
|
||||
machines: true,
|
||||
};
|
||||
|
||||
export class TypeMachineMapper {
|
||||
static toCreateInput(
|
||||
dto: CreateTypeMachineDto,
|
||||
): Prisma.TypeMachineCreateInput {
|
||||
const { customFields, componentRequirements, pieceRequirements, ...data } =
|
||||
dto;
|
||||
|
||||
return {
|
||||
...data,
|
||||
customFields: this.mapCustomFields(customFields),
|
||||
componentRequirements: this.mapComponentRequirements(componentRequirements),
|
||||
pieceRequirements: this.mapPieceRequirements(pieceRequirements),
|
||||
};
|
||||
}
|
||||
|
||||
static toUpdateData(dto: UpdateTypeMachineDto): Prisma.TypeMachineUpdateInput {
|
||||
const { customFields, componentRequirements, pieceRequirements, ...data } =
|
||||
dto;
|
||||
|
||||
const payload: Prisma.TypeMachineUpdateInput = { ...data };
|
||||
|
||||
if (customFields !== undefined) {
|
||||
payload.customFields = undefined;
|
||||
}
|
||||
|
||||
if (componentRequirements !== undefined) {
|
||||
payload.componentRequirements = undefined;
|
||||
}
|
||||
|
||||
if (pieceRequirements !== undefined) {
|
||||
payload.pieceRequirements = undefined;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
static mapCustomFields(
|
||||
fields?: CreateTypeMachineDto['customFields'],
|
||||
): Prisma.CustomFieldCreateNestedManyWithoutTypeMachineInput | undefined {
|
||||
if (!fields || fields.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
create: fields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static mapCustomFieldInputs(
|
||||
fields?: CreateTypeMachineDto['customFields'],
|
||||
) {
|
||||
if (!fields || fields.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fields.map((field) => ({
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required ?? false,
|
||||
options: field.options,
|
||||
}));
|
||||
}
|
||||
|
||||
static mapComponentRequirements(
|
||||
requirements?: RequirementDto[] | null,
|
||||
): Prisma.TypeMachineComponentRequirementCreateNestedManyWithoutTypeMachineInput | undefined {
|
||||
if (!requirements || requirements.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
create: requirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 1,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? true,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
typeComposant: requirement.typeComposantId
|
||||
? {
|
||||
connect: { id: requirement.typeComposantId },
|
||||
}
|
||||
: (() => {
|
||||
throw new Error(
|
||||
'typeComposantId est requis pour créer une contrainte de composant.',
|
||||
);
|
||||
})(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static mapComponentRequirementInputs(
|
||||
requirements?: RequirementDto[] | null,
|
||||
) {
|
||||
if (!requirements || requirements.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return requirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 1,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? true,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
typeComposantId: requirement.typeComposantId!,
|
||||
}));
|
||||
}
|
||||
|
||||
static mapPieceRequirements(
|
||||
requirements?: RequirementDto[] | null,
|
||||
): Prisma.TypeMachinePieceRequirementCreateNestedManyWithoutTypeMachineInput | undefined {
|
||||
if (!requirements || requirements.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
create: requirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 0,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? false,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
typePiece: requirement.typePieceId
|
||||
? {
|
||||
connect: { id: requirement.typePieceId },
|
||||
}
|
||||
: (() => {
|
||||
throw new Error(
|
||||
'typePieceId est requis pour créer une contrainte de pièce.',
|
||||
);
|
||||
})(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static mapPieceRequirementInputs(
|
||||
requirements?: RequirementDto[] | null,
|
||||
) {
|
||||
if (!requirements || requirements.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return requirements.map((requirement) => ({
|
||||
label: requirement.label ?? null,
|
||||
minCount: requirement.minCount ?? 0,
|
||||
maxCount: requirement.maxCount ?? null,
|
||||
required: requirement.required ?? false,
|
||||
allowNewModels: requirement.allowNewModels ?? true,
|
||||
typePieceId: requirement.typePieceId!,
|
||||
}));
|
||||
}
|
||||
}
|
||||
58
src/common/repositories/composant-models.repository.ts
Normal file
58
src/common/repositories/composant-models.repository.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, PrismaClient } from '@prisma/client';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ComposantModelsRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private get client(): PrismaClient {
|
||||
return this.prisma;
|
||||
}
|
||||
|
||||
async create(
|
||||
data: Prisma.ComposantModelCreateInput,
|
||||
include?: Prisma.ComposantModelInclude,
|
||||
) {
|
||||
return this.client.composantModel.create({
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(
|
||||
typeComposantId?: string,
|
||||
include?: Prisma.ComposantModelInclude,
|
||||
) {
|
||||
return this.client.composantModel.findMany({
|
||||
where: typeComposantId ? { typeComposantId } : undefined,
|
||||
include,
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string, include?: Prisma.ComposantModelInclude) {
|
||||
return this.client.composantModel.findUnique({
|
||||
where: { id },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Prisma.ComposantModelUpdateInput,
|
||||
include?: Prisma.ComposantModelInclude,
|
||||
) {
|
||||
return this.client.composantModel.update({
|
||||
where: { id },
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return this.client.composantModel.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
}
|
||||
71
src/common/repositories/model-types.repository.spec.ts
Normal file
71
src/common/repositories/model-types.repository.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ModelTypesRepository } from './model-types.repository';
|
||||
|
||||
describe('ModelTypesRepository', () => {
|
||||
const prismaMock = {
|
||||
modelType: {
|
||||
findUnique: jest.fn(),
|
||||
create: jest.fn(),
|
||||
findMany: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
update: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
},
|
||||
customField: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
},
|
||||
} as any;
|
||||
|
||||
const repository = new ModelTypesRepository(prismaMock);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should generate unique code when conflicts exist', async () => {
|
||||
prismaMock.modelType.findUnique
|
||||
.mockResolvedValueOnce({ id: 'existing' })
|
||||
.mockResolvedValueOnce(null);
|
||||
|
||||
const code = await repository.generateUniqueCode('My Type');
|
||||
|
||||
expect(code).toBe('my-type-1');
|
||||
expect(prismaMock.modelType.findUnique).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should create component type custom fields with relation id', async () => {
|
||||
await repository.createComponentTypeCustomFields('comp-id', [
|
||||
{ name: 'Field', type: 'string', required: true, options: [] },
|
||||
]);
|
||||
|
||||
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
name: 'Field',
|
||||
type: 'string',
|
||||
required: true,
|
||||
options: [],
|
||||
typeComposantId: 'comp-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create piece type custom fields with relation id', async () => {
|
||||
await repository.createPieceTypeCustomFields('piece-id', [
|
||||
{ name: 'Field', type: 'string', required: false, options: [] },
|
||||
]);
|
||||
|
||||
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
name: 'Field',
|
||||
type: 'string',
|
||||
required: false,
|
||||
options: [],
|
||||
typePieceId: 'piece-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
172
src/common/repositories/model-types.repository.ts
Normal file
172
src/common/repositories/model-types.repository.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ModelCategory, Prisma, PrismaClient } from '@prisma/client';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { slugifyName } from '../utils/slug.util';
|
||||
|
||||
type CustomFieldInput = Omit<
|
||||
Prisma.CustomFieldCreateManyInput,
|
||||
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
|
||||
>;
|
||||
|
||||
type PieceCustomFieldInput = Omit<
|
||||
Prisma.CustomFieldCreateManyInput,
|
||||
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
|
||||
>;
|
||||
|
||||
type ModelTypeCreateData = Omit<Prisma.ModelTypeCreateInput, 'category'>;
|
||||
|
||||
@Injectable()
|
||||
export class ModelTypesRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private get client(): PrismaClient {
|
||||
return this.prisma;
|
||||
}
|
||||
|
||||
async generateUniqueCode(name: string): Promise<string> {
|
||||
const base = slugifyName(name) || 'type';
|
||||
let candidate = base;
|
||||
let suffix = 1;
|
||||
|
||||
while (await this.client.modelType.findUnique({ where: { code: candidate } })) {
|
||||
candidate = `${base}-${suffix++}`;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
async createComponentType(
|
||||
data: ModelTypeCreateData,
|
||||
include?: Prisma.ModelTypeInclude,
|
||||
) {
|
||||
return this.client.modelType.create({
|
||||
data: {
|
||||
...data,
|
||||
category: ModelCategory.COMPONENT,
|
||||
},
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findComponentTypes(include?: Prisma.ModelTypeInclude) {
|
||||
return this.client.modelType.findMany({
|
||||
where: { category: ModelCategory.COMPONENT },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findComponentType(id: string, include?: Prisma.ModelTypeInclude) {
|
||||
return this.client.modelType.findFirst({
|
||||
where: { id, category: ModelCategory.COMPONENT },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async updateComponentType(
|
||||
id: string,
|
||||
data: Prisma.ModelTypeUpdateInput,
|
||||
include?: Prisma.ModelTypeInclude,
|
||||
) {
|
||||
return this.client.modelType.update({
|
||||
where: { id },
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteComponentType(id: string) {
|
||||
return this.client.modelType.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async deleteComponentTypeCustomFields(typeComposantId: string) {
|
||||
await this.client.customField.deleteMany({
|
||||
where: { typeComposantId },
|
||||
});
|
||||
}
|
||||
|
||||
async createComponentTypeCustomFields(
|
||||
typeComposantId: string,
|
||||
customFields: CustomFieldInput[],
|
||||
) {
|
||||
if (!customFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client.customField.createMany({
|
||||
data: customFields.map((field) => ({
|
||||
...field,
|
||||
typeComposantId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
async createPieceType(
|
||||
data: ModelTypeCreateData,
|
||||
include?: Prisma.ModelTypeInclude,
|
||||
) {
|
||||
return this.client.modelType.create({
|
||||
data: {
|
||||
...data,
|
||||
category: ModelCategory.PIECE,
|
||||
},
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findPieceTypes(include?: Prisma.ModelTypeInclude) {
|
||||
return this.client.modelType.findMany({
|
||||
where: { category: ModelCategory.PIECE },
|
||||
include,
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async findPieceType(id: string, include?: Prisma.ModelTypeInclude) {
|
||||
return this.client.modelType.findFirst({
|
||||
where: { id, category: ModelCategory.PIECE },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async updatePieceType(
|
||||
id: string,
|
||||
data: Prisma.ModelTypeUpdateInput,
|
||||
include?: Prisma.ModelTypeInclude,
|
||||
) {
|
||||
return this.client.modelType.update({
|
||||
where: { id },
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async deletePieceType(id: string) {
|
||||
return this.client.modelType.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async deletePieceTypeCustomFields(typePieceId: string) {
|
||||
await this.client.customField.deleteMany({
|
||||
where: { typePieceId },
|
||||
});
|
||||
}
|
||||
|
||||
async createPieceTypeCustomFields(
|
||||
typePieceId: string,
|
||||
customFields: PieceCustomFieldInput[],
|
||||
) {
|
||||
if (!customFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client.customField.createMany({
|
||||
data: customFields.map((field) => ({
|
||||
...field,
|
||||
typePieceId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
55
src/common/repositories/piece-models.repository.ts
Normal file
55
src/common/repositories/piece-models.repository.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, PrismaClient } from '@prisma/client';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class PieceModelsRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private get client(): PrismaClient {
|
||||
return this.prisma;
|
||||
}
|
||||
|
||||
async create(
|
||||
data: Prisma.PieceModelCreateInput,
|
||||
include?: Prisma.PieceModelInclude,
|
||||
) {
|
||||
return this.client.pieceModel.create({
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(typePieceId?: string, include?: Prisma.PieceModelInclude) {
|
||||
return this.client.pieceModel.findMany({
|
||||
where: typePieceId ? { typePieceId } : undefined,
|
||||
include,
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string, include?: Prisma.PieceModelInclude) {
|
||||
return this.client.pieceModel.findUnique({
|
||||
where: { id },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Prisma.PieceModelUpdateInput,
|
||||
include?: Prisma.PieceModelInclude,
|
||||
) {
|
||||
return this.client.pieceModel.update({
|
||||
where: { id },
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return this.client.pieceModel.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
}
|
||||
117
src/common/repositories/type-machines.repository.spec.ts
Normal file
117
src/common/repositories/type-machines.repository.spec.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { TypeMachinesRepository } from './type-machines.repository';
|
||||
|
||||
describe('TypeMachinesRepository', () => {
|
||||
const prismaMock = {
|
||||
customField: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
},
|
||||
typeMachineComponentRequirement: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
},
|
||||
typeMachinePieceRequirement: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
},
|
||||
machine: {
|
||||
findMany: jest.fn().mockResolvedValue([{ id: '1', name: 'Machine' }]),
|
||||
},
|
||||
typeMachine: {
|
||||
create: jest.fn(),
|
||||
findMany: jest.fn(),
|
||||
findUnique: jest.fn(),
|
||||
update: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
},
|
||||
} as any;
|
||||
|
||||
const repository = new TypeMachinesRepository(prismaMock);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should append typeMachineId when creating custom fields', async () => {
|
||||
await repository.createCustomFields('machine-id', [
|
||||
{ name: 'Field', type: 'string', required: true, options: [] },
|
||||
]);
|
||||
|
||||
expect(prismaMock.customField.createMany).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
name: 'Field',
|
||||
type: 'string',
|
||||
required: true,
|
||||
options: [],
|
||||
typeMachineId: 'machine-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should append typeMachineId when creating component requirements', async () => {
|
||||
await repository.createComponentRequirements('machine-id', [
|
||||
{
|
||||
label: 'Comp',
|
||||
minCount: 1,
|
||||
maxCount: 2,
|
||||
required: true,
|
||||
allowNewModels: true,
|
||||
typeComposantId: 'comp',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(
|
||||
prismaMock.typeMachineComponentRequirement.createMany,
|
||||
).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
label: 'Comp',
|
||||
minCount: 1,
|
||||
maxCount: 2,
|
||||
required: true,
|
||||
allowNewModels: true,
|
||||
typeComposantId: 'comp',
|
||||
typeMachineId: 'machine-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should append typeMachineId when creating piece requirements', async () => {
|
||||
await repository.createPieceRequirements('machine-id', [
|
||||
{
|
||||
label: 'Piece',
|
||||
minCount: 0,
|
||||
maxCount: 1,
|
||||
required: false,
|
||||
allowNewModels: true,
|
||||
typePieceId: 'piece',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(prismaMock.typeMachinePieceRequirement.createMany).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
label: 'Piece',
|
||||
minCount: 0,
|
||||
maxCount: 1,
|
||||
required: false,
|
||||
allowNewModels: true,
|
||||
typePieceId: 'piece',
|
||||
typeMachineId: 'machine-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should find machines using type', async () => {
|
||||
const result = await repository.findMachinesUsingType('machine-id');
|
||||
expect(result).toEqual([{ id: '1', name: 'Machine' }]);
|
||||
expect(prismaMock.machine.findMany).toHaveBeenCalledWith({
|
||||
where: { typeMachineId: 'machine-id' },
|
||||
select: { id: true, name: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
141
src/common/repositories/type-machines.repository.ts
Normal file
141
src/common/repositories/type-machines.repository.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, PrismaClient } from '@prisma/client';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
|
||||
type CustomFieldInput = Omit<
|
||||
Prisma.CustomFieldCreateManyInput,
|
||||
'id' | 'typeMachineId' | 'typePieceId' | 'typeComposantId'
|
||||
>;
|
||||
|
||||
type ComponentRequirementInput = Omit<
|
||||
Prisma.TypeMachineComponentRequirementCreateManyInput,
|
||||
'id' | 'typeMachineId'
|
||||
>;
|
||||
|
||||
type PieceRequirementInput = Omit<
|
||||
Prisma.TypeMachinePieceRequirementCreateManyInput,
|
||||
'id' | 'typeMachineId'
|
||||
>;
|
||||
|
||||
@Injectable()
|
||||
export class TypeMachinesRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private get client(): PrismaClient {
|
||||
return this.prisma;
|
||||
}
|
||||
|
||||
async create(
|
||||
data: Prisma.TypeMachineCreateInput,
|
||||
include?: Prisma.TypeMachineInclude,
|
||||
) {
|
||||
return this.client.typeMachine.create({
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(include?: Prisma.TypeMachineInclude) {
|
||||
return this.client.typeMachine.findMany({
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: string, include?: Prisma.TypeMachineInclude) {
|
||||
return this.client.typeMachine.findUnique({
|
||||
where: { id },
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: Prisma.TypeMachineUpdateInput,
|
||||
include?: Prisma.TypeMachineInclude,
|
||||
) {
|
||||
return this.client.typeMachine.update({
|
||||
where: { id },
|
||||
data,
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
return this.client.typeMachine.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async deleteCustomFields(typeMachineId: string) {
|
||||
await this.client.customField.deleteMany({
|
||||
where: { typeMachineId },
|
||||
});
|
||||
}
|
||||
|
||||
async createCustomFields(
|
||||
typeMachineId: string,
|
||||
customFields: CustomFieldInput[],
|
||||
) {
|
||||
if (!customFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client.customField.createMany({
|
||||
data: customFields.map((field) => ({
|
||||
...field,
|
||||
typeMachineId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
async deleteComponentRequirements(typeMachineId: string) {
|
||||
await this.client.typeMachineComponentRequirement.deleteMany({
|
||||
where: { typeMachineId },
|
||||
});
|
||||
}
|
||||
|
||||
async createComponentRequirements(
|
||||
typeMachineId: string,
|
||||
requirements: ComponentRequirementInput[],
|
||||
) {
|
||||
if (!requirements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client.typeMachineComponentRequirement.createMany({
|
||||
data: requirements.map((requirement) => ({
|
||||
...requirement,
|
||||
typeMachineId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
async deletePieceRequirements(typeMachineId: string) {
|
||||
await this.client.typeMachinePieceRequirement.deleteMany({
|
||||
where: { typeMachineId },
|
||||
});
|
||||
}
|
||||
|
||||
async createPieceRequirements(
|
||||
typeMachineId: string,
|
||||
requirements: PieceRequirementInput[],
|
||||
) {
|
||||
if (!requirements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client.typeMachinePieceRequirement.createMany({
|
||||
data: requirements.map((requirement) => ({
|
||||
...requirement,
|
||||
typeMachineId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
async findMachinesUsingType(typeMachineId: string) {
|
||||
return this.client.machine.findMany({
|
||||
where: { typeMachineId },
|
||||
select: { id: true, name: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
9
src/common/utils/slug.util.ts
Normal file
9
src/common/utils/slug.util.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function slugifyName(name: string): string {
|
||||
return name
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-+/g, '-');
|
||||
}
|
||||
Reference in New Issue
Block a user