Browse Source

introduce a performance backend

Sven Czarnian 2 years ago
parent
commit
822709f577

File diff suppressed because it is too large
+ 4467 - 0
config/performance.yaml


File diff suppressed because it is too large
+ 564 - 31
package-lock.json


+ 3 - 0
package.json

@@ -26,6 +26,9 @@
     "@nestjs/core": "^9.0.0",
     "@nestjs/mongoose": "^9.2.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",
     "js-yaml": "^4.1.0",
     "mongoose": "^6.6.7",

+ 4 - 2
src/app.module.ts

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

+ 25 - 0
src/performance/models/performance.model.ts

@@ -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);

+ 16 - 0
src/performance/models/performanceentry.model.ts

@@ -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);

+ 17 - 0
src/performance/performance.module.ts

@@ -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 {}

+ 18 - 0
src/performance/performance.service.spec.ts

@@ -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();
+  });
+});

+ 99 - 0
src/performance/performance.service.ts

@@ -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();
+  }
+}

+ 25 - 0
src/versioning/dto/semanticversion.dto.ts

@@ -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;
+}

+ 24 - 0
src/versioning/versioning.model.ts

@@ -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);

+ 15 - 0
src/versioning/versioning.module.ts

@@ -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 {}

+ 18 - 0
src/versioning/versioning.service.spec.ts

@@ -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();
+  });
+});

+ 51 - 0
src/versioning/versioning.service.ts

@@ -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;
+  }
+}