Skip to content

Commit

Permalink
Merge branch 'develop' into feat/xd-257
Browse files Browse the repository at this point in the history
  • Loading branch information
IngoSternberg authored Jul 15, 2024
2 parents adab3cb + a864c73 commit 2dcaf70
Show file tree
Hide file tree
Showing 33 changed files with 875 additions and 514 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ POSTGRES_DB=amos
# Full database connection URL constructed from the above PostgreSQL variables used for Prisma
DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}

# The Api Url for the Insight Hub API
INSIGHT_HUB_API_URL=https://gateway.eu1.mindsphere.io/api
# The Api Key for the Insight Hub API
INSIGHT_HUB_API_KEY=KEY

# Frontend
XD_API_URL=http://${BACKEND_HOST}:${BACKEND_PORT}
SECRET_KEY=SecretKey
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { XdMetricsModule } from '@frontend/facilities/backend/metrics';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XdCaseManagementModule } from 'cases-backend-case-management';
Expand All @@ -24,6 +25,7 @@ import { validateConfig } from './config/validation';
XdTimeseriesModule,
XdCaseManagementModule,
XdFacilitiesBackendFacilitiesModule,
XdMetricsModule
],
controllers: [],
providers: [],
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/app/config/classes/environment.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ export class EnvironmentVariables implements IEnvironmentVariables {
/**
* The URL of the API to use for the IotTimeSeriesService
*/
@IsDefined()
@IsOptional()
@IsString()
@MinLength(1)
INSIGHT_HUB_API_URL?: string;

/**
* The API key to use for the IotTimeSeriesService
*/
@IsDefined()
@IsOptional()
@IsString()
@MinLength(1)
INSIGHT_HUB_API_KEY?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { faker } from '@faker-js/faker';
import { HttpService } from '@nestjs/axios';
import { HttpException, HttpStatus, Inject, Injectable, Logger } from '@nestjs/common';
import { HttpException, Inject, Injectable, Logger } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AxiosResponse } from 'axios';
import { IInsightHub } from 'common-backend-models';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { HttpService } from '@nestjs/axios';
import { Logger } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AxiosResponse } from 'axios';
import { IInsightHub } from 'common-backend-models';
import { firstValueFrom, Observable, of } from 'rxjs';

import { ITimeSeriesRequestParameter } from '../models';
Expand All @@ -18,6 +19,7 @@ interface MockSelectParameter {
describe('XdIotTimeSeriesService', () => {
let service: XdIotTimeSeriesService;
let httpService: HttpService;
let insightHubOptions: IInsightHub;

beforeEach(async () => {
const httpServiceMock = {
Expand Down Expand Up @@ -55,6 +57,7 @@ describe('XdIotTimeSeriesService', () => {

service = module.get<XdIotTimeSeriesService>(XdIotTimeSeriesService);
httpService = module.get<HttpService>(HttpService);
insightHubOptions = module.get(INSIGHT_HUB_OPTIONS);
});

it('should be defined', () => {
Expand All @@ -71,7 +74,7 @@ describe('XdIotTimeSeriesService', () => {
from: faker.date.past(),
to: faker.date.recent(),
limit: faker.number.int(),
select: [ 'flow', 'pressure' ],
select: ['flow', 'pressure'],
};
const assetId = faker.string.uuid();
const propertySetName = faker.lorem.word(1);
Expand Down Expand Up @@ -99,4 +102,56 @@ describe('XdIotTimeSeriesService', () => {
expect(response).toEqual(mockResponse);
});
});

describe('isLocalSession', () => {
it('should return true for isLocalSession when apiKey and apiUrl are undefined', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
insightHubOptions.apiKey = null;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
insightHubOptions.apiUrl = null;

expect(service.isLocalSession()).toBe(true);
});

it('should return true for isLocalSession when apiKey and apiUrl are empty strings', () => {
insightHubOptions.apiKey = '';
insightHubOptions.apiUrl = '';

expect(service.isLocalSession()).toBe(true);
});

it('should return true for isLocalSession when apiKey is defined and apiUrl is undefined', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
insightHubOptions.apiUrl = null;

expect(service.isLocalSession()).toBe(true);
});

it('should return true for isLocalSession when apiKey is undefined and apiUrl is defined', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
insightHubOptions.apiKey = null;

expect(service.isLocalSession()).toBe(true);
});

it('should return true for isLocalSession when apiKey is defined and apiUrl is an empty string', () => {
insightHubOptions.apiUrl = '';

expect(service.isLocalSession()).toBe(true);
});

it('should return true for isLocalSession when apiKey is an empty string and apiUrl is defined', () => {
insightHubOptions.apiKey = '';

expect(service.isLocalSession()).toBe(true);
});

it('should return false for isLocalSession when apiKey and apiUrl are defined', () => {
expect(service.isLocalSession()).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class XdIotTimeSeriesService extends XdBaseBearerInteractionService {
);
}

/**
/**
* Allows to get the time series data from the IoT Time Series API.
* @see https://documentation.mindsphere.io/MindSphere/apis/iot-iottimeseries/api-iottimeseries-api.html
*
Expand All @@ -44,4 +44,11 @@ export class XdIotTimeSeriesService extends XdBaseBearerInteractionService {
): Observable<ITimeSeriesResponse> {
return super._getData<ITimeSeriesResponse>(`${assetId}/${propertySetName}`, params);
}

/**
* Checks if the session is local or not.
*/
public isLocalSession(): boolean {
return !this.insightHubOptions.apiKey || !this.insightHubOptions.apiUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export const SWAGGER_TAG_INFORMATION: Record<ESwaggerTag, ISwaggerTagInformation
name: 'Cases',
description: 'Endpoints for managing cases',
},
[ESwaggerTag.METRICS]: {
name: 'Metrics',
description: 'Endpoints for managing metrics',
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum ESwaggerTag {
FACILITIES = 'Facilities',
TIME_SERIES = 'Timeseries',
CASES = 'Cases',
METRICS = 'Metrics',
}
18 changes: 18 additions & 0 deletions libs/facilities/backend/metrics/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
3 changes: 3 additions & 0 deletions libs/facilities/backend/metrics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# facilities-backend-metrics

This library provides a set of classes and functions to collect and report metrics from the backend services.
11 changes: 11 additions & 0 deletions libs/facilities/backend/metrics/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'facilities-backend-metrics',
preset: '../../../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../../../coverage/libs/facilities/backend/metrics',
};
16 changes: 16 additions & 0 deletions libs/facilities/backend/metrics/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "facilities-backend-metrics",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/facilities/backend/metrics/src",
"projectType": "library",
"tags": ["domain:facilities", "kind:backend", "type:feature"],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/facilities/backend/metrics/jest.config.ts"
}
}
}
}
1 change: 1 addition & 0 deletions libs/facilities/backend/metrics/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/facilities-backend-metrics.module';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Controller, Get, Param } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { ESwaggerTag } from 'common-backend-swagger';
import { GetTimeSeriesParamsDto } from 'facilities-backend-timeseries';

import { XdMetricsService } from '../services/metrics.service';

@ApiTags(ESwaggerTag.METRICS)
@Controller('metrics')
export class XdMetricsController {
constructor(private readonly metricsService: XdMetricsService) {}

/**
* Get the metrics for the asset
*
* @param params
*/
@Get(':assetId/:propertySetName')
@ApiOkResponse({ description: 'Returns metrics data for an asset' })
public getMetricsForAsset(@Param() params: GetTimeSeriesParamsDto) {
return this.metricsService.getMetricsForAsset(params.assetId, params.propertySetName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { XdPrismaModule } from 'common-backend-prisma';
import { XdTimeseriesModule } from 'facilities-backend-timeseries';

import { XdMetricsController } from './controller/metrics.controller';
import { XdMetricsService } from './services/metrics.service';

@Module({
imports: [XdTimeseriesModule, XdPrismaModule],
controllers: [XdMetricsController],
providers: [XdMetricsService],
exports: [XdMetricsService],
})
export class XdMetricsModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { checkPumpStatus } from '@frontend/facilities/backend/utils';
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { PrismaService } from 'common-backend-prisma';
import { XdTimeseriesService } from 'facilities-backend-timeseries';
import { ITimeSeriesPumpReport } from 'facilities-shared-models';
import { from, map, switchMap } from 'rxjs';
import dayjs = require('dayjs');
import { pick } from 'lodash';

@Injectable()
export class XdMetricsService {
constructor(
@Inject(forwardRef(() => PrismaService))
private readonly prismaService: PrismaService,

private readonly timeSeriesService: XdTimeseriesService,
) {}

/**
* Get the metrics for the asset
*
* @param assetId
* @param propertySetName
*/
public getMetricsForAsset(assetId: string, propertySetName: string) {
return this.timeSeriesService
.getTimeSeries({
assetId,
propertySetName,
from: dayjs().subtract(60, 'minute').toDate(),
})
.pipe(
switchMap((data) => {
return this.upsertMetrics(assetId, propertySetName, data);
}),
);
}

/**
* Calculates and upserts the metrics for the asset
*
* @param assetId
* @param propertySetName
* @param data
* @private
*/
private upsertMetrics(assetId: string, propertySetName: string, data: unknown): any {
switch (propertySetName) {
case 'PumpData': {
const { status, indicatorMsg, metrics } = checkPumpStatus(
data as unknown as ITimeSeriesPumpReport[],
);
return from(
this.prismaService.metrics.deleteMany({
where: {
assetId: assetId,
},
}),
).pipe(
switchMap(() => {
return this.prismaService.asset
.update({
where: {
assetId,
},
data: {
status,
indicatorMsg,
metrics: {
create: metrics,
},
},
})
.metrics();
}),
map((metrics) =>
metrics.map((m) => ({
...pick(m, [
'name',
'min',
'max',
'variance',
'standardDeviation',
'coefficientOfVariation',
'mean',
]),
})),
),
);
}
}
}
}
22 changes: 22 additions & 0 deletions libs/facilities/backend/metrics/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
Loading

0 comments on commit 2dcaf70

Please sign in to comment.