From 29494897f93f710fc3abc0c2ad76dcbe07f576f2 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 13:50:55 +0200 Subject: [PATCH 01/12] feat: connect Cases and facilities in the db Signed-off-by: Jonas --- prisma/schema.prisma | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6f05268e..d96b9ea1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,6 +46,7 @@ model Asset { status FacilityStatus @default(REGULAR) indicatorMsg String @default("The pump is working as expected.") + cases Case[] timeSeriesItems TimeSeriesItem[] metrics Metrics[] @@ -89,8 +90,10 @@ model Case { eTag String modifiedBy String @default("") - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + Asset Asset @relation(fields: [assetAssetId], references: [assetId]) + assetAssetId String } model Metrics { From 908aec445db7061ae62f728e3203f0182087efec Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 14:05:56 +0200 Subject: [PATCH 02/12] fix: cases service Signed-off-by: Jonas --- .../lib/controller/case.controller.spec.ts | 14 +- .../src/lib/controller/case.controller.ts | 2 +- .../case-management/src/lib/dto/body.dto.ts | 8 +- .../case-management/src/lib/dto/params.dto.ts | 2 +- .../src/lib/services/case.service.spec.ts | 6 +- .../src/lib/services/case.service.ts | 46 ++- .../application/facades/cases.facade.spec.ts | 2 +- .../lib/application/facades/cases.facade.ts | 2 +- .../cases-request.service.spec.ts | 6 +- .../infrastructure/cases-request.service.ts | 5 +- .../create-case/create-case.component.ts | 345 +++++++++--------- .../interfaces/case-form-data.interface.ts | 16 +- .../open-cases/open-cases.component.ts | 64 ++++ .../src/lib/service/facilities.service.ts | 18 + .../src/lib/interfaces/response.interface.ts | 3 + tsconfig.base.json | 2 +- 16 files changed, 330 insertions(+), 211 deletions(-) create mode 100644 libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts diff --git a/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts b/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts index 9f9a1206..844a9ef8 100644 --- a/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts +++ b/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts @@ -1,4 +1,5 @@ import { faker } from '@faker-js/faker'; +<<<<<<< HEAD import { ECasePriority, ECaseStatus, @@ -6,7 +7,16 @@ import { ICaseResponse, ICreateCaseBody, } from '@frontend/cases/shared/models'; +======= +>>>>>>> 3254e7e (fix: cases service) import { Test, TestingModule } from '@nestjs/testing'; +import { + ECasePriority, + ECaseStatus, + ECaseType, + ICaseResponse, + ICreateCaseBody, +} from 'cases-shared-models'; import { firstValueFrom, of } from 'rxjs'; import { XdCaseService } from '../services/case.service'; @@ -40,7 +50,7 @@ describe('CaseController', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [ XdCaseController ], + controllers: [XdCaseController], providers: [ { provide: XdCaseService, @@ -58,7 +68,7 @@ describe('CaseController', () => { }); it('should call getAllCases', async () => { - const getAllReturnValue = [ returnValue ] as ICaseResponse[]; + const getAllReturnValue = [returnValue] as ICaseResponse[]; const spy = jest.spyOn(service, 'getAllCases').mockReturnValue(of(getAllReturnValue)); const result = await firstValueFrom(controller.getAllCases()); diff --git a/libs/cases/backend/case-management/src/lib/controller/case.controller.ts b/libs/cases/backend/case-management/src/lib/controller/case.controller.ts index 08a16d05..2822088c 100644 --- a/libs/cases/backend/case-management/src/lib/controller/case.controller.ts +++ b/libs/cases/backend/case-management/src/lib/controller/case.controller.ts @@ -1,6 +1,6 @@ -import { ICaseResponse } from '@frontend/cases/shared/models'; import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; import { ApiAcceptedResponse, ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { ICaseResponse } from 'cases-shared-models'; import { ESwaggerTag } from 'common-backend-swagger'; import { Observable } from 'rxjs'; diff --git a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts index 5d40707d..02d9c5ce 100644 --- a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts +++ b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts @@ -1,4 +1,10 @@ -import { ECasePriority, ECaseStatus, ECaseType, ICreateCaseBody, IUpdateCaseBody } from '@frontend/cases/shared/models'; +import { + ECasePriority, + ECaseStatus, + ECaseType, + ICreateCaseBody, + IUpdateCaseBody, +} from 'cases-shared-models'; import { Type } from 'class-transformer'; import { IsDate, IsEmail, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; diff --git a/libs/cases/backend/case-management/src/lib/dto/params.dto.ts b/libs/cases/backend/case-management/src/lib/dto/params.dto.ts index 054a3311..26df5dc8 100644 --- a/libs/cases/backend/case-management/src/lib/dto/params.dto.ts +++ b/libs/cases/backend/case-management/src/lib/dto/params.dto.ts @@ -1,4 +1,4 @@ -import { ICaseParams } from '@frontend/cases/shared/models'; +import { ICaseParams } from 'cases-shared-models'; import { Type } from 'class-transformer'; import { IsNotEmpty, IsNumber } from 'class-validator'; diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts index f51c3bb1..9465b562 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts @@ -1,11 +1,15 @@ import { faker } from '@faker-js/faker'; +<<<<<<< HEAD import { ECasePriority, ECaseStatus, ECaseType, ICreateCaseBody, } from '@frontend/cases/shared/models'; +======= +>>>>>>> 3254e7e (fix: cases service) import { Test, TestingModule } from '@nestjs/testing'; +import { ECasePriority, ECaseStatus, ECaseType, ICreateCaseBody } from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; import { firstValueFrom } from 'rxjs'; @@ -43,7 +47,7 @@ describe('CaseController', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [ XdCaseController ], + controllers: [XdCaseController], providers: [ XdCaseService, { diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.ts b/libs/cases/backend/case-management/src/lib/services/case.service.ts index 9b903add..fc636d68 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.ts @@ -1,5 +1,5 @@ -import { ICaseResponse, ICreateCaseBody, IUpdateCaseBody } from '@frontend/cases/shared/models'; import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { ICaseResponse, ICreateCaseBody, IUpdateCaseBody } from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; import { from, map, Observable } from 'rxjs'; @@ -21,10 +21,13 @@ export class XdCaseService { public getAllCases(): Observable { return from(this.prismaService.case.findMany()).pipe( map((items) => - items.map((item) => ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse), + items.map( + (item) => + ({ + ...item, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }) as ICaseResponse, + ), ), ); } @@ -64,10 +67,13 @@ export class XdCaseService { }, }), ).pipe( - map((item) => ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - } as ICaseResponse)), + map( + (item) => + ({ + ...item, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }) as ICaseResponse, + ), ); } @@ -83,10 +89,13 @@ export class XdCaseService { data: caseData, }), ).pipe( - map((item) => ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse), + map( + (item) => + ({ + ...item, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }) as ICaseResponse, + ), ); } @@ -101,10 +110,13 @@ export class XdCaseService { where: { id }, }), ).pipe( - map((item) => ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse), + map( + (item) => + ({ + ...item, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }) as ICaseResponse, + ), ); } } diff --git a/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.spec.ts b/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.spec.ts index dee34471..1e854d14 100644 --- a/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.spec.ts +++ b/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.spec.ts @@ -1,5 +1,5 @@ import { TestBed } from '@angular/core/testing'; -import { ICaseParams } from '@frontend/cases/shared/models'; +import { ICaseParams } from 'cases-shared-models'; import { firstValueFrom, of } from 'rxjs'; import { XdCasesRequestService } from '../../infrastructure/cases-request.service'; diff --git a/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.ts b/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.ts index 89d8a9c5..255c061e 100644 --- a/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.ts +++ b/libs/cases/frontend/domain/src/lib/application/facades/cases.facade.ts @@ -1,5 +1,5 @@ import { inject, Injectable } from '@angular/core'; -import { ICaseParams, ICaseResponse, ICreateCaseBody } from '@frontend/cases/shared/models'; +import { ICaseParams, ICaseResponse, ICreateCaseBody } from 'cases-shared-models'; import { Observable } from 'rxjs'; import { XdCasesRequestService } from '../../infrastructure/cases-request.service'; diff --git a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts index e84a8a30..9c653d10 100644 --- a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts +++ b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { faker } from '@faker-js/faker'; -import { ICaseParams, ICaseResponse } from '@frontend/cases/shared/models'; +import { ICaseParams, ICaseResponse } from 'cases-shared-models'; import { firstValueFrom, of } from 'rxjs'; import { XdCasesRequestService } from './cases-request.service'; @@ -13,7 +13,7 @@ describe('XdCasesRequestService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ], + imports: [HttpClientTestingModule], providers: [ XdCasesRequestService, { @@ -45,7 +45,7 @@ describe('XdCasesRequestService', () => { describe('getTimeSeries', () => { it('should forward the request to the backend', async () => { const params = { id: faker.number.int() } as ICaseParams; - const mockResponse: ICaseParams[] = [ { id: faker.number.int() } ] as ICaseParams[]; + const mockResponse: ICaseParams[] = [{ id: faker.number.int() }] as ICaseParams[]; const spy = jest.spyOn(httpClient, 'get').mockReturnValue(of(mockResponse)); diff --git a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts index de6e9417..c8228ec5 100644 --- a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts +++ b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; -import { ICaseParams, ICaseResponse, ICreateCaseBody } from '@frontend/cases/shared/models'; +import { ICaseParams, ICaseResponse, ICreateCaseBody } from 'cases-shared-models'; import { Observable } from 'rxjs'; @Injectable({ @@ -33,6 +33,7 @@ export class XdCasesRequestService { public createCase(body: ICreateCaseBody) { return this._httpClient.post('/api/case', body); } +<<<<<<< HEAD public updateCase(params: ICaseParams, body: ICreateCaseBody) { return this._httpClient.put(`/api/case/${params.id}`, body); @@ -45,4 +46,6 @@ export class XdCasesRequestService { public deleteCase(params: ICaseParams) { return this._httpClient.delete(`/api/case/${params.id}`); } +======= +>>>>>>> 3254e7e (fix: cases service) } diff --git a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts index 389e573d..e407c7de 100644 --- a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts +++ b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts @@ -1,19 +1,20 @@ import { CommonModule } from '@angular/common'; import { - ChangeDetectionStrategy, - Component, - inject, OnInit, - signal, - ViewEncapsulation, + ChangeDetectionStrategy, + Component, + inject, + OnInit, + signal, + ViewEncapsulation, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { FormsModule, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { faker } from '@faker-js/faker'; import { XdCasesFacade } from '@frontend/cases/frontend/domain'; -import { ECasePriority, ECaseStatus, ECaseType } from '@frontend/cases/shared/models'; import { XdBrowseFacade } from '@frontend/facilities/frontend/domain'; import { IxModule, IxSelectCustomEvent, ToastService } from '@siemens/ix-angular'; +import { ECasePriority, ECaseStatus, ECaseType } from 'cases-shared-models'; import { CaseFormData } from '../interfaces/case-form-data.interface'; import { DateDropdownWrapperComponent } from './date-dropdown-accessor'; @@ -21,176 +22,174 @@ import { DateDropdownWrapperComponent } from './date-dropdown-accessor'; @Component({ selector: 'lib-create-case', standalone: true, - imports: [ CommonModule, IxModule, FormsModule, RouterLink, DateDropdownWrapperComponent ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useClass: DateDropdownWrapperComponent, - multi: true - }, - ], + imports: [CommonModule, IxModule, FormsModule, RouterLink, DateDropdownWrapperComponent], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useClass: DateDropdownWrapperComponent, + multi: true, + }, + ], templateUrl: './create-case.component.html', styleUrl: './create-case.component.scss', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CreateCaseComponent implements OnInit { - private readonly _browseFacade = inject(XdBrowseFacade); - protected readonly _casesFacade = inject(XdCasesFacade); - protected readonly facilities = toSignal(this._browseFacade.getAllFacilities()); - - facilityPlaceholder = signal('Select Facility'); - typePlaceholder = signal('Select Type'); - priorityPlaceholder = signal('Select Priority'); - - constructor( - protected readonly router: Router, - protected readonly route: ActivatedRoute, - private readonly toastService: ToastService - ) {} - - casePriority = ECasePriority; - caseType = ECaseType; - wasValidated = false; - - createCaseForm = { - selectFacility: '', - title: '', - dueDate: '', - selectPriority: '', - selectType: '', - email: '', - text: '', - }; - - ngOnInit(){ - this.resizeObserver('input-facilitySelection', 'facilitySelection'); - this.resizeObserver('input-typeSelection', 'typeSelection'); - this.resizeObserver('input-prioritySelection', 'prioritySelection'); - } - - /** - * called when the user presses the Create Case Button - */ - onSubmit(form: NgForm): void { - this.wasValidated = true; - - if (form.valid) { - const caseData = this.mapFormData(form.form.value); - - this._casesFacade.createCase(caseData).subscribe({ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - next: (_) => { - this.showSuccessToast(); - this.wasValidated = false; - form.reset(); - }, - }); - } - } - - async showSuccessToast() { - await this.toastService.show({ - type: 'success', - message: 'Successfully created Case' - }); - } - - public set facilityValue(value: string) { - this.createCaseForm.selectFacility = value; - } - - public get facilityValue() { - return this.createCaseForm.selectFacility; - } - - public set emailValue(value: string) { - this.createCaseForm.email = value; - } - - public get emailValue() { - return this.createCaseForm.email; - } - - public getFacility() { - return this.facilities()?.find( - (facility) => facility.id === this.createCaseForm.selectFacility, - ); - } - - private resizeObserver(inputElement: string, selectElement: string) { - const input = document.getElementById(inputElement); - const select = document.getElementById(selectElement) - if (input && select) { - new ResizeObserver(entries => { - entries.forEach(entry => { - const width = entry.contentRect.width; - const height = entry.contentRect.height; - const xPos = entry.contentRect.x; - const yPos = entry.contentRect.y; - if (input && input.style) { - input.style.width = `${width}px`; - input.style.height = `${height}px`; - input.style.x = `${xPos}`; - input.style.y = `${yPos}`; - } - }) - }).observe(select); - } - } - - - onFacilityChange(event: IxSelectCustomEvent) { - if (event.target.value !== undefined) { - this.createCaseForm.selectFacility = event.target.value.toString(); - } - } - - onFacilityInputChange(event: CustomEvent) { - this.createCaseForm.selectFacility = ''; - this.facilityPlaceholder.set(event.detail); - } - - onTypeChange(event: IxSelectCustomEvent) { - if (event.target.value !== undefined) { - this.createCaseForm.selectType = event.target.value.toString(); - } - } - - onTypeInputChange(event: CustomEvent) { - this.createCaseForm.selectType = ''; - this.typePlaceholder.set(event.detail); - } - - onPriorityChange(event: IxSelectCustomEvent) { - if (event.target.value !== undefined) { - this.createCaseForm.selectPriority = event.target.value.toString(); - } - } - - onPriorityInputChange(event: CustomEvent) { - this.createCaseForm.selectPriority = ''; - this.priorityPlaceholder.set(event.detail); - } - - /** - * - * @param formData case data in the form filled in by the user - * @returns {JSON} - */ - private mapFormData(formData: CaseFormData) { - return { - handle: 'AA-' + faker.number.int({ min: 1000, max: 9999 }), - dueDate: formData.dueDate, - title: formData.title, - type: formData.selectType, - status: ECaseStatus.OPEN, - description: formData.text, - source: 'Internal System ' + faker.number.int({min: 1, max: 10}), - priority: formData.selectPriority, - createdBy: formData.email, - eTag: faker.string.alphanumeric(10), - }; - } - + private readonly _browseFacade = inject(XdBrowseFacade); + protected readonly _casesFacade = inject(XdCasesFacade); + protected readonly facilities = toSignal(this._browseFacade.getAllFacilities()); + + facilityPlaceholder = signal('Select Facility'); + typePlaceholder = signal('Select Type'); + priorityPlaceholder = signal('Select Priority'); + + constructor( + protected readonly router: Router, + protected readonly route: ActivatedRoute, + private readonly toastService: ToastService, + ) {} + + casePriority = ECasePriority; + caseType = ECaseType; + wasValidated = false; + + createCaseForm = { + selectFacility: '', + title: '', + dueDate: '', + selectPriority: '', + selectType: '', + email: '', + text: '', + }; + + ngOnInit() { + this.resizeObserver('input-facilitySelection', 'facilitySelection'); + this.resizeObserver('input-typeSelection', 'typeSelection'); + this.resizeObserver('input-prioritySelection', 'prioritySelection'); + } + + /** + * called when the user presses the Create Case Button + */ + onSubmit(form: NgForm): void { + this.wasValidated = true; + + if (form.valid) { + const caseData = this.mapFormData(form.form.value); + + this._casesFacade.createCase(caseData).subscribe({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + next: (_) => { + this.showSuccessToast(); + this.wasValidated = false; + form.reset(); + }, + }); + } + } + + async showSuccessToast() { + await this.toastService.show({ + type: 'success', + message: 'Successfully created Case', + }); + } + + public set facilityValue(value: string) { + this.createCaseForm.selectFacility = value; + } + + public get facilityValue() { + return this.createCaseForm.selectFacility; + } + + public set emailValue(value: string) { + this.createCaseForm.email = value; + } + + public get emailValue() { + return this.createCaseForm.email; + } + + public getFacility() { + return this.facilities()?.find( + (facility) => facility.id === this.createCaseForm.selectFacility, + ); + } + + private resizeObserver(inputElement: string, selectElement: string) { + const input = document.getElementById(inputElement); + const select = document.getElementById(selectElement); + if (input && select) { + new ResizeObserver((entries) => { + entries.forEach((entry) => { + const width = entry.contentRect.width; + const height = entry.contentRect.height; + const xPos = entry.contentRect.x; + const yPos = entry.contentRect.y; + if (input && input.style) { + input.style.width = `${width}px`; + input.style.height = `${height}px`; + input.style.x = `${xPos}`; + input.style.y = `${yPos}`; + } + }); + }).observe(select); + } + } + + onFacilityChange(event: IxSelectCustomEvent) { + if (event.target.value !== undefined) { + this.createCaseForm.selectFacility = event.target.value.toString(); + } + } + + onFacilityInputChange(event: CustomEvent) { + this.createCaseForm.selectFacility = ''; + this.facilityPlaceholder.set(event.detail); + } + + onTypeChange(event: IxSelectCustomEvent) { + if (event.target.value !== undefined) { + this.createCaseForm.selectType = event.target.value.toString(); + } + } + + onTypeInputChange(event: CustomEvent) { + this.createCaseForm.selectType = ''; + this.typePlaceholder.set(event.detail); + } + + onPriorityChange(event: IxSelectCustomEvent) { + if (event.target.value !== undefined) { + this.createCaseForm.selectPriority = event.target.value.toString(); + } + } + + onPriorityInputChange(event: CustomEvent) { + this.createCaseForm.selectPriority = ''; + this.priorityPlaceholder.set(event.detail); + } + + /** + * + * @param formData case data in the form filled in by the user + * @returns {JSON} + */ + private mapFormData(formData: CaseFormData) { + return { + handle: 'AA-' + faker.number.int({ min: 1000, max: 9999 }), + dueDate: formData.dueDate, + title: formData.title, + type: formData.selectType, + status: ECaseStatus.OPEN, + description: formData.text, + source: 'Internal System ' + faker.number.int({ min: 1, max: 10 }), + priority: formData.selectPriority, + createdBy: formData.email, + eTag: faker.string.alphanumeric(10), + }; + } } diff --git a/libs/cases/frontend/view/src/lib/components/interfaces/case-form-data.interface.ts b/libs/cases/frontend/view/src/lib/components/interfaces/case-form-data.interface.ts index 10c49897..ca98c9ad 100644 --- a/libs/cases/frontend/view/src/lib/components/interfaces/case-form-data.interface.ts +++ b/libs/cases/frontend/view/src/lib/components/interfaces/case-form-data.interface.ts @@ -1,14 +1,14 @@ -import { ECasePriority, ECaseType } from '@frontend/cases/shared/models'; +import { ECasePriority, ECaseType } from 'cases-shared-models'; /** * Data structure for the case form */ export interface CaseFormData { - selectFacility: string; - title: string; - dueDate: Date; - selectPriority: ECasePriority; - selectType: ECaseType; - email: string; - text: string; + selectFacility: string; + title: string; + dueDate: Date; + selectPriority: ECasePriority; + selectType: ECaseType; + email: string; + text: string; } diff --git a/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts new file mode 100644 index 00000000..bd98077c --- /dev/null +++ b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts @@ -0,0 +1,64 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + inject, + ViewEncapsulation, +} from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { RouterLink } from '@angular/router'; +import { XdBrowseFacadesService } from '@frontend/cases/frontend/domain'; +import { IxModule } from '@siemens/ix-angular'; +import { ICaseResponse } from 'cases-shared-models'; + +@Component({ + selector: 'lib-open-cases', + standalone: true, + imports: [ CommonModule, IxModule, RouterLink ], + templateUrl: './open-cases.component.html', + styleUrl: './open-cases.component.scss', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class OpenCasesComponent { + protected readonly _browseFacade = inject(XdBrowseFacadesService); + protected readonly _cases = toSignal(this._browseFacade.getAllCases()); + protected readonly _filteredCases = computed(() => { + const cases = this._cases(); + if (cases === undefined) { + return; + } + cases + .filter((_case: ICaseResponse) => _case.status === 'OPEN') + .sort((a, b) => { + const priorityOrder = [ 'EMERGENCY', 'HIGH', 'MEDIUM', 'LOW' ]; + const priorityAIndex = priorityOrder.indexOf(a.priority.toUpperCase()); + const priorityBIndex = priorityOrder.indexOf(b.priority.toUpperCase()); + + if (priorityAIndex === priorityBIndex) { + return a.id - b.id; + } else if (priorityAIndex === -1) { + return 1; + } else if (priorityBIndex === -1) { + return -1; + } + return priorityAIndex - priorityBIndex; + }); + + return cases; + }); + + getStatusClasses(_case: ICaseResponse) { + return { + emergency: _case.priority === 'EMERGENCY', + 'status-open': _case.status === 'OPEN', + 'status-inprogress': _case.status === 'INPROGRESS', + 'status-overdue': _case.status === 'OVERDUE', + 'status-onhold': _case.status === 'ONHOLD', + 'status-done': _case.status === 'DONE', + 'status-cancelled': _case.status === 'CANCELLED', + 'status-archived': _case.status === 'ARCHIVED', + }; + } +} diff --git a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts index 9710f18d..9a4cc9e8 100644 --- a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts +++ b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts @@ -163,7 +163,11 @@ export class XdFacilitiesService { this.prismaService.asset.findMany({ include: { location: true, +<<<<<<< HEAD metrics: true, +======= + cases: true, +>>>>>>> 3254e7e (fix: cases service) }, }), ).pipe( @@ -177,9 +181,13 @@ export class XdFacilitiesService { createdAt, updatedAt, variables, +<<<<<<< HEAD status, indicatorMsg, metrics +======= + cases, +>>>>>>> 3254e7e (fix: cases service) } = asset; const location: IFacilityLocation | undefined = asset.location @@ -206,6 +214,7 @@ export class XdFacilitiesService { description: description || '', createdAt: createdAt, updatedAt: updatedAt, + cases: cases.map((c) => ({ caseId: c.id })), }; }); }), @@ -223,7 +232,11 @@ export class XdFacilitiesService { }, include: { location: true, +<<<<<<< HEAD metrics: true, +======= + cases: true, +>>>>>>> 3254e7e (fix: cases service) }, }), ).pipe( @@ -240,9 +253,13 @@ export class XdFacilitiesService { variables, createdAt, updatedAt, +<<<<<<< HEAD status, indicatorMsg, metrics +======= + cases, +>>>>>>> 3254e7e (fix: cases service) } = asset; const location: IFacilityLocation | undefined = asset.location @@ -269,6 +286,7 @@ export class XdFacilitiesService { location: location, createdAt: createdAt, updatedAt: updatedAt, + cases: cases.map((c) => ({ caseId: c.id })), }; }), ); diff --git a/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts b/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts index e1692cc9..8b8b27a1 100644 --- a/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts +++ b/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts @@ -1,3 +1,4 @@ +import { ICaseResponse } from 'common-shared-models'; import { TTimeSeriesData } from 'common-shared-models'; import { EPumpStatus } from '../enums'; @@ -93,6 +94,8 @@ export interface IFacilitiesResponse { * The updatedAt timestamp of the asset */ updatedAt: Date; + + cases: Pick[]; } export interface IFacilityLocation { diff --git a/tsconfig.base.json b/tsconfig.base.json index 9bba39f8..a05240cf 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -17,7 +17,6 @@ "baseUrl": ".", "paths": { "@frontend/cases/frontend/domain": ["libs/cases/frontend/domain/src/index.ts"], - "@frontend/cases/shared/models": ["libs/cases/shared/models/src/index.ts"], "@frontend/common/shared/models": ["libs/common/shared/models/src/index.ts"], "@frontend/facilities/backend/models": ["libs/facilities/backend/models/src/index.ts"], "@frontend/facilities/backend/utils": ["libs/facilities/backend/utils/src/index.ts"], @@ -34,6 +33,7 @@ "cases-backend-management": ["libs/cases/backend/case-management/src/index.ts"], "cases-frontend-shell": ["libs/cases/frontend/shell/src/index.ts"], "cases-frontend-view": ["libs/cases/frontend/view/src/index.ts"], + "cases-shared-models": ["libs/cases/shared/models/src/index.ts"], "common-backend-insight-hub": ["libs/common/backend/insight-hub/src/index.ts"], "common-backend-models": ["libs/common/backend/models/src/index.ts"], "common-backend-prisma": ["libs/common/backend/prisma/src/index.ts"], From 517edaad5c56c2fd564acb3c6680351e2bafc124 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 14:56:30 +0200 Subject: [PATCH 03/12] fix: case Service test Signed-off-by: Jonas --- .../case-management/src/lib/dto/body.dto.ts | 8 + .../src/lib/services/case.service.spec.ts | 75 ++++++++- .../src/lib/services/case.service.ts | 158 +++++++++++++----- .../case-browse/case-browse.component.ts | 66 ++++++++ .../open-cases/open-cases.component.ts | 4 +- .../src/lib/interfaces/body.interface.ts | 3 +- .../src/lib/interfaces/response.interface.ts | 1 + 7 files changed, 260 insertions(+), 55 deletions(-) diff --git a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts index 02d9c5ce..9b488e7a 100644 --- a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts +++ b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts @@ -99,6 +99,14 @@ export class createCaseBodyDto implements ICreateCaseBody { @IsString() @IsNotEmpty() eTag: string; + + /** + * The asset which the case belongs to + * @example "1702540787672" + */ + @IsString() + @IsNotEmpty() + assetId: string; } /** diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts index 9465b562..07ea0187 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts @@ -11,6 +11,7 @@ import { import { Test, TestingModule } from '@nestjs/testing'; import { ECasePriority, ECaseStatus, ECaseType, ICreateCaseBody } from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; +import { omit } from 'lodash'; import { firstValueFrom } from 'rxjs'; import { XdCaseController } from '../controller/case.controller'; @@ -37,6 +38,10 @@ describe('CaseController', () => { const prismaServiceMock = { onModuleInit: jest.fn(), + asset: { + findUnique: jest.fn(), + }, + case: { create: jest.fn(), findMany: jest.fn(), @@ -69,26 +74,46 @@ describe('CaseController', () => { const createResult = { ...createCaseModel, id: faker.number.int(), + assetAssetId: faker.string.uuid(), modifiedBy: '', createdAt: new Date(), updatedAt: new Date(), }; const spy = jest.spyOn(prisma.case, 'create').mockResolvedValue(createResult); + const findUniqeSpy = jest.spyOn(prisma.asset, 'findUnique').mockResolvedValue({ + assetId: createResult.assetId, + description: faker.lorem.sentence(), + name: faker.lorem.sentence(), + typeId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + variables: {}, + }); // act const result = await firstValueFrom(service.createCase(createCaseModel)); // assert expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith({ data: createCaseModel }); - expect(result).toEqual({ ...createResult, overdue: false }); + expect(spy).toHaveBeenCalledWith({ + data: { assetId: createCaseModel.assetId, ...omit(createCaseModel, 'assetId') }, + }); + + const expectedResult = { + ...omit(createResult, 'assetAssetId'), + assetId: createResult.assetAssetId, + overdue: false, + }; + + expect(result).toEqual({ ...expectedResult, overdue: false }); }); it('createCase => should create a new case by given data with overdue false', async () => { const createResult = { id: faker.number.int(), ...createCaseModel, + assetAssetId: faker.string.uuid(), modifiedBy: '', createdAt: new Date(), updatedAt: new Date(), @@ -96,11 +121,21 @@ describe('CaseController', () => { jest.spyOn(prisma.case, 'create').mockResolvedValue(createResult); + jest.spyOn(prisma.asset, 'findUnique').mockResolvedValue({ + assetId: createResult.assetId, + description: faker.lorem.sentence(), + name: faker.lorem.sentence(), + typeId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + variables: {}, + }); + // act const result = await firstValueFrom(service.createCase(createCaseModel)); // assert - expect(result).toEqual({ ...createResult, overdue: false }); + expect(result).toEqual(expect.objectContaining({ overdue: false })); }); it('getAllCases => should return an array of cases', async () => { @@ -108,6 +143,7 @@ describe('CaseController', () => { id: faker.number.int(), ...createCaseModel, modifiedBy: '', + assetAssetId: faker.string.uuid(), createdAt: new Date(), updatedAt: new Date(), }; @@ -126,7 +162,11 @@ describe('CaseController', () => { // assert expect(spy).toHaveBeenCalledTimes(1); expect(result).toEqual( - getResultMany.map((item, index) => ({ ...item, overdue: index !== 2 })), + getResultMany.map((item) => ({ + ...omit(item, 'assetAssetId'), + assetId: item.assetAssetId, + overdue: item.dueDate < new Date(), + })), ); }); @@ -135,6 +175,7 @@ describe('CaseController', () => { id: faker.number.int(), ...createCaseModel, modifiedBy: '', + assetAssetId: faker.string.uuid(), createdAt: new Date(), updatedAt: new Date(), }; @@ -144,9 +185,15 @@ describe('CaseController', () => { // act const result = await firstValueFrom(service.getCaseById(getResult.id)); + const expectedResult = { + ...omit(getResult, 'assetAssetId'), + assetId: getResult.assetAssetId, + overdue: false, + }; + // assert expect(spy).toHaveBeenCalledTimes(1); - expect(result).toEqual({ ...getResult, overdue: false }); + expect(result).toEqual(expectedResult); }); it('updateCaseById => should find a case by ID and update it', async () => { @@ -154,6 +201,7 @@ describe('CaseController', () => { id: faker.number.int(), ...createCaseModel, modifiedBy: '', + assetAssetId: faker.string.uuid(), createdAt: new Date(), updatedAt: new Date(), }; @@ -163,15 +211,23 @@ describe('CaseController', () => { // act const result = await firstValueFrom(service.updateCaseById(updateResult.id, updateResult)); + console.log('result', result); + + const expectedResult = { + ...omit(updateResult, 'assetAssetId'), + assetId: updateResult.assetAssetId, + overdue: false, + }; // assert expect(spy).toHaveBeenCalledTimes(1); - expect(result).toEqual({ ...updateResult, overdue: false }); + expect(result).toEqual(expectedResult); }); it('deleteCaseById => should find a case by ID and delete it', async () => { const deleteResult = { id: faker.number.int(), ...createCaseModel, + assetAssetId: faker.string.uuid(), modifiedBy: '', createdAt: new Date(), updatedAt: new Date(), @@ -182,8 +238,13 @@ describe('CaseController', () => { // act const result = await firstValueFrom(service.deleteCaseById(deleteResult.id)); + const expectedResult = { + ...omit(deleteResult, 'assetAssetId'), + assetId: deleteResult.assetAssetId, + overdue: false, + }; // assert expect(spy).toHaveBeenCalledTimes(1); - expect(result).toEqual({ ...deleteResult, overdue: false }); + expect(result).toEqual(expectedResult); }); }); diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.ts b/libs/cases/backend/case-management/src/lib/services/case.service.ts index fc636d68..b21b1685 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.ts @@ -1,7 +1,15 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { ICaseResponse, ICreateCaseBody, IUpdateCaseBody } from 'cases-shared-models'; +import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; +import { CasePriority, CaseStatus, CaseType } from '@prisma/client'; +import { + ECasePriority, + ECaseStatus, + ECaseType, + ICaseResponse, + ICreateCaseBody, + IUpdateCaseBody, +} from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; -import { from, map, Observable } from 'rxjs'; +import { from, map, Observable, switchMap } from 'rxjs'; /** * handles database operations and contains business logic @@ -21,17 +29,86 @@ export class XdCaseService { public getAllCases(): Observable { return from(this.prismaService.case.findMany()).pipe( map((items) => - items.map( - (item) => - ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse, - ), + items.map((item) => { + const { assetAssetId, type, status, priority, ...rest } = item; + + return { + ...rest, + type: type as unknown as ECaseType, + status: status as unknown as ECaseStatus, + priority: priority as unknown as ECasePriority, + + assetId: assetAssetId, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }; + }), ), ); } + private convertCaseType(prismaType: CaseType): ECaseType { + switch (prismaType) { + case CaseType.PLANNED: + return ECaseType.PLANNED; + case CaseType.INCIDENT: + return ECaseType.INCIDENT; + case CaseType.ANNOTATION: + return ECaseType.ANNOTATION; + default: + return ECaseType.PLANNED; + } + } + + private convertCaseStatus(prismaStatus: CaseStatus): ECaseStatus { + switch (prismaStatus) { + case CaseStatus.OPEN: + return ECaseStatus.OPEN; + case CaseStatus.INPROGRESS: + return ECaseStatus.INPROGRESS; + case CaseStatus.ONHOLD: + return ECaseStatus.ONHOLD; + case CaseStatus.DONE: + return ECaseStatus.DONE; + case CaseStatus.OVERDUE: + return ECaseStatus.OVERDUE; + case CaseStatus.CANCELLED: + return ECaseStatus.CANCELLED; + case CaseStatus.ARCHIVED: + return ECaseStatus.ARCHIVED; + default: + return ECaseStatus.OPEN; + } + } + + private convertCasePriority(prismaPriority: CasePriority): ECasePriority { + switch (prismaPriority) { + case CasePriority.LOW: + return ECasePriority.LOW; + case CasePriority.MEDIUM: + return ECasePriority.MEDIUM; + case CasePriority.HIGH: + return ECasePriority.HIGH; + case CasePriority.EMERGENCY: + return ECasePriority.EMERGENCY; + default: + return ECasePriority.LOW; + } + } + + private responseFromItem(item: any): ICaseResponse { + const { assetAssetId, type, status, priority, ...rest } = item; + + return { + ...rest, + type: this.convertCaseType(type), + status: this.convertCaseStatus(status), + priority: this.convertCasePriority(priority), + + assetId: assetAssetId, + overdue: Date.now() > new Date(item.dueDate).getTime(), + }; + } + /** * retrieves a single case by ID * Maps the found case to an ICaseResponse object and returns an Observable @@ -42,13 +119,10 @@ export class XdCaseService { return from(this.prismaService.case.findUnique({ where: { id } })).pipe( map((item) => { if (!item) { - throw new Error('Case not found'); + throw new HttpException('Case not found', HttpStatus.NOT_FOUND); } - return { - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - } as ICaseResponse; + return this.responseFromItem(item); }), ); } @@ -58,22 +132,32 @@ export class XdCaseService { * @param {ICaseRequest} caseData represents a case * @returns {Observable} */ - public createCase(caseData: ICreateCaseBody): Observable { + public createCase({ assetId, ...caseData }: ICreateCaseBody): Observable { return from( - this.prismaService.case.create({ - data: { + this.prismaService.asset.findUnique({ + where: { assetId }, + }), + ).pipe( + map((asset) => { + if (!asset) { + throw new HttpException('Asset not found', HttpStatus.NOT_FOUND); + } + + return asset; + }), + switchMap((asset) => { + const data = { ...caseData, + assetAssetId: asset.assetId, dueDate: new Date(caseData.dueDate), - }, + }; + + return from( + this.prismaService.case.create({ + data, + }), + ).pipe(map((item) => this.responseFromItem(item))); }), - ).pipe( - map( - (item) => - ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse, - ), ); } @@ -88,15 +172,7 @@ export class XdCaseService { where: { id }, data: caseData, }), - ).pipe( - map( - (item) => - ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse, - ), - ); + ).pipe(map((item) => this.responseFromItem(item))); } /** @@ -109,14 +185,6 @@ export class XdCaseService { this.prismaService.case.delete({ where: { id }, }), - ).pipe( - map( - (item) => - ({ - ...item, - overdue: Date.now() > new Date(item.dueDate).getTime(), - }) as ICaseResponse, - ), - ); + ).pipe(map((item) => this.responseFromItem(item))); } } diff --git a/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts b/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts index 1d773a2d..f4199c03 100644 --- a/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts +++ b/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts @@ -4,6 +4,7 @@ import { Component, computed, inject, +<<<<<<< HEAD signal, ViewEncapsulation, } from '@angular/core'; @@ -11,18 +12,33 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { XdCasesFacade } from '@frontend/cases/frontend/domain'; import { ICaseResponse } from '@frontend/cases/shared/models'; +======= + ViewEncapsulation, +} from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { RouterLink } from '@angular/router'; +import { XdBrowseFacadesService } from '@frontend/cases/frontend/domain'; +>>>>>>> 38fb606 (fix: case Service test) import { IxModule } from '@siemens/ix-angular'; +import { ICaseResponse } from 'cases-shared-models'; @Component({ selector: 'lib-brows-cases', standalone: true, +<<<<<<< HEAD imports: [ CommonModule, IxModule, RouterLink ], templateUrl: './case-browse.component.html', styleUrls: [ './case-browse.component.scss' ], +======= + imports: [CommonModule, IxModule, RouterLink], + templateUrl: './case-browse.component.html', + styleUrls: ['./case-browse.component.scss'], +>>>>>>> 38fb606 (fix: case Service test) encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CaseBrowseComponent { +<<<<<<< HEAD protected readonly showPriorityEmergency = signal(true); protected readonly showPriorityHigh = signal(true); protected readonly showPriorityMedium = signal(true); @@ -136,6 +152,53 @@ export class CaseBrowseComponent { protected route: ActivatedRoute ) {} +======= + protected readonly _browseFacade = inject(XdBrowseFacadesService); + protected readonly _cases = toSignal(this._browseFacade.getAllCases()); + protected readonly _sortedCases = computed(() => { + const cases = this._cases(); + if (cases === undefined) { + return; + } + const statusOrder = [ + 'OPEN', + 'INPROGRESS', + 'OVERDUE', + 'ONHOLD', + 'DONE', + 'CANCELLED', + 'ARCHIVED', + ]; + const priorityOrder = ['EMERGENCY', 'HIGH', 'MEDIUM', 'LOW']; + + [...cases].sort((a, b) => { + const statusAIndex = statusOrder.indexOf(a.status); + const statusBIndex = statusOrder.indexOf(b.status); + + if (statusAIndex === statusBIndex) { + const priorityAIndex = priorityOrder.indexOf(a.priority.toUpperCase()); + const priorityBIndex = priorityOrder.indexOf(b.priority.toUpperCase()); + + if (priorityAIndex === priorityBIndex) { + return a.id - b.id; + } else if (priorityAIndex === -1) { + return 1; + } else if (priorityBIndex === -1) { + return -1; + } + return priorityAIndex - priorityBIndex; + } else if (statusAIndex === -1) { + return 1; + } else if (statusBIndex === -1) { + return -1; + } + return statusAIndex - statusBIndex; + }); + + return cases; + }); + +>>>>>>> 38fb606 (fix: case Service test) getStatusClasses(_case: ICaseResponse) { return { emergency: _case.priority === 'EMERGENCY', @@ -148,6 +211,7 @@ export class CaseBrowseComponent { 'status-archived': _case.status === 'ARCHIVED', }; } +<<<<<<< HEAD flipShowPriorityEmergency() { this.showPriorityEmergency.update(value => !value); @@ -193,4 +257,6 @@ export class CaseBrowseComponent { flipShowStatusArchived() { this.showStatusArchived.update(value => !value); } +======= +>>>>>>> 38fb606 (fix: case Service test) } diff --git a/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts index bd98077c..356d6477 100644 --- a/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts +++ b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts @@ -15,7 +15,7 @@ import { ICaseResponse } from 'cases-shared-models'; @Component({ selector: 'lib-open-cases', standalone: true, - imports: [ CommonModule, IxModule, RouterLink ], + imports: [CommonModule, IxModule, RouterLink], templateUrl: './open-cases.component.html', styleUrl: './open-cases.component.scss', encapsulation: ViewEncapsulation.None, @@ -32,7 +32,7 @@ export class OpenCasesComponent { cases .filter((_case: ICaseResponse) => _case.status === 'OPEN') .sort((a, b) => { - const priorityOrder = [ 'EMERGENCY', 'HIGH', 'MEDIUM', 'LOW' ]; + const priorityOrder = ['EMERGENCY', 'HIGH', 'MEDIUM', 'LOW']; const priorityAIndex = priorityOrder.indexOf(a.priority.toUpperCase()); const priorityBIndex = priorityOrder.indexOf(b.priority.toUpperCase()); diff --git a/libs/cases/shared/models/src/lib/interfaces/body.interface.ts b/libs/cases/shared/models/src/lib/interfaces/body.interface.ts index b44e2411..04ae80e1 100644 --- a/libs/cases/shared/models/src/lib/interfaces/body.interface.ts +++ b/libs/cases/shared/models/src/lib/interfaces/body.interface.ts @@ -1,4 +1,3 @@ - /* Interface for creating a new case body */ import { ECasePriority, ECaseStatus, ECaseType } from '../enums'; @@ -25,6 +24,8 @@ export interface ICreateCaseBody { createdBy: string; /* Identifier of the work order */ eTag: string; + /* The asset which the case belongs to */ + assetId: string; } /* Interface for updating an existing case body */ diff --git a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts index 99754ddc..4f779abe 100644 --- a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts +++ b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts @@ -19,4 +19,5 @@ export interface ICaseResponse { modifiedBy: string; updatedAt: Date; overdue: boolean; + assetId: string; } From 5792704c0c16b5e4d34e84014c511f3428c27b39 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 15:15:24 +0200 Subject: [PATCH 04/12] fix: case Service test Signed-off-by: Jonas --- .eslintrc.json | 6 +++++- .../src/lib/interfaces/response.interface.ts | 20 ------------------- .../controller/facilities.controller.spec.ts | 16 ++++++++++----- .../lib/service/facilities.service.spec.ts | 5 +++-- .../src/lib/service/facilities.service.ts | 4 ++-- .../src/lib/interfaces/response.interface.ts | 2 +- 6 files changed, 22 insertions(+), 31 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 22e73226..8f890bad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -62,7 +62,11 @@ }, { "sourceTag": "domain:facilities", - "onlyDependOnLibsWithTags": ["domain:facilities", "domain:common"] + "onlyDependOnLibsWithTags": [ + "domain:facilities", + "domain:common", + "domain:cases" + ] }, { "sourceTag": "domain:common", diff --git a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts index 4f779abe..7f97cf44 100644 --- a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts +++ b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts @@ -1,23 +1,3 @@ import { ECasePriority, ECaseStatus, ECaseType } from '../enums'; // TODO: Add User model -export interface ICaseResponse { - id: number; - handle: string; - dueDate: Date; - title: string; - type: ECaseType; - status: ECaseStatus; - // assignedTo: User, - // assignedToId: Int, - description: string; - source: string; - priority: ECasePriority; - createdBy: string; - createdAt: Date; - eTag: string; - modifiedBy: string; - updatedAt: Date; - overdue: boolean; - assetId: string; -} diff --git a/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts b/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts index 98d70663..455282f1 100644 --- a/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts +++ b/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts @@ -29,8 +29,14 @@ describe('FacilitiesController ', () => { postalCode: faker.location.zipCode(), region: faker.location.state(), streetAddress: faker.location.streetAddress(), +<<<<<<< HEAD } }; +======= + }, + cases: [], + }; +>>>>>>> 151131f (fix: case Service test) beforeAll(async () => { const serviceMock = { @@ -40,7 +46,7 @@ describe('FacilitiesController ', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [ XdFacilitiesController ], + controllers: [XdFacilitiesController], providers: [ { provide: XdFacilitiesService, @@ -60,23 +66,23 @@ describe('FacilitiesController ', () => { it('should return all facilities', async () => { const Spy = jest .spyOn(service, 'getAllFacilitiesFromDB') - .mockReturnValue(of([ facilitiesResponse ])); + .mockReturnValue(of([facilitiesResponse])); const result = await firstValueFrom(controller.getAllFacilities()); console.log(result); expect(Spy).toHaveBeenCalled(); - expect(result).toEqual([ facilitiesResponse ]); + expect(result).toEqual([facilitiesResponse]); }); it('should seed the database', async () => { - const Spy = jest.spyOn(service, 'seedTheDB').mockReturnValue(of([ facilitiesResponse ])); + const Spy = jest.spyOn(service, 'seedTheDB').mockReturnValue(of([facilitiesResponse])); const result = await firstValueFrom(controller.seedTheDB()); expect(Spy).toHaveBeenCalled(); - expect(result).toEqual([ facilitiesResponse ]); + expect(result).toEqual([facilitiesResponse]); }); it('should get a facility by id', async () => { diff --git a/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts b/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts index 19a1fc8b..6034fe18 100644 --- a/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts +++ b/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts @@ -98,15 +98,16 @@ describe('FacilitiesService ', () => { latitude: 1, longitude: 1, }, + cases: [], }; it('should return all facilities', async () => { - const prismaSpy = jest.spyOn(prisma.asset, 'findMany').mockResolvedValue([ resultMock ]); + const prismaSpy = jest.spyOn(prisma.asset, 'findMany').mockResolvedValue([resultMock]); const result = await lastValueFrom(service.getAllFacilitiesFromDB()); expect(prismaSpy).toHaveBeenCalled(); - expect(result).toEqual([ resultMock ]); + expect(result).toEqual([resultMock]); }); it('should return a facility by id', async () => { diff --git a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts index 9a4cc9e8..46294e4b 100644 --- a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts +++ b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts @@ -214,7 +214,7 @@ export class XdFacilitiesService { description: description || '', createdAt: createdAt, updatedAt: updatedAt, - cases: cases.map((c) => ({ caseId: c.id })), + cases: cases.map((c) => ({ id: c.id })), }; }); }), @@ -286,7 +286,7 @@ export class XdFacilitiesService { location: location, createdAt: createdAt, updatedAt: updatedAt, - cases: cases.map((c) => ({ caseId: c.id })), + cases: cases.map((c) => ({ id: c.id })), }; }), ); diff --git a/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts b/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts index 8b8b27a1..14f879ad 100644 --- a/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts +++ b/libs/facilities/shared/models/src/lib/interfaces/response.interface.ts @@ -1,4 +1,4 @@ -import { ICaseResponse } from 'common-shared-models'; +import { ICaseResponse } from 'cases-shared-models'; import { TTimeSeriesData } from 'common-shared-models'; import { EPumpStatus } from '../enums'; From 37e1b29fc89d982628cd7022348bcc960d4e6d55 Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 15:26:53 +0200 Subject: [PATCH 05/12] fix: finished case Signed-off-by: Jonas --- .../case-management/src/lib/dto/body.dto.ts | 6 +++--- .../src/lib/interfaces/response.interface.ts | 20 +++++++++++++++++++ .../migrations/20240619132010_y/migration.sql | 11 ++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20240619132010_y/migration.sql diff --git a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts index 9b488e7a..82ccf6fc 100644 --- a/libs/cases/backend/case-management/src/lib/dto/body.dto.ts +++ b/libs/cases/backend/case-management/src/lib/dto/body.dto.ts @@ -45,14 +45,14 @@ export class createCaseBodyDto implements ICreateCaseBody { */ @IsString() @IsEnum(ECaseType, { - message: 'Type must be one of these values: ' + Object.values(ECaseType).join(', '), + message: 'type must be one of these values: ' + Object.values(ECaseType).join(', '), }) type: ECaseType; @IsString() @IsNotEmpty() @IsEnum(ECaseStatus, { - message: 'Status must be one of these values: ' + Object.values(ECaseStatus).join(', '), + message: 'status must be one of these values: ' + Object.values(ECaseStatus).join(', '), }) status: ECaseStatus; @@ -79,7 +79,7 @@ export class createCaseBodyDto implements ICreateCaseBody { @IsString() @IsNotEmpty() @IsEnum(ECasePriority, { - message: 'Priority must be one of these values: ' + Object.values(ECasePriority).join(', '), + message: 'priority must be one of these values: ' + Object.values(ECasePriority).join(', '), }) priority: ECasePriority; diff --git a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts index 7f97cf44..4f779abe 100644 --- a/libs/cases/shared/models/src/lib/interfaces/response.interface.ts +++ b/libs/cases/shared/models/src/lib/interfaces/response.interface.ts @@ -1,3 +1,23 @@ import { ECasePriority, ECaseStatus, ECaseType } from '../enums'; // TODO: Add User model +export interface ICaseResponse { + id: number; + handle: string; + dueDate: Date; + title: string; + type: ECaseType; + status: ECaseStatus; + // assignedTo: User, + // assignedToId: Int, + description: string; + source: string; + priority: ECasePriority; + createdBy: string; + createdAt: Date; + eTag: string; + modifiedBy: string; + updatedAt: Date; + overdue: boolean; + assetId: string; +} diff --git a/prisma/migrations/20240619132010_y/migration.sql b/prisma/migrations/20240619132010_y/migration.sql new file mode 100644 index 00000000..7b92bf1a --- /dev/null +++ b/prisma/migrations/20240619132010_y/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `assetAssetId` to the `Case` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Case" ADD COLUMN "assetAssetId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "Case" ADD CONSTRAINT "Case_assetAssetId_fkey" FOREIGN KEY ("assetAssetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; From 14446974a7ea4d737fa61f1836d1a7184ad6178d Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 19 Jun 2024 19:03:53 +0200 Subject: [PATCH 06/12] fix: add case error handling Signed-off-by: Jonas --- .../case-management/src/lib/services/case.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.ts b/libs/cases/backend/case-management/src/lib/services/case.service.ts index b21b1685..389d02ea 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.ts @@ -9,7 +9,7 @@ import { IUpdateCaseBody, } from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; -import { from, map, Observable, switchMap } from 'rxjs'; +import { catchError, from, map, Observable, switchMap } from 'rxjs'; /** * handles database operations and contains business logic @@ -185,6 +185,11 @@ export class XdCaseService { this.prismaService.case.delete({ where: { id }, }), - ).pipe(map((item) => this.responseFromItem(item))); + ).pipe( + catchError(() => { + throw new HttpException('Case not found', HttpStatus.NOT_FOUND); + }), + map((item) => this.responseFromItem(item)), + ); } } From d498407b251278ecef760d9891394e3829c3f92a Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 20 Jun 2024 12:32:22 +0200 Subject: [PATCH 07/12] fix: migration Signed-off-by: Jonas --- .../migrations/20240619132010_y/migration.sql | 11 ---- .../migrations/20240625190245_/migration.sql | 17 ----- .../migrations/20240625190500_/migration.sql | 9 --- .../migrations/20240625191344_/migration.sql | 19 ------ .../migrations/20240625191546_/migration.sql | 64 ------------------- .../migrations/20240625201404_/migration.sql | 7 -- .../migrations/20240625201916_/migration.sql | 23 ------- .../migrations/20240625210808_/migration.sql | 2 - .../migration.sql | 62 ++++-------------- 9 files changed, 11 insertions(+), 203 deletions(-) delete mode 100644 prisma/migrations/20240619132010_y/migration.sql delete mode 100644 prisma/migrations/20240625190245_/migration.sql delete mode 100644 prisma/migrations/20240625190500_/migration.sql delete mode 100644 prisma/migrations/20240625191344_/migration.sql delete mode 100644 prisma/migrations/20240625191546_/migration.sql delete mode 100644 prisma/migrations/20240625201404_/migration.sql delete mode 100644 prisma/migrations/20240625201916_/migration.sql delete mode 100644 prisma/migrations/20240625210808_/migration.sql rename prisma/migrations/{20240625190103_ => 20240704142642_}/migration.sql (62%) diff --git a/prisma/migrations/20240619132010_y/migration.sql b/prisma/migrations/20240619132010_y/migration.sql deleted file mode 100644 index 7b92bf1a..00000000 --- a/prisma/migrations/20240619132010_y/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - Added the required column `assetAssetId` to the `Case` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Case" ADD COLUMN "assetAssetId" TEXT NOT NULL; - --- AddForeignKey -ALTER TABLE "Case" ADD CONSTRAINT "Case_assetAssetId_fkey" FOREIGN KEY ("assetAssetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240625190245_/migration.sql b/prisma/migrations/20240625190245_/migration.sql deleted file mode 100644 index a315ff5c..00000000 --- a/prisma/migrations/20240625190245_/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `Assetid` on the `Metrics` table. All the data in the column will be lost. - -*/ --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "Metrics_Assetid_fkey"; - --- DropIndex -DROP INDEX "Metrics_Assetid_key"; - --- AlterTable -ALTER TABLE "Asset" ADD COLUMN "dataMetrics" JSONB; - --- AlterTable -ALTER TABLE "Metrics" DROP COLUMN "Assetid"; diff --git a/prisma/migrations/20240625190500_/migration.sql b/prisma/migrations/20240625190500_/migration.sql deleted file mode 100644 index ba30ceee..00000000 --- a/prisma/migrations/20240625190500_/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `dataMetrics` on the `Asset` table. All the data in the column will be lost. - -*/ --- AlterTable -ALTER TABLE "Asset" DROP COLUMN "dataMetrics", -ADD COLUMN "metrics" JSONB; diff --git a/prisma/migrations/20240625191344_/migration.sql b/prisma/migrations/20240625191344_/migration.sql deleted file mode 100644 index 4b4a2a1a..00000000 --- a/prisma/migrations/20240625191344_/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `metrics` on the `Asset` table. All the data in the column will be lost. - - A unique constraint covering the columns `[Assetid]` on the table `Metrics` will be added. If there are existing duplicate values, this will fail. - - Added the required column `Assetid` to the `Metrics` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Asset" DROP COLUMN "metrics"; - --- AlterTable -ALTER TABLE "Metrics" ADD COLUMN "Assetid" TEXT NOT NULL; - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_Assetid_key" ON "Metrics"("Assetid"); - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "Metrics_Assetid_fkey" FOREIGN KEY ("Assetid") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240625191546_/migration.sql b/prisma/migrations/20240625191546_/migration.sql deleted file mode 100644 index 183a6fa1..00000000 --- a/prisma/migrations/20240625191546_/migration.sql +++ /dev/null @@ -1,64 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `flowId` on the `Metrics` table. All the data in the column will be lost. - - You are about to drop the column `motorCurrentId` on the `Metrics` table. All the data in the column will be lost. - - You are about to drop the column `pressureInId` on the `Metrics` table. All the data in the column will be lost. - - You are about to drop the column `pressureOutId` on the `Metrics` table. All the data in the column will be lost. - - You are about to drop the column `stuffingBoxTemperatureId` on the `Metrics` table. All the data in the column will be lost. - - You are about to drop the `MetricStats` table. If the table is not empty, all the data it contains will be lost. - - Added the required column `coefficientOfVariation` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `max` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `mean` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `min` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `name` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `standardDeviation` to the `Metrics` table without a default value. This is not possible if the table is not empty. - - Added the required column `variance` to the `Metrics` table without a default value. This is not possible if the table is not empty. - -*/ --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "Flow_fkey"; - --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "MotorCurrent_fkey"; - --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "PressureIn_fkey"; - --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "PressureOut_fkey"; - --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "StuffingBoxTemperature_fkey"; - --- DropIndex -DROP INDEX "Metrics_flowId_key"; - --- DropIndex -DROP INDEX "Metrics_motorCurrentId_key"; - --- DropIndex -DROP INDEX "Metrics_pressureInId_key"; - --- DropIndex -DROP INDEX "Metrics_pressureOutId_key"; - --- DropIndex -DROP INDEX "Metrics_stuffingBoxTemperatureId_key"; - --- AlterTable -ALTER TABLE "Metrics" DROP COLUMN "flowId", -DROP COLUMN "motorCurrentId", -DROP COLUMN "pressureInId", -DROP COLUMN "pressureOutId", -DROP COLUMN "stuffingBoxTemperatureId", -ADD COLUMN "coefficientOfVariation" DOUBLE PRECISION NOT NULL, -ADD COLUMN "max" DOUBLE PRECISION NOT NULL, -ADD COLUMN "mean" DOUBLE PRECISION NOT NULL, -ADD COLUMN "min" DOUBLE PRECISION NOT NULL, -ADD COLUMN "name" TEXT NOT NULL, -ADD COLUMN "standardDeviation" DOUBLE PRECISION NOT NULL, -ADD COLUMN "variance" DOUBLE PRECISION NOT NULL; - --- DropTable -DROP TABLE "MetricStats"; diff --git a/prisma/migrations/20240625201404_/migration.sql b/prisma/migrations/20240625201404_/migration.sql deleted file mode 100644 index dda7aed7..00000000 --- a/prisma/migrations/20240625201404_/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- AlterTable -ALTER TABLE "Metrics" ALTER COLUMN "coefficientOfVariation" DROP NOT NULL, -ALTER COLUMN "max" DROP NOT NULL, -ALTER COLUMN "mean" DROP NOT NULL, -ALTER COLUMN "min" DROP NOT NULL, -ALTER COLUMN "standardDeviation" DROP NOT NULL, -ALTER COLUMN "variance" DROP NOT NULL; diff --git a/prisma/migrations/20240625201916_/migration.sql b/prisma/migrations/20240625201916_/migration.sql deleted file mode 100644 index 6d19c2b2..00000000 --- a/prisma/migrations/20240625201916_/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `Assetid` on the `Metrics` table. All the data in the column will be lost. - - A unique constraint covering the columns `[assetId]` on the table `Metrics` will be added. If there are existing duplicate values, this will fail. - - Added the required column `assetId` to the `Metrics` table without a default value. This is not possible if the table is not empty. - -*/ --- DropForeignKey -ALTER TABLE "Metrics" DROP CONSTRAINT "Metrics_Assetid_fkey"; - --- DropIndex -DROP INDEX "Metrics_Assetid_key"; - --- AlterTable -ALTER TABLE "Metrics" DROP COLUMN "Assetid", -ADD COLUMN "assetId" TEXT NOT NULL; - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_assetId_key" ON "Metrics"("assetId"); - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "Metrics_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240625210808_/migration.sql b/prisma/migrations/20240625210808_/migration.sql deleted file mode 100644 index 56822e4f..00000000 --- a/prisma/migrations/20240625210808_/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- DropIndex -DROP INDEX "Metrics_assetId_key"; diff --git a/prisma/migrations/20240625190103_/migration.sql b/prisma/migrations/20240704142642_/migration.sql similarity index 62% rename from prisma/migrations/20240625190103_/migration.sql rename to prisma/migrations/20240704142642_/migration.sql index 25db1000..67cea79a 100644 --- a/prisma/migrations/20240625190103_/migration.sql +++ b/prisma/migrations/20240704142642_/migration.sql @@ -79,6 +79,7 @@ CREATE TABLE "Case" ( "modifiedBy" TEXT NOT NULL DEFAULT '', "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, + "assetAssetId" TEXT NOT NULL, CONSTRAINT "Case_pkey" PRIMARY KEY ("id") ); @@ -86,55 +87,26 @@ CREATE TABLE "Case" ( -- CreateTable CREATE TABLE "Metrics" ( "id" SERIAL NOT NULL, - "motorCurrentId" INTEGER NOT NULL, - "pressureOutId" INTEGER NOT NULL, - "stuffingBoxTemperatureId" INTEGER NOT NULL, - "pressureInId" INTEGER NOT NULL, - "flowId" INTEGER NOT NULL, - "Assetid" TEXT NOT NULL, + "min" DOUBLE PRECISION, + "max" DOUBLE PRECISION, + "mean" DOUBLE PRECISION, + "variance" DOUBLE PRECISION, + "standardDeviation" DOUBLE PRECISION, + "coefficientOfVariation" DOUBLE PRECISION, + "name" TEXT NOT NULL, + "assetId" TEXT NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, CONSTRAINT "Metrics_pkey" PRIMARY KEY ("id") ); --- CreateTable -CREATE TABLE "MetricStats" ( - "id" SERIAL NOT NULL, - "min" DOUBLE PRECISION NOT NULL, - "max" DOUBLE PRECISION NOT NULL, - "mean" DOUBLE PRECISION NOT NULL, - "variance" DOUBLE PRECISION NOT NULL, - "standardDeviation" DOUBLE PRECISION NOT NULL, - "coefficientOfVariation" DOUBLE PRECISION NOT NULL, - - CONSTRAINT "MetricStats_pkey" PRIMARY KEY ("id") -); - -- CreateIndex CREATE UNIQUE INDEX "TimeSeriesItem_assetId_propertySetName_key" ON "TimeSeriesItem"("assetId", "propertySetName"); -- CreateIndex CREATE UNIQUE INDEX "AssetLocation_Assetid_key" ON "AssetLocation"("Assetid"); --- CreateIndex -CREATE UNIQUE INDEX "Metrics_motorCurrentId_key" ON "Metrics"("motorCurrentId"); - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_pressureOutId_key" ON "Metrics"("pressureOutId"); - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_stuffingBoxTemperatureId_key" ON "Metrics"("stuffingBoxTemperatureId"); - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_pressureInId_key" ON "Metrics"("pressureInId"); - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_flowId_key" ON "Metrics"("flowId"); - --- CreateIndex -CREATE UNIQUE INDEX "Metrics_Assetid_key" ON "Metrics"("Assetid"); - -- AddForeignKey ALTER TABLE "TimeSeriesItem" ADD CONSTRAINT "TimeSeriesItem_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; @@ -145,19 +117,7 @@ ALTER TABLE "TimeSeriesDataItem" ADD CONSTRAINT "TimeSeriesDataItem_timeSeriesIt ALTER TABLE "AssetLocation" ADD CONSTRAINT "AssetLocation_Assetid_fkey" FOREIGN KEY ("Assetid") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "MotorCurrent_fkey" FOREIGN KEY ("motorCurrentId") REFERENCES "MetricStats"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "PressureOut_fkey" FOREIGN KEY ("pressureOutId") REFERENCES "MetricStats"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "StuffingBoxTemperature_fkey" FOREIGN KEY ("stuffingBoxTemperatureId") REFERENCES "MetricStats"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "PressureIn_fkey" FOREIGN KEY ("pressureInId") REFERENCES "MetricStats"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "Flow_fkey" FOREIGN KEY ("flowId") REFERENCES "MetricStats"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "Case" ADD CONSTRAINT "Case_assetAssetId_fkey" FOREIGN KEY ("assetAssetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "Metrics" ADD CONSTRAINT "Metrics_Assetid_fkey" FOREIGN KEY ("Assetid") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "Metrics" ADD CONSTRAINT "Metrics_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "Asset"("assetId") ON DELETE RESTRICT ON UPDATE CASCADE; From 944357fa1457a6108679ebe3a979d4449c03b6cc Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 20 Jun 2024 12:44:05 +0200 Subject: [PATCH 08/12] feat: adjust seeding script --- prisma/seed.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/prisma/seed.ts b/prisma/seed.ts index 5c3c03db..841c87af 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -144,13 +144,6 @@ async function seedSingleFacility({ await prisma.timeSeriesDataItem.createMany({ data: [newPumpData, newEnvData].flat(), }); -} - -async function main() { - // Seed database with facility data - for (let i = 0; i < facilityNames.length; i++) { - await seedSingleFacility({ ...facilityNames[i], index: i }); - } // create new case data from JSON file const newCaseData = caseData.map((data: any) => { @@ -166,6 +159,7 @@ async function main() { priority: data.priority, createdBy: data.createdBy, eTag: data.eTag, + assetAssetId: asset.assetId, }; }); @@ -175,6 +169,13 @@ async function main() { }); } +async function main() { + // Seed database with facility data + for (let i = 0; i < facilityNames.length; i++) { + await seedSingleFacility({ ...facilityNames[i], index: i }); + } +} + main().catch((e) => { console.error(e); From 7c20602e0e4ee618c031d1626f23da4ea8eaace9 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 20 Jun 2024 12:47:47 +0200 Subject: [PATCH 09/12] feat: frontend adjustments needed --- .../src/lib/components/create-case/create-case.component.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts index e407c7de..eed72f22 100644 --- a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts +++ b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts @@ -190,6 +190,12 @@ export class CreateCaseComponent implements OnInit { priority: formData.selectPriority, createdBy: formData.email, eTag: faker.string.alphanumeric(10), + + /** + * @PatrickSchm1dt + * I'm not sure if this is the correct way to get the assetId + */ + assetId: formData.selectFacility, }; } } From aff07617a97265ec71e1e66178f5c45e4b20de9f Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 20 Jun 2024 13:39:00 +0200 Subject: [PATCH 10/12] feat: adjust seeding script Co-authored-by: Patrick Schmidt Signed-off-by: Jonas --- prisma/seed.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/prisma/seed.ts b/prisma/seed.ts index 841c87af..85c27369 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -47,8 +47,13 @@ async function seedSingleFacility({ indicatorMsg, location: { create: { - latitude: 0, - longitude: 0, + latitude: 37.7749, + longitude: 122.4194, + country: 'United States', + region: 'California', + streetAddress: '123 Main St', + postalCode: '94105', + locality: 'San Francisco', }, }, variables: {}, From 96f5e721166081ea12dd6c75ed652b738d6f866b Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 20 Jun 2024 14:01:38 +0200 Subject: [PATCH 11/12] feat: remove comment Co-authored-by: Patrick Schmidt Signed-off-by: Jonas --- .../src/lib/components/create-case/create-case.component.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts index eed72f22..92967686 100644 --- a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts +++ b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts @@ -190,11 +190,6 @@ export class CreateCaseComponent implements OnInit { priority: formData.selectPriority, createdBy: formData.email, eTag: faker.string.alphanumeric(10), - - /** - * @PatrickSchm1dt - * I'm not sure if this is the correct way to get the assetId - */ assetId: formData.selectFacility, }; } From ebf3286c7f7665f47d688b114e7e917ec85c3d9a Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 4 Jul 2024 16:48:36 +0200 Subject: [PATCH 12/12] fix: case tests Signed-off-by: Jonas --- .../lib/controller/case.controller.spec.ts | 14 +- .../src/lib/services/case.service.spec.ts | 16 +- .../cases-request.service.spec.ts | 4 +- .../infrastructure/cases-request.service.ts | 23 +- .../case-browse/case-browse.component.ts | 158 +++------ .../create-case/create-case.component.ts | 2 +- .../deleteModal.component.spec.ts | 6 +- .../detail-case/detail-case.component.ts | 318 +++++++++--------- .../open-cases/open-cases.component.ts | 4 +- .../src/lib/swagger-document-builder.ts | 2 +- .../controller/facilities.controller.spec.ts | 21 +- .../lib/service/facilities.service.spec.ts | 4 +- .../src/lib/service/facilities.service.ts | 35 +- .../lib/services/timeseries.service.spec.ts | 6 + 14 files changed, 263 insertions(+), 350 deletions(-) diff --git a/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts b/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts index 844a9ef8..dce2f9f1 100644 --- a/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts +++ b/libs/cases/backend/case-management/src/lib/controller/case.controller.spec.ts @@ -1,14 +1,4 @@ import { faker } from '@faker-js/faker'; -<<<<<<< HEAD -import { - ECasePriority, - ECaseStatus, - ECaseType, - ICaseResponse, - ICreateCaseBody, -} from '@frontend/cases/shared/models'; -======= ->>>>>>> 3254e7e (fix: cases service) import { Test, TestingModule } from '@nestjs/testing'; import { ECasePriority, @@ -50,7 +40,7 @@ describe('CaseController', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [XdCaseController], + controllers: [ XdCaseController ], providers: [ { provide: XdCaseService, @@ -68,7 +58,7 @@ describe('CaseController', () => { }); it('should call getAllCases', async () => { - const getAllReturnValue = [returnValue] as ICaseResponse[]; + const getAllReturnValue = [ returnValue ] as ICaseResponse[]; const spy = jest.spyOn(service, 'getAllCases').mockReturnValue(of(getAllReturnValue)); const result = await firstValueFrom(controller.getAllCases()); diff --git a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts index 07ea0187..deb38e29 100644 --- a/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts +++ b/libs/cases/backend/case-management/src/lib/services/case.service.spec.ts @@ -1,13 +1,5 @@ import { faker } from '@faker-js/faker'; -<<<<<<< HEAD -import { - ECasePriority, - ECaseStatus, - ECaseType, - ICreateCaseBody, -} from '@frontend/cases/shared/models'; -======= ->>>>>>> 3254e7e (fix: cases service) +import { EPumpStatus } from '@frontend/facilities/shared/models'; import { Test, TestingModule } from '@nestjs/testing'; import { ECasePriority, ECaseStatus, ECaseType, ICreateCaseBody } from 'cases-shared-models'; import { PrismaService } from 'common-backend-prisma'; @@ -52,7 +44,7 @@ describe('CaseController', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [XdCaseController], + controllers: [ XdCaseController ], providers: [ XdCaseService, { @@ -89,6 +81,8 @@ describe('CaseController', () => { createdAt: new Date(), updatedAt: new Date(), variables: {}, + status: faker.helpers.enumValue(EPumpStatus), + indicatorMsg: faker.string.sample(), }); // act @@ -129,6 +123,8 @@ describe('CaseController', () => { createdAt: new Date(), updatedAt: new Date(), variables: {}, + status: faker.helpers.enumValue(EPumpStatus), + indicatorMsg: faker.string.sample(), }); // act diff --git a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts index 9c653d10..dae313cd 100644 --- a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts +++ b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.spec.ts @@ -13,7 +13,7 @@ describe('XdCasesRequestService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], + imports: [ HttpClientTestingModule ], providers: [ XdCasesRequestService, { @@ -45,7 +45,7 @@ describe('XdCasesRequestService', () => { describe('getTimeSeries', () => { it('should forward the request to the backend', async () => { const params = { id: faker.number.int() } as ICaseParams; - const mockResponse: ICaseParams[] = [{ id: faker.number.int() }] as ICaseParams[]; + const mockResponse: ICaseParams[] = [ { id: faker.number.int() } ] as ICaseParams[]; const spy = jest.spyOn(httpClient, 'get').mockReturnValue(of(mockResponse)); diff --git a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts index c8228ec5..dfddc0bb 100644 --- a/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts +++ b/libs/cases/frontend/domain/src/lib/infrastructure/cases-request.service.ts @@ -33,19 +33,16 @@ export class XdCasesRequestService { public createCase(body: ICreateCaseBody) { return this._httpClient.post('/api/case', body); } -<<<<<<< HEAD - public updateCase(params: ICaseParams, body: ICreateCaseBody) { - return this._httpClient.put(`/api/case/${params.id}`, body); - } + public updateCase(params: ICaseParams, body: ICreateCaseBody) { + return this._httpClient.put(`/api/case/${params.id}`, body); + } - /** - * Delete case by Id - * @param params - */ - public deleteCase(params: ICaseParams) { - return this._httpClient.delete(`/api/case/${params.id}`); - } -======= ->>>>>>> 3254e7e (fix: cases service) + /** + * Delete case by Id + * @param params + */ + public deleteCase(params: ICaseParams) { + return this._httpClient.delete(`/api/case/${params.id}`); + } } diff --git a/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts b/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts index f4199c03..17ff1bbc 100644 --- a/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts +++ b/libs/cases/frontend/view/src/lib/components/case-browse/case-browse.component.ts @@ -4,41 +4,25 @@ import { Component, computed, inject, -<<<<<<< HEAD signal, ViewEncapsulation, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { XdCasesFacade } from '@frontend/cases/frontend/domain'; -import { ICaseResponse } from '@frontend/cases/shared/models'; -======= - ViewEncapsulation, -} from '@angular/core'; -import { toSignal } from '@angular/core/rxjs-interop'; -import { RouterLink } from '@angular/router'; -import { XdBrowseFacadesService } from '@frontend/cases/frontend/domain'; ->>>>>>> 38fb606 (fix: case Service test) import { IxModule } from '@siemens/ix-angular'; import { ICaseResponse } from 'cases-shared-models'; @Component({ selector: 'lib-brows-cases', standalone: true, -<<<<<<< HEAD imports: [ CommonModule, IxModule, RouterLink ], templateUrl: './case-browse.component.html', styleUrls: [ './case-browse.component.scss' ], -======= - imports: [CommonModule, IxModule, RouterLink], - templateUrl: './case-browse.component.html', - styleUrls: ['./case-browse.component.scss'], ->>>>>>> 38fb606 (fix: case Service test) encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CaseBrowseComponent { -<<<<<<< HEAD protected readonly showPriorityEmergency = signal(true); protected readonly showPriorityHigh = signal(true); protected readonly showPriorityMedium = signal(true); @@ -147,58 +131,11 @@ export class CaseBrowseComponent { return cases; }); - constructor( - protected router: Router, - protected route: ActivatedRoute - ) {} - -======= - protected readonly _browseFacade = inject(XdBrowseFacadesService); - protected readonly _cases = toSignal(this._browseFacade.getAllCases()); - protected readonly _sortedCases = computed(() => { - const cases = this._cases(); - if (cases === undefined) { - return; - } - const statusOrder = [ - 'OPEN', - 'INPROGRESS', - 'OVERDUE', - 'ONHOLD', - 'DONE', - 'CANCELLED', - 'ARCHIVED', - ]; - const priorityOrder = ['EMERGENCY', 'HIGH', 'MEDIUM', 'LOW']; - - [...cases].sort((a, b) => { - const statusAIndex = statusOrder.indexOf(a.status); - const statusBIndex = statusOrder.indexOf(b.status); - - if (statusAIndex === statusBIndex) { - const priorityAIndex = priorityOrder.indexOf(a.priority.toUpperCase()); - const priorityBIndex = priorityOrder.indexOf(b.priority.toUpperCase()); - - if (priorityAIndex === priorityBIndex) { - return a.id - b.id; - } else if (priorityAIndex === -1) { - return 1; - } else if (priorityBIndex === -1) { - return -1; - } - return priorityAIndex - priorityBIndex; - } else if (statusAIndex === -1) { - return 1; - } else if (statusBIndex === -1) { - return -1; - } - return statusAIndex - statusBIndex; - }); - - return cases; - }); + constructor( + protected router: Router, + protected route: ActivatedRoute, + ) {} ->>>>>>> 38fb606 (fix: case Service test) getStatusClasses(_case: ICaseResponse) { return { emergency: _case.priority === 'EMERGENCY', @@ -211,52 +148,49 @@ export class CaseBrowseComponent { 'status-archived': _case.status === 'ARCHIVED', }; } -<<<<<<< HEAD - flipShowPriorityEmergency() { - this.showPriorityEmergency.update(value => !value); - } - flipShowPriorityHigh() { - this.showPriorityHigh.update(value => !value); - } - flipShowPriorityMedium() { - this.showPriorityMedium.update(value => !value); - } - flipShowPriorityLow() { - this.showPriorityLow.update(value => !value); - } + flipShowPriorityEmergency() { + this.showPriorityEmergency.update((value) => !value); + } + flipShowPriorityHigh() { + this.showPriorityHigh.update((value) => !value); + } + flipShowPriorityMedium() { + this.showPriorityMedium.update((value) => !value); + } + flipShowPriorityLow() { + this.showPriorityLow.update((value) => !value); + } - flipShowTypePlanned() { - this.showTypePlanned.update(value => !value); - } - flipShowTypeIncident() { - this.showTypeIncident.update(value => !value); - } - flipShowTypeAnnotation() { - this.showTypeAnnotation.update(value => !value); - } + flipShowTypePlanned() { + this.showTypePlanned.update((value) => !value); + } + flipShowTypeIncident() { + this.showTypeIncident.update((value) => !value); + } + flipShowTypeAnnotation() { + this.showTypeAnnotation.update((value) => !value); + } - flipShowStatusOpen() { - this.showStatusOpen.update(value => !value); - } - flipShowStatusInProgress() { - this.showStatusInProgress.update(value => !value); - } - flipShowStatusOnHold() { - this.showStatusOnHold.update(value => !value); - } - flipShowStatusDone() { - this.showStatusDone.update(value => !value); - } - flipShowStatusOverdue() { - this.showStatusOverdue.update(value => !value); - } - flipShowStatusCancelled() { - this.showStatusCancelled.update(value => !value); - } - flipShowStatusArchived() { - this.showStatusArchived.update(value => !value); - } -======= ->>>>>>> 38fb606 (fix: case Service test) + flipShowStatusOpen() { + this.showStatusOpen.update((value) => !value); + } + flipShowStatusInProgress() { + this.showStatusInProgress.update((value) => !value); + } + flipShowStatusOnHold() { + this.showStatusOnHold.update((value) => !value); + } + flipShowStatusDone() { + this.showStatusDone.update((value) => !value); + } + flipShowStatusOverdue() { + this.showStatusOverdue.update((value) => !value); + } + flipShowStatusCancelled() { + this.showStatusCancelled.update((value) => !value); + } + flipShowStatusArchived() { + this.showStatusArchived.update((value) => !value); + } } diff --git a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts index 92967686..a97786ca 100644 --- a/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts +++ b/libs/cases/frontend/view/src/lib/components/create-case/create-case.component.ts @@ -22,7 +22,7 @@ import { DateDropdownWrapperComponent } from './date-dropdown-accessor'; @Component({ selector: 'lib-create-case', standalone: true, - imports: [CommonModule, IxModule, FormsModule, RouterLink, DateDropdownWrapperComponent], + imports: [ CommonModule, IxModule, FormsModule, RouterLink, DateDropdownWrapperComponent ], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/libs/cases/frontend/view/src/lib/components/detail-case/delete-modal/deleteModal.component.spec.ts b/libs/cases/frontend/view/src/lib/components/detail-case/delete-modal/deleteModal.component.spec.ts index 9c633d6d..da0e5c5c 100644 --- a/libs/cases/frontend/view/src/lib/components/detail-case/delete-modal/deleteModal.component.spec.ts +++ b/libs/cases/frontend/view/src/lib/components/detail-case/delete-modal/deleteModal.component.spec.ts @@ -10,11 +10,11 @@ describe('DeleteModalComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [DeleteModalComponent], - providers: [{ provide: IxActiveModal, useValue: { data: {} } }, { + imports: [ DeleteModalComponent ], + providers: [ { provide: IxActiveModal, useValue: { data: {} } }, { provide: ActivatedRoute, useValue: {}, - }], + } ], }).compileComponents(); fixture = TestBed.createComponent(DeleteModalComponent); diff --git a/libs/cases/frontend/view/src/lib/components/detail-case/detail-case.component.ts b/libs/cases/frontend/view/src/lib/components/detail-case/detail-case.component.ts index 65423b4d..27ede7e6 100644 --- a/libs/cases/frontend/view/src/lib/components/detail-case/detail-case.component.ts +++ b/libs/cases/frontend/view/src/lib/components/detail-case/detail-case.component.ts @@ -1,171 +1,173 @@ import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component, computed, inject, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + computed, + inject, + ViewEncapsulation, +} from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { XdCasesFacade } from '@frontend/cases/frontend/domain'; -import { ECasePriority, ECaseStatus, ECaseType, ICaseResponse } from '@frontend/cases/shared/models'; import { IxModule, ModalService, ToastService } from '@siemens/ix-angular'; +import { ECasePriority, ECaseStatus, ECaseType, ICaseResponse } from 'cases-shared-models'; import DeleteModalComponent from './delete-modal/deleteModal.component'; - @Component({ - selector: 'lib-detail-case', - standalone: true, - imports: [CommonModule, FormsModule, IxModule, RouterLink], - templateUrl: './detail-case.component.html', - styleUrls: ['./detail-case.component.scss'], - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'lib-detail-case', + standalone: true, + imports: [ CommonModule, FormsModule, IxModule, RouterLink ], + templateUrl: './detail-case.component.html', + styleUrls: [ './detail-case.component.scss' ], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DetailCaseComponent { - private readonly _casesFacade = inject(XdCasesFacade); - protected readonly _cases = toSignal(this._casesFacade.getAllCases()); - protected readonly casedetail = computed(() => { - const _case = this._cases(); - if (_case === undefined) { - return; - } - return _case.find((_case) => String(_case.id) === this.route.snapshot.params['id']); - }); - - isEditing = false; - datePattern = /^\d{4}-\d{2}-\d{2}T00:00:00\.000Z$/; - - constructor( - protected router: Router, - protected route: ActivatedRoute, - private readonly _modalService: ModalService, - private readonly toastService: ToastService - ) {} - - deleteCase() { - const caseId = this.mapCaseId(this.casedetail()); - if (caseId !== undefined) { - // The subscribe is necessary, otherwise the request is not sent - this._casesFacade.deleteCase(caseId).subscribe(); - } - } - - mapCaseId(_case: ICaseResponse | undefined) { - if (_case === undefined) { - return undefined; - } - return { - id: _case.id, - }; - } - - async deleting() { - const instance = await this._modalService.open({ - content: DeleteModalComponent, - }); - - // modal closes on confirm and dismisses on cancel - instance.onClose.on(() => { - this.deleteCase(); - }); - } - - cancelEdit() { - this.isEditing = false; - window.location.reload(); - } - - toggleEdit() { - if (this.isEditing) { - this.onSubmit(); - } else { - this.isEditing = true; - } - } - - onSubmit(): void { - const casedetail = this.casedetail(); - - if (casedetail !== undefined) { - const validationString = this.validateForm(casedetail); - if (validationString === 'valid') { - const caseId = this.mapCaseId(this.casedetail()); - const caseData = this.casedetail(); - - if (caseId !== undefined && caseData !== undefined) { - // The subscribe is necessary, otherwise the request is not sent - this._casesFacade.updateCase(caseId, caseData).subscribe({}); - } - this.isEditing = false; - } else { - this.showErrorToast(validationString); - } - } - } - - validateForm(casedetail: ICaseResponse) { - - - if (casedetail !== undefined) { - - if (casedetail.title === '') { - return 'Empty title'; - } - - - if (!Object.values(ECaseStatus).includes(casedetail.status)) { - return 'Status is not in List: OPEN, INPROGRESS, OVERDUE, ONHOLD, DONE, CANCELLED, ARCHIVED'; - } - - - if (!Object.values(ECasePriority).includes(casedetail.priority)) { - return 'Priority is not in List: EMERGENCY, HIGH, MEDIUM, LOW'; - } - - if (!(this.casedetail()?.modifiedBy.includes('@') && this.casedetail()?.modifiedBy.includes('.'))) { - return 'Invalid email'; - } - - if (!Object.values(ECaseType).includes(casedetail.type)) { - return 'Priority is not in List: PLANNED, INNCIDENT, ANNOTATION'; - } - - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - if (!this.datePattern.test(casedetail.dueDate)) { - return 'Invalid date format'; - } - - const dueDate = this.casedetail()?.dueDate; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - if (dueDate && this.datePattern.test(this.casedetail()?.dueDate)) { - const match = this.casedetail()?.dueDate; - if (match) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const month: int = (parseInt(match[5])) * 10 + parseInt(match[6]); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const day = parseInt(match[8]) * 10 + parseInt(match[9]); - if (!(month >= 1 && month <= 12 && day >= 1 && day <= 31)) { - return 'Invalid month or day in due date'; - } - } - } else { - return 'Invalid date format'; - } - - return 'valid'; - - } - return 'An error occurred :('; - - } - - async showErrorToast(info: string) { - await this.toastService.show({ - type: 'error', - message: info, - }); - } + private readonly _casesFacade = inject(XdCasesFacade); + protected readonly _cases = toSignal(this._casesFacade.getAllCases()); + protected readonly casedetail = computed(() => { + const _case = this._cases(); + if (_case === undefined) { + return; + } + return _case.find((_case) => String(_case.id) === this.route.snapshot.params['id']); + }); + + isEditing = false; + datePattern = /^\d{4}-\d{2}-\d{2}T00:00:00\.000Z$/; + + constructor( + protected router: Router, + protected route: ActivatedRoute, + private readonly _modalService: ModalService, + private readonly toastService: ToastService, + ) {} + + deleteCase() { + const caseId = this.mapCaseId(this.casedetail()); + if (caseId !== undefined) { + // The subscribe is necessary, otherwise the request is not sent + this._casesFacade.deleteCase(caseId).subscribe(); + } + } + + mapCaseId(_case: ICaseResponse | undefined) { + if (_case === undefined) { + return undefined; + } + return { + id: _case.id, + }; + } + + async deleting() { + const instance = await this._modalService.open({ + content: DeleteModalComponent, + }); + + // modal closes on confirm and dismisses on cancel + instance.onClose.on(() => { + this.deleteCase(); + }); + } + + cancelEdit() { + this.isEditing = false; + window.location.reload(); + } + + toggleEdit() { + if (this.isEditing) { + this.onSubmit(); + } else { + this.isEditing = true; + } + } + + onSubmit(): void { + const casedetail = this.casedetail(); + + if (casedetail !== undefined) { + const validationString = this.validateForm(casedetail); + if (validationString === 'valid') { + const caseId = this.mapCaseId(this.casedetail()); + const caseData = this.casedetail(); + + if (caseId !== undefined && caseData !== undefined) { + // The subscribe is necessary, otherwise the request is not sent + this._casesFacade.updateCase(caseId, caseData).subscribe({}); + } + this.isEditing = false; + } else { + this.showErrorToast(validationString); + } + } + } + + validateForm(casedetail: ICaseResponse) { + if (casedetail !== undefined) { + if (casedetail.title === '') { + return 'Empty title'; + } + + if (!Object.values(ECaseStatus).includes(casedetail.status)) { + return 'Status is not in List: OPEN, INPROGRESS, OVERDUE, ONHOLD, DONE, CANCELLED, ARCHIVED'; + } + + if (!Object.values(ECasePriority).includes(casedetail.priority)) { + return 'Priority is not in List: EMERGENCY, HIGH, MEDIUM, LOW'; + } + + if ( + !( + this.casedetail()?.modifiedBy.includes('@') && + this.casedetail()?.modifiedBy.includes('.') + ) + ) { + return 'Invalid email'; + } + + if (!Object.values(ECaseType).includes(casedetail.type)) { + return 'Priority is not in List: PLANNED, INNCIDENT, ANNOTATION'; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + if (!this.datePattern.test(casedetail.dueDate)) { + return 'Invalid date format'; + } + + const dueDate = this.casedetail()?.dueDate; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + if (dueDate && this.datePattern.test(this.casedetail()?.dueDate)) { + const match = this.casedetail()?.dueDate; + if (match) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const month: int = parseInt(match[5]) * 10 + parseInt(match[6]); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const day = parseInt(match[8]) * 10 + parseInt(match[9]); + if (!(month >= 1 && month <= 12 && day >= 1 && day <= 31)) { + return 'Invalid month or day in due date'; + } + } + } else { + return 'Invalid date format'; + } + + return 'valid'; + } + return 'An error occurred :('; + } + + async showErrorToast(info: string) { + await this.toastService.show({ + type: 'error', + message: info, + }); + } } diff --git a/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts index 356d6477..bd98077c 100644 --- a/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts +++ b/libs/cases/frontend/view/src/lib/components/open-cases/open-cases.component.ts @@ -15,7 +15,7 @@ import { ICaseResponse } from 'cases-shared-models'; @Component({ selector: 'lib-open-cases', standalone: true, - imports: [CommonModule, IxModule, RouterLink], + imports: [ CommonModule, IxModule, RouterLink ], templateUrl: './open-cases.component.html', styleUrl: './open-cases.component.scss', encapsulation: ViewEncapsulation.None, @@ -32,7 +32,7 @@ export class OpenCasesComponent { cases .filter((_case: ICaseResponse) => _case.status === 'OPEN') .sort((a, b) => { - const priorityOrder = ['EMERGENCY', 'HIGH', 'MEDIUM', 'LOW']; + const priorityOrder = [ 'EMERGENCY', 'HIGH', 'MEDIUM', 'LOW' ]; const priorityAIndex = priorityOrder.indexOf(a.priority.toUpperCase()); const priorityBIndex = priorityOrder.indexOf(b.priority.toUpperCase()); diff --git a/libs/common/backend/swagger/src/lib/swagger-document-builder.ts b/libs/common/backend/swagger/src/lib/swagger-document-builder.ts index 14807fec..944554d0 100644 --- a/libs/common/backend/swagger/src/lib/swagger-document-builder.ts +++ b/libs/common/backend/swagger/src/lib/swagger-document-builder.ts @@ -30,7 +30,7 @@ export class SwaggerDocumentBuilder { 'JWTAuthorization', ); - Object.entries(SWAGGER_TAG_INFORMATION).forEach(([tag, tagInformation]) => { + Object.entries(SWAGGER_TAG_INFORMATION).forEach(([ tag, tagInformation ]) => { docBuilder.addTag(tag, tagInformation.description); }); diff --git a/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts b/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts index 455282f1..18d1a517 100644 --- a/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts +++ b/libs/facilities/backend/facilities/src/lib/controller/facilities.controller.spec.ts @@ -11,9 +11,9 @@ describe('FacilitiesController ', () => { let service: XdFacilitiesService; const facilitiesResponse: IFacilitiesResponse = { - indicatorMsg: faker.string.sample(), - metrics: [ { standardDeviation: faker.number.int() } as IPumpMetrics ], - assetId: faker.string.uuid(), + indicatorMsg: faker.string.sample(), + metrics: [ { standardDeviation: faker.number.int() } as IPumpMetrics ], + assetId: faker.string.uuid(), createdAt: faker.date.recent(), description: faker.string.sample(), name: faker.string.sample(), @@ -29,14 +29,9 @@ describe('FacilitiesController ', () => { postalCode: faker.location.zipCode(), region: faker.location.state(), streetAddress: faker.location.streetAddress(), -<<<<<<< HEAD - } - }; -======= }, cases: [], }; ->>>>>>> 151131f (fix: case Service test) beforeAll(async () => { const serviceMock = { @@ -46,7 +41,7 @@ describe('FacilitiesController ', () => { }; const module: TestingModule = await Test.createTestingModule({ - controllers: [XdFacilitiesController], + controllers: [ XdFacilitiesController ], providers: [ { provide: XdFacilitiesService, @@ -66,23 +61,23 @@ describe('FacilitiesController ', () => { it('should return all facilities', async () => { const Spy = jest .spyOn(service, 'getAllFacilitiesFromDB') - .mockReturnValue(of([facilitiesResponse])); + .mockReturnValue(of([ facilitiesResponse ])); const result = await firstValueFrom(controller.getAllFacilities()); console.log(result); expect(Spy).toHaveBeenCalled(); - expect(result).toEqual([facilitiesResponse]); + expect(result).toEqual([ facilitiesResponse ]); }); it('should seed the database', async () => { - const Spy = jest.spyOn(service, 'seedTheDB').mockReturnValue(of([facilitiesResponse])); + const Spy = jest.spyOn(service, 'seedTheDB').mockReturnValue(of([ facilitiesResponse ])); const result = await firstValueFrom(controller.seedTheDB()); expect(Spy).toHaveBeenCalled(); - expect(result).toEqual([facilitiesResponse]); + expect(result).toEqual([ facilitiesResponse ]); }); it('should get a facility by id', async () => { diff --git a/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts b/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts index 6034fe18..12eeaa77 100644 --- a/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts +++ b/libs/facilities/backend/facilities/src/lib/service/facilities.service.spec.ts @@ -102,12 +102,12 @@ describe('FacilitiesService ', () => { }; it('should return all facilities', async () => { - const prismaSpy = jest.spyOn(prisma.asset, 'findMany').mockResolvedValue([resultMock]); + const prismaSpy = jest.spyOn(prisma.asset, 'findMany').mockResolvedValue([ resultMock ]); const result = await lastValueFrom(service.getAllFacilitiesFromDB()); expect(prismaSpy).toHaveBeenCalled(); - expect(result).toEqual([resultMock]); + expect(result).toEqual([ resultMock ]); }); it('should return a facility by id', async () => { diff --git a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts index 46294e4b..a9dd5509 100644 --- a/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts +++ b/libs/facilities/backend/facilities/src/lib/service/facilities.service.ts @@ -1,4 +1,9 @@ -import { EPumpStatus, IFacilitiesResponse, IFacilityLocation, IPumpMetrics } from '@frontend/facilities/shared/models'; +import { + EPumpStatus, + IFacilitiesResponse, + IFacilityLocation, + IPumpMetrics, +} from '@frontend/facilities/shared/models'; import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; import { Aspect, Asset, XdAssetsService } from 'common-backend-insight-hub'; import { PrismaService } from 'common-backend-prisma'; @@ -163,11 +168,8 @@ export class XdFacilitiesService { this.prismaService.asset.findMany({ include: { location: true, -<<<<<<< HEAD - metrics: true, -======= + metrics: true, cases: true, ->>>>>>> 3254e7e (fix: cases service) }, }), ).pipe( @@ -181,13 +183,10 @@ export class XdFacilitiesService { createdAt, updatedAt, variables, -<<<<<<< HEAD status, - indicatorMsg, - metrics -======= + indicatorMsg, + metrics, cases, ->>>>>>> 3254e7e (fix: cases service) } = asset; const location: IFacilityLocation | undefined = asset.location @@ -208,8 +207,8 @@ export class XdFacilitiesService { typeId, status: status as EPumpStatus, location, - indicatorMsg, - metrics: metrics as IPumpMetrics[], + indicatorMsg, + metrics: metrics as IPumpMetrics[], variables: variables || undefined, description: description || '', createdAt: createdAt, @@ -232,11 +231,8 @@ export class XdFacilitiesService { }, include: { location: true, -<<<<<<< HEAD - metrics: true, -======= + metrics: true, cases: true, ->>>>>>> 3254e7e (fix: cases service) }, }), ).pipe( @@ -253,13 +249,10 @@ export class XdFacilitiesService { variables, createdAt, updatedAt, -<<<<<<< HEAD status, - indicatorMsg, - metrics -======= + indicatorMsg, + metrics, cases, ->>>>>>> 3254e7e (fix: cases service) } = asset; const location: IFacilityLocation | undefined = asset.location diff --git a/libs/facilities/backend/timeseries/src/lib/services/timeseries.service.spec.ts b/libs/facilities/backend/timeseries/src/lib/services/timeseries.service.spec.ts index da8ed93e..9bf23c89 100644 --- a/libs/facilities/backend/timeseries/src/lib/services/timeseries.service.spec.ts +++ b/libs/facilities/backend/timeseries/src/lib/services/timeseries.service.spec.ts @@ -195,6 +195,12 @@ describe('TimeseriesService', () => { .spyOn(prisma.timeSeriesDataItem, 'findMany') .mockResolvedValue([]); + jest.spyOn(prisma.timeSeriesItem, 'findUnique').mockResolvedValue({ + assetId: faker.string.uuid(), + propertySetName: faker.string.sample(), + variables: {}, + }); + const params: IGetTimeSeriesParams = { assetId: faker.string.uuid(), propertySetName: faker.string.sample(),