introduce a performance backend

This commit is contained in:
Sven Czarnian
2022-10-22 20:25:55 +02:00
parent 31c9739d38
commit 822709f577
14 changed files with 5346 additions and 33 deletions

4467
config/performance.yaml Normal file

File diff suppressed because it is too large Load Diff

595
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,9 @@
"@nestjs/core": "^9.0.0", "@nestjs/core": "^9.0.0",
"@nestjs/mongoose": "^9.2.0", "@nestjs/mongoose": "^9.2.0",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/swagger": "^6.1.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"helmet": "^6.0.0", "helmet": "^6.0.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"mongoose": "^6.6.7", "mongoose": "^6.6.7",

View File

@@ -2,6 +2,8 @@ import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose'; import { MongooseModule } from '@nestjs/mongoose';
import Configuration from './config/configuration'; import Configuration from './config/configuration';
import { VersioningModule } from './versioning/versioning.module';
import { PerformanceModule } from './performance/performance.module';
@Module({ @Module({
imports: [ imports: [
@@ -20,8 +22,8 @@ import Configuration from './config/configuration';
)}`, )}`,
}), }),
}), }),
VersioningModule,
PerformanceModule,
], ],
controllers: [],
providers: [],
}) })
export class AppModule {} export class AppModule {}

View File

@@ -0,0 +1,25 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { PerformanceEntry } from './performanceentry.model';
export type PerformanceDocument = Performance & Document;
@Schema()
export class Performance {
@Prop({ required: true })
icaoCode: string;
@Prop({ required: true, type: PerformanceEntry })
aboveFL240: PerformanceEntry;
@Prop({ required: true, type: PerformanceEntry })
aboveFL100: PerformanceEntry;
@Prop({ required: true, type: PerformanceEntry })
belowFL100: PerformanceEntry;
@Prop({ required: true })
minimalApproachSpeed: number;
}
export const PerformanceSchema = SchemaFactory.createForClass(Performance);

View File

@@ -0,0 +1,16 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type PerformanceEntryDocument = PerformanceEntry & Document;
@Schema()
export class PerformanceEntry {
@Prop({ required: true })
speed: number;
@Prop({ required: true })
rateOfDescend: number;
}
export const PerformanceEntrySchema =
SchemaFactory.createForClass(PerformanceEntry);

View File

@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { PerformanceSchema } from './models/performance.model';
import { PerformanceService } from './performance.service';
import { VersioningModule } from '../versioning/versioning.module';
@Module({
imports: [
MongooseModule.forFeature([
{ name: 'performance', schema: PerformanceSchema },
]),
VersioningModule,
],
providers: [PerformanceService],
exports: [MongooseModule, PerformanceService],
})
export class PerformanceModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PerformanceService } from './performance.service';
describe('PerformanceService', () => {
let service: PerformanceService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PerformanceService],
}).compile();
service = module.get<PerformanceService>(PerformanceService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,99 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { Model } from 'mongoose';
import { join } from 'path';
import { SemanticVersionDto } from 'src/versioning/dto/semanticversion.dto';
import { VersioningService } from '../versioning/versioning.service';
import { PerformanceDocument } from './models/performance.model';
const COMPONENT_NAME = 'performance';
@Injectable()
export class PerformanceService {
private static readPerformanceFile(): Record<string, any> {
return yaml.load(
readFileSync(join(process.cwd(), 'config/performance.yaml'), 'utf8'),
) as Record<string, any>;
}
private async updatePerformanceData(
performanceData: Record<string, any>,
version: SemanticVersionDto,
): Promise<boolean> {
return this.performanceModel.deleteMany({}).then(async () => {
for (const key in performanceData) {
if (key !== 'FILEINFO') {
await this.performanceModel.create({
icaoCode: key,
aboveFL240: {
speed: performanceData[key]['speedabovefl240'],
rateOfDescend: performanceData[key]['rodabovefl240'],
},
aboveFL100: {
speed: performanceData[key]['speedabovefl100'],
rateOfDescend: performanceData[key]['rodabovefl100'],
},
belowFL100: {
speed: performanceData[key]['speedbelowfl100'],
rateOfDescend: performanceData[key]['rodbelowfl100'],
},
minimalApproachSpeed: performanceData[key]['speedapproach'],
});
}
}
await this.versioningService.updateComponent(
COMPONENT_NAME,
performanceData['FILEINFO']['createdAt'],
version,
);
return true;
});
}
private async validateAndUpdatePerformanceData(): Promise<boolean> {
// read the performance file and validate the version length
const data = PerformanceService.readPerformanceFile();
const versionParts = (data['FILEINFO']['version'] as string).split('.');
if (versionParts.length !== 3) return false;
// create the fileversion
const fileversion: SemanticVersionDto = {
main: parseInt(versionParts[0]),
major: parseInt(versionParts[1]),
minor: parseInt(versionParts[2]),
};
return this.versioningService
.findComponent(COMPONENT_NAME)
.then((resp) => {
if (
resp === null ||
VersioningService.newerVersion(
{
main: resp.versionMain,
major: resp.versionMajor,
minor: resp.versionMinor,
},
fileversion,
)
) {
return this.updatePerformanceData(data, fileversion);
}
return false;
})
.catch(() => this.updatePerformanceData(data, fileversion));
}
constructor(
@InjectModel(COMPONENT_NAME)
private readonly performanceModel: Model<PerformanceDocument>,
private readonly versioningService: VersioningService,
) {
this.validateAndUpdatePerformanceData();
}
}

View File

@@ -0,0 +1,25 @@
import { IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class SemanticVersionDto {
@IsNotEmpty()
@ApiProperty({
description: 'The main version to describe downwards compatibility issues',
example: 1,
})
main: number;
@IsNotEmpty()
@ApiProperty({
description: 'The major version to describe upwards compatibility issues',
example: 1,
})
major: number;
@IsNotEmpty()
@ApiProperty({
description: 'The minor version to describe API stable changes',
example: 1,
})
minor: number;
}

View File

@@ -0,0 +1,24 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type VersioningDocument = Versioning & Document;
@Schema()
export class Versioning {
@Prop({ required: true })
componentName: string;
@Prop({ required: true })
createdAt: string;
@Prop({ required: true })
versionMain: number;
@Prop({ required: true })
versionMajor: number;
@Prop({ required: true })
versionMinor: number;
}
export const VersioningSchema = SchemaFactory.createForClass(Versioning);

View File

@@ -0,0 +1,15 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { VersioningSchema } from './versioning.model';
import { VersioningService } from './versioning.service';
@Module({
imports: [
MongooseModule.forFeature([
{ name: 'versioning', schema: VersioningSchema },
]),
],
providers: [VersioningService],
exports: [MongooseModule, VersioningService],
})
export class VersioningModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { VersioningService } from './versioning.service';
describe('VersioningService', () => {
let service: VersioningService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [VersioningService],
}).compile();
service = module.get<VersioningService>(VersioningService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,51 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { SemanticVersionDto } from './dto/semanticversion.dto';
import { Versioning, VersioningDocument } from './versioning.model';
@Injectable()
export class VersioningService {
constructor(
@InjectModel('versioning')
private readonly versioningModel: Model<VersioningDocument>,
) {}
async findComponent(name: string): Promise<Versioning> {
return this.versioningModel.findOne({ componentName: name });
}
async updateComponent(
name: string,
createdAt: string,
version: SemanticVersionDto,
): Promise<void> {
return this.versioningModel.deleteOne({ componentName: name }).then(() => {
this.versioningModel.create({
componentName: name,
createdAt,
versionMain: version.main,
versionMajor: version.major,
versionMinor: version.minor,
});
});
}
static newerVersion(
reference: SemanticVersionDto,
version: SemanticVersionDto,
): boolean {
// check special cases for the main part
if (reference.main < version.main) return true;
if (reference.main > version.main) return false;
// main versions are equal
// check the special cases for the major part
if (reference.major < version.major) return true;
if (reference.major > version.major) return false;
// major versions are equal
// check if the minor versions increased
return reference.minor < version.minor;
}
}