96 switch from azure table storage to sqlite #98

Merged
noahspannbauer merged 11 commits from 96-switch-from-azure-table-storage-to-sqlite into main 2025-11-23 11:51:01 -05:00
51 changed files with 22651 additions and 13863 deletions
Showing only changes of commit e1605992b0 - Show all commits

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -0,0 +1,64 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class InitialMigration1754234179211 implements MigrationInterface {
name = 'InitialMigration1754234179211'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "pilots" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "address" varchar NOT NULL, "city" varchar NOT NULL, "state" varchar NOT NULL, "postalCode" varchar NOT NULL, "email" varchar NOT NULL, "phone" varchar NOT NULL)`);
await queryRunner.query(`CREATE TABLE "logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar)`);
await queryRunner.query(`CREATE TABLE "tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar)`);
await queryRunner.query(`CREATE TABLE "temporary_certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_05a68997dc2d27dfcc642a4cf51" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_certificates"("id", "type", "number", "issueDate", "pilotId") SELECT "id", "type", "number", "issueDate", "pilotId" FROM "certificates"`);
await queryRunner.query(`DROP TABLE "certificates"`);
await queryRunner.query(`ALTER TABLE "temporary_certificates" RENAME TO "certificates"`);
await queryRunner.query(`CREATE TABLE "temporary_endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_96a69a0bfbdeccce6bffe34002d" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_endorsements"("id", "type", "issueDate", "pilotId") SELECT "id", "type", "issueDate", "pilotId" FROM "endorsements"`);
await queryRunner.query(`DROP TABLE "endorsements"`);
await queryRunner.query(`ALTER TABLE "temporary_endorsements" RENAME TO "endorsements"`);
await queryRunner.query(`CREATE TABLE "temporary_medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar, CONSTRAINT "FK_cb1f5d88fa2b105cc77513e9082" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_medical"("id", "class", "expirationDate", "pilotId") SELECT "id", "class", "expirationDate", "pilotId" FROM "medical"`);
await queryRunner.query(`DROP TABLE "medical"`);
await queryRunner.query(`ALTER TABLE "temporary_medical" RENAME TO "medical"`);
await queryRunner.query(`CREATE TABLE "temporary_logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar, CONSTRAINT "FK_19598551658f7625a82c7f029c8" FOREIGN KEY ("pilotId") REFERENCES "pilots" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_logs"("id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId") SELECT "id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId" FROM "logs"`);
await queryRunner.query(`DROP TABLE "logs"`);
await queryRunner.query(`ALTER TABLE "temporary_logs" RENAME TO "logs"`);
await queryRunner.query(`CREATE TABLE "temporary_tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar, CONSTRAINT "FK_71881df31cfff2362e39accc4b2" FOREIGN KEY ("logId") REFERENCES "logs" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_tracks"("id", "url", "order", "logId") SELECT "id", "url", "order", "logId" FROM "tracks"`);
await queryRunner.query(`DROP TABLE "tracks"`);
await queryRunner.query(`ALTER TABLE "temporary_tracks" RENAME TO "tracks"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "tracks" RENAME TO "temporary_tracks"`);
await queryRunner.query(`CREATE TABLE "tracks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "order" integer NOT NULL, "logId" varchar)`);
await queryRunner.query(`INSERT INTO "tracks"("id", "url", "order", "logId") SELECT "id", "url", "order", "logId" FROM "temporary_tracks"`);
await queryRunner.query(`DROP TABLE "temporary_tracks"`);
await queryRunner.query(`ALTER TABLE "logs" RENAME TO "temporary_logs"`);
await queryRunner.query(`CREATE TABLE "logs" ("id" varchar PRIMARY KEY NOT NULL, "date" datetime NOT NULL, "aircraftMakeModel" varchar NOT NULL, "aircraftIdentity" varchar NOT NULL, "routeFrom" varchar NOT NULL, "routeTo" varchar NOT NULL, "durationOfFlight" integer NOT NULL, "singleEngineLand" integer, "simulatorAtd" integer, "landingsDay" integer, "landingsNight" integer, "groundTrainingReceived" integer, "flightTrainingReceived" integer, "crossCountry" integer, "night" integer, "solo" integer, "pilotInCommand" integer, "instrumentActual" integer, "instrumentSimulated" integer, "instrumentApproaches" integer, "instrumentHolds" integer, "instrumentNavTrack" integer, "notes" varchar, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "logs"("id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId") SELECT "id", "date", "aircraftMakeModel", "aircraftIdentity", "routeFrom", "routeTo", "durationOfFlight", "singleEngineLand", "simulatorAtd", "landingsDay", "landingsNight", "groundTrainingReceived", "flightTrainingReceived", "crossCountry", "night", "solo", "pilotInCommand", "instrumentActual", "instrumentSimulated", "instrumentApproaches", "instrumentHolds", "instrumentNavTrack", "notes", "pilotId" FROM "temporary_logs"`);
await queryRunner.query(`DROP TABLE "temporary_logs"`);
await queryRunner.query(`ALTER TABLE "medical" RENAME TO "temporary_medical"`);
await queryRunner.query(`CREATE TABLE "medical" ("id" varchar PRIMARY KEY NOT NULL, "class" varchar NOT NULL, "expirationDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "medical"("id", "class", "expirationDate", "pilotId") SELECT "id", "class", "expirationDate", "pilotId" FROM "temporary_medical"`);
await queryRunner.query(`DROP TABLE "temporary_medical"`);
await queryRunner.query(`ALTER TABLE "endorsements" RENAME TO "temporary_endorsements"`);
await queryRunner.query(`CREATE TABLE "endorsements" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "endorsements"("id", "type", "issueDate", "pilotId") SELECT "id", "type", "issueDate", "pilotId" FROM "temporary_endorsements"`);
await queryRunner.query(`DROP TABLE "temporary_endorsements"`);
await queryRunner.query(`ALTER TABLE "certificates" RENAME TO "temporary_certificates"`);
await queryRunner.query(`CREATE TABLE "certificates" ("id" varchar PRIMARY KEY NOT NULL, "type" varchar NOT NULL, "number" varchar NOT NULL, "issueDate" datetime NOT NULL, "pilotId" varchar)`);
await queryRunner.query(`INSERT INTO "certificates"("id", "type", "number", "issueDate", "pilotId") SELECT "id", "type", "number", "issueDate", "pilotId" FROM "temporary_certificates"`);
await queryRunner.query(`DROP TABLE "temporary_certificates"`);
await queryRunner.query(`DROP TABLE "tracks"`);
await queryRunner.query(`DROP TABLE "logs"`);
await queryRunner.query(`DROP TABLE "pilots"`);
await queryRunner.query(`DROP TABLE "medical"`);
await queryRunner.query(`DROP TABLE "endorsements"`);
await queryRunner.query(`DROP TABLE "certificates"`);
}
}

View File

@@ -16,7 +16,11 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "npm run build && npx typeorm -d dist/config/typeorm-cli.config.js",
"migration:generate": "npm run typeorm -- migration:generate",
"migration:run": "npm run typeorm -- migration:run",
"migration:revert": "npm run typeorm -- migration:revert"
},
"dependencies": {
"@azure/storage-blob": "^12.27.0",
@@ -32,7 +36,7 @@
"@noahspan/noahspan-modules": "^1.1.5",
"@schematics/angular": "^17.3.7",
"@types/multer": "^1.4.12",
"dotenv": "^16.4.7",
"dotenv": "^16.6.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"sqlite3": "^5.1.7",

View File

@@ -8,6 +8,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthGuard, AuthModule, UserModule } from '@noahspan/noahspan-modules';
import configuration from './config/configuration';
import { TypeOrmModule } from '@nestjs/typeorm';
import { dataSourceOptions } from './config/typeorm-cli.config';
import { TrackModule } from './track/track.module';
@Module({
imports: [
@@ -23,20 +25,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
},
}),
ConfigModule.forRoot({
isGlobal: true,
load: [configuration]
}),
FeatureFlagModule,
LogModule,
PilotModule,
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'sqlite',
database: configService.get<string>('dbPath'),
entities: [__dirname + "/**/*.entity{.ts,.js}"],
synchronize: false
})
}),
TrackModule,
TypeOrmModule.forRoot(dataSourceOptions),
UserModule.registerAsync({
inject: [ConfigService],
imports: [ConfigModule],

View File

View File

@@ -1,6 +1,5 @@
export default () => ({
azureStorageConnectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
dbPath: process.env.DB_PATH,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
tenantId: process.env.TENANT_ID

View File

@@ -0,0 +1,20 @@
import { DataSource, DataSourceOptions } from 'typeorm';
import { config } from 'dotenv';
import { ConfigService } from '@nestjs/config';
import configuration from './configuration';
config();
const configService = new ConfigService();
export const dataSourceOptions: DataSourceOptions = {
type: 'sqlite',
database: configService.get<string>('DB_PATH'),
entities: ['dist/**/*.entity.js'],
migrations: ['dist/migrations/*.js'],
synchronize: configService.get<boolean>('DB_SYNC')
}
const dataSource = new DataSource(dataSourceOptions);
export default dataSource;

View File

@@ -0,0 +1,65 @@
import { Body, Controller, Delete, Get, HttpException, Param, Post, Put } from "@nestjs/common";
import { EndorsementService } from "./endorsement.service";
import { CustomError } from "@noahspan/noahspan-modules";
import { EndorsementDto } from "./endorsement.dto";
@Controller('endorsements')
export class EndorsementController {
constructor(private readonly endorsementService: EndorsementService) {}
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.endorsementService.find(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get()
async findAll() {
try {
return await this.endorsementService.findAll();
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Post()
async create(@Body() endorsementDto: EndorsementDto) {
try {
return await this.endorsementService.create(endorsementDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Put(':id')
async update(@Param('id') id: string, @Body() endorsementDto: EndorsementDto) {
try {
return await this.endorsementService.update(id, endorsementDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode)
}
}
@Delete(':id')
async delete(@Param('id') id: string) {
try {
return await this.endorsementService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode)
}
}
}

View File

@@ -0,0 +1,3 @@
export class EndorsementDto {
}

View File

@@ -0,0 +1,32 @@
import { InjectRepository } from "@nestjs/typeorm";
import { EndorsementEntity } from "./endorsement.entity";
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { Injectable } from "@nestjs/common";
import { EndorsementDto } from "./endorsement.dto";
@Injectable()
export class EndorsementService {
constructor(
@InjectRepository(EndorsementEntity) private readonly endorsementRepository: Repository<EndorsementEntity>
) {}
async find(id: string): Promise<EndorsementEntity> {
return await this.endorsementRepository.findOneBy({ id });
}
async findAll(): Promise<EndorsementEntity[]> {
return await this.endorsementRepository.find();
}
async create(endorsement: EndorsementDto): Promise<InsertResult> {
return await this.endorsementRepository.insert(endorsement);
}
async update(id: string, endorsement: EndorsementDto): Promise<UpdateResult> {
return await this.endorsementRepository.update(id, endorsement);
}
async delete(id: string): Promise<DeleteResult> {
return await this.endorsementRepository.delete({ id });
}
}

View File

@@ -37,10 +37,11 @@ import { ConfigService } from '@nestjs/config';
return blockBlobClient;
}
async uploadFile(file: Express.Multer.File, containerName: string, rowKey: string): Promise<string> {
async uploadFile(file: Express.Multer.File, containerName: string, logId: string): Promise<string> {
console.log(file)
this.containerName = containerName;
const blockBlobClient = await this.getBlobClient(`${rowKey}/${file.originalname}`);
const blockBlobClient = await this.getBlobClient(`${logId}/${file.originalname}`);
const fileUrl = blockBlobClient.url;
await blockBlobClient.uploadData(file.buffer);

View File

@@ -7,20 +7,17 @@ import {
Param,
Post,
Put,
Query,
StreamableFile,
UploadedFile,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { LogDto } from './log.dto';
import { Log } from './log.entity';
import { LogEntity } from './log.entity';
import { LogService } from './log.service';
import { CustomError } from '../error/customError';
import { AuthGuard } from '@noahspan/noahspan-modules';
import { LogInterceptor } from './interceptors/log.interceptor';
import { FileService } from '../file/file.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
@Controller('logs')
export class LogController {
@@ -30,14 +27,13 @@ export class LogController {
) {}
@Get(':partitionKey/:rowKey')
@Get(':id')
@UseInterceptors(new LogInterceptor())
async find(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string
): Promise<Log> {
@Param('id') id: string,
): Promise<LogEntity> {
try {
return await this.logService.find(partitionKey, rowKey);
return await this.logService.find(id);
} catch (error) {
const customError = error as CustomError;
@@ -47,7 +43,7 @@ export class LogController {
@Get()
@UseInterceptors(new LogInterceptor())
async findAll(): Promise<Log[]> {
async findAll(): Promise<LogEntity[]> {
try {
return await this.logService.findAll();
} catch (error) {
@@ -57,35 +53,26 @@ export class LogController {
}
}
@UseGuards(AuthGuard)
// @UseGuards(AuthGuard)
@Post()
async create(@Body() logDto: LogDto): Promise<Log> {
async create(@Body() logDto: LogDto): Promise<InsertResult> {
try {
const log = new Log();
Object.assign(log, logDto);
return await this.logService.create(log);
return await this.logService.create(logDto);
} catch (error) {
const customError = error as CustomError;
console.log(error)
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Put(':partitionKey/:rowKey')
// @UseGuards(AuthGuard)
@Put(':id')
async update(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string,
@Param('id') id: string,
@Body() logDto: LogDto
): Promise<Log> {
): Promise<UpdateResult> {
try {
const log = new Log();
Object.assign(log, logDto);
return await this.logService.update(partitionKey, rowKey, log);
return await this.logService.update(id, logDto);
} catch (error) {
const customError = error as CustomError;
@@ -93,52 +80,13 @@ export class LogController {
}
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey')
// @UseGuards(AuthGuard)
@Delete(':id')
async delete(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string
): Promise<void> {
@Param('id') id: string,
): Promise<DeleteResult> {
try {
return await this.logService.delete(partitionKey, rowKey);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post(':partitionKey/:rowKey/track')
@UseInterceptors(FileInterceptor('file'))
async createTrack(@Param('rowKey') rowKey: string, @UploadedFile() file: Express.Multer.File) {
try {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, rowKey);
return { url }
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get(':partitionKey/:rowKey/track')
async downloadTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<string> {
const containerName = 'tracks';
const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName)
return downloadedFile;
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey/track')
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
try {
const containerName = 'tracks';
return await this.fileService.deleteFile(containerName, rowKey, fileName)
return await this.logService.delete(id);
} catch (error) {
const customError = error as CustomError;

View File

@@ -1,27 +1,30 @@
import { PilotEntity } from "src/pilot/pilot.entity";
import { TrackEntity } from "src/track/track.entity";
export class LogDto {
pilotId: string;
pilotName: string;
date: string;
date: Date;
aircraftMakeModel: string;
aircraftIdentity: string;
routeFrom: string;
routeTo: string;
durationOfFlight: number;
singleEngineLand: string;
simulatorAtd: number;
landingsDay: number;
landingsNight: number;
instrumentActual: number;
instrumentSimulated: number;
instrumentApproaches: number;
instrumentHolds: number;
instrumentNavTrack: number;
groundTrainingReceived: number;
flightTrainingReceived: number;
crossCountry: number;
night: number;
solo: number;
pilotInCommand: number;
tracks: string[];
notes: string;
durationOfFlight?: number;
singleEngineLand?: number;
simulatorAtd?: number;
landingsDay?: number;
landingsNight?: number;
instrumentActual?: number;
instrumentSimulated?: number;
instrumentApproaches?: number;
instrumentHolds?: number;
instrumentNavTrack?: number;
groundTrainingReceived?: number;
flightTrainingReceived?: number;
crossCountry?: number;
night?: number;
solo?: number;
pilotInCommand?: number;
notes?: string;
pilot?: PilotEntity;
tracks?: TrackEntity[];
}

View File

@@ -1,35 +1,36 @@
export class Log {
partitionKey: string;
rowKey: string;
pilotId: string;
pilotName: string;
date: string;
aircraftMakeModel: string;
aircraftIdentity: string;
routeFrom: string;
routeTo: string;
durationOfFlight: number | null;
singleEngineLand: number | null;
simulatorAtd?: number | null;
landingsDay?: number | null;
landingsNight?: number | null;
groundTrainingReceived?: number;
flightTrainingReceived?: number;
crossCountry?: number | null;
night?: number | null;
solo?: number | null;
pilotInCommand?: number | null;
instrumentActual?: number | null;
instrumentSimulated?: number | null;
instrumentApproaches?: number | null;
instrumentHolds?: number | null;
instrumentNavTrack?: number | null;
tracks?: string[];
notes?: string;
}
// export class Log {
// partitionKey: string;
// rowKey: string;
// pilotId: string;
// pilotName: string;
// date: string;
// aircraftMakeModel: string;
// aircraftIdentity: string;
// routeFrom: string;
// routeTo: string;
// durationOfFlight: number | null;
// singleEngineLand: number | null;
// simulatorAtd?: number | null;
// landingsDay?: number | null;
// landingsNight?: number | null;
// groundTrainingReceived?: number;
// flightTrainingReceived?: number;
// crossCountry?: number | null;
// night?: number | null;
// solo?: number | null;
// pilotInCommand?: number | null;
// instrumentActual?: number | null;
// instrumentSimulated?: number | null;
// instrumentApproaches?: number | null;
// instrumentHolds?: number | null;
// instrumentNavTrack?: number | null;
// tracks?: string[];
// notes?: string;
// }
import { PilotEntity } from 'src/pilot/pilot.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { TrackEntity } from 'src/track/track.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
@Entity({ name: 'logs' })
export class LogEntity {
@@ -54,53 +55,56 @@ export class LogEntity {
@Column()
durationOfFlight: number;
@Column()
singleEngineLand: number;
@Column({ nullable: true })
singleEngineLand: number | null;
@Column()
simulatorAtd: number;
@Column({ nullable: true })
simulatorAtd: number | null;
@Column()
landingsDay: number;
@Column({ nullable: true })
landingsDay: number | null;
@Column()
landingsNight: number;
@Column({ nullable: true })
landingsNight: number | null;
@Column()
groundTrainingReceived: number;
@Column({ nullable: true })
groundTrainingReceived: number | null;
@Column()
flightTrainingReceived: number;
@Column({ nullable: true })
flightTrainingReceived: number | null;
@Column()
crossCountry: number;
@Column({ nullable: true })
crossCountry: number | null;
@Column()
night: number;
@Column({ nullable: true })
night: number | null;
@Column()
solo: number;
@Column({ nullable: true })
solo: number | null;
@Column()
pilotInCommand: number;
@Column({ nullable: true })
pilotInCommand: number | null;
@Column()
instrumentActual: number;
@Column({ nullable: true })
instrumentActual: number | null;
@Column()
instrumentSimulated: number;
@Column({ nullable: true })
instrumentSimulated: number | null;
@Column()
instrumentApproaches: number;
@Column({ nullable: true })
instrumentApproaches: number | null;
@Column()
instrumentHolds: number;
@Column({ nullable: true })
instrumentHolds: number | null;
@Column()
instrumentNavTrack: number;
@Column({ nullable: true })
instrumentNavTrack: number | null;
@Column()
notes: string;
@Column({ nullable: true })
notes: string | null;
@OneToMany(() => TrackEntity, (track: TrackEntity) => track.log, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
tracks: TrackEntity[]
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.logs)
@JoinColumn({ name: 'pilotId' })

View File

@@ -1,28 +1,33 @@
import { Module } from '@nestjs/common';
import { LogController } from './log.controller';
import { LogService } from './log.service';
import { AzureTableStorageModule } from '@noahspan/azure-database';
import { Log } from './log.entity';
// import { AzureTableStorageModule } from '@noahspan/azure-database';
import { LogEntity } from './log.entity';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FileService } from '../file/file.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PilotModule } from 'src/pilot/pilot.module';
@Module({
imports: [
AzureTableStorageModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
connectionString: configService.get<string>('azureStorageConnectionString')
};
},
inject: [ConfigService]
}),
AzureTableStorageModule.forFeature(Log, {
createTableIfNotExists: false,
table: 'logs'
}),
// AzureTableStorageModule.forRootAsync({
// imports: [ConfigModule],
// useFactory: async (configService: ConfigService) => {
// return {
// connectionString: configService.get<string>('azureStorageConnectionString')
// };
// },
// inject: [ConfigService]
// }),
// AzureTableStorageModule.forFeature(Log, {
// createTableIfNotExists: false,
// table: 'logs'
// }),
PilotModule,
TypeOrmModule.forFeature([LogEntity])
],
controllers: [LogController],
exports: [LogService],
providers: [
ConfigService,
FileService,

View File

@@ -1,34 +1,58 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository, Repository } from '@noahspan/azure-database';
import { Log } from './log.entity';
import { v4 as uuidv4 } from 'uuid';
import { InjectRepository } from '@nestjs/typeorm';
import { LogEntity } from './log.entity';
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { LogDto } from './log.dto';
import { PilotService } from 'src/pilot/pilot.service';
import { PilotEntity } from 'src/pilot/pilot.entity';
import { CustomError } from 'src/error/customError';
import { TrackService } from 'src/track/track.service';
@Injectable()
export class LogService {
constructor(
@InjectRepository(Log) private readonly logRepository: Repository<Log>
@InjectRepository(LogEntity) private readonly logRepository: Repository<LogEntity>,
private readonly pilotService: PilotService
) {}
async find(partitionKey: string, rowKey: string): Promise<Log> {
return await this.logRepository.find(partitionKey, rowKey);
async find(id: string): Promise<LogEntity> {
const logEntity: LogEntity = await this.logRepository.findOne({
where: { id: id },
relations: ['pilot', 'tracks']
});
console.log(logEntity);
return logEntity;
}
async findAll(): Promise<Log[]> {
return await this.logRepository.findAll();
async findAll(): Promise<LogEntity[]> {
return await this.logRepository.find();
}
async create(log: Log): Promise<Log> {
log.partitionKey = 'log';
log.rowKey = uuidv4();
async create(logDto: LogDto): Promise<InsertResult> {
try{
const pilotEntity: PilotEntity = await this.pilotService.find(logDto.pilotId);
return await this.logRepository.create(log);
if (pilotEntity) {
const { pilotId, ...newLogDto } = logDto;
const log = this.logRepository.create({
...newLogDto,
pilot: pilotEntity
})
return this.logRepository.insert(log);
} else {
throw new CustomError('Pilot not found', 'Not found', 404);
}
} catch (error) {
throw error
}
}
async update(partitionKey: string, rowKey: string, log: Log): Promise<Log> {
return await this.logRepository.update(partitionKey, rowKey, log);
async update(id: string, log: LogDto): Promise<UpdateResult> {
return await this.logRepository.update(id, log);
}
async delete(partitionKey: string, rowKey: string): Promise<void> {
await this.logRepository.delete(partitionKey, rowKey);
async delete(id: string): Promise<DeleteResult> {
return await this.logRepository.delete({ id });
}
}

View File

@@ -0,0 +1,71 @@
import { Body, Controller, Delete, Get, HttpException, Param, Post, Put, UseGuards } from "@nestjs/common";
import { MedicalService } from "./medical.service";
import { CustomError } from "src/error/customError";
import { MedicalDto } from "./medical.dto";
import { AuthGuard } from "@noahspan/noahspan-modules";
@Controller('medical')
export class MedicalController {
constructor(private readonly medicalService: MedicalService) {}
@UseGuards(AuthGuard)
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.medicalService.find(id)
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Get()
async findAll() {
try {
return await this.medicalService.findAll();
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post()
async create(@Body() medicalDto: MedicalDto) {
try {
return await this.medicalService.create(medicalDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Put(':id')
async update(@Param('id') id: string, @Body() medicalDto: MedicalDto) {
try {
return await this.medicalService.update(id, medicalDto);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Delete(':id')
async delete(@Param('id') id: string) {
try {
return await this.medicalService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
}

View File

@@ -0,0 +1,6 @@
export class MedicalDto {
id: string;
class: string;
expirationDate: string;
pilotId: string;
}

View File

View File

@@ -0,0 +1,33 @@
import { MedicalEntity } from "./medical.entity";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { MedicalDto } from "./medical.dto";
@Injectable()
export class MedicalService {
constructor(
@InjectRepository(MedicalEntity) private readonly medicalRepository: Repository<MedicalEntity>
) {}
async find(id: string): Promise<MedicalEntity> {
return await this.medicalRepository.findOneBy({ id });
}
async findAll(): Promise<MedicalEntity[]> {
return await this.medicalRepository.find();
}
async create(medical: MedicalDto): Promise<InsertResult> {
return await this.medicalRepository.insert(medical)
}
async update(id: string, medical: MedicalDto): Promise<UpdateResult> {
return await this.medicalRepository.update(id, medical);
}
async delete(id: string): Promise<DeleteResult> {
return await this.medicalRepository.delete({ id });
}
}

View File

@@ -11,24 +11,21 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { PilotDto } from './pilot.dto';
import { Pilot } from './pilot.entity';
import { PilotEntity } from './pilot.entity';
import { PilotService } from './pilot.service';
import { CustomError } from '../error/customError';
import { AuthGuard } from '@noahspan/noahspan-modules'
import { PilotInterceptor } from './interceptors/pilot.interceptor';
@Controller('pilots')
@UseInterceptors(new PilotInterceptor())
// @UseInterceptors(new PilotInterceptor())
export class PilotController {
constructor(private readonly pilotService: PilotService) {}
@Get(':partitionKey/:rowKey')
async find(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string
) {
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.pilotService.find(partitionKey, rowKey);
return await this.pilotService.find(id);
} catch (error) {
const customError = error as CustomError;
@@ -47,30 +44,11 @@ export class PilotController {
}
}
@UseGuards(AuthGuard)
// @UseGuards(AuthGuard)
@Post()
async create(@Body() pilotDto: PilotDto) {
try {
let pilot = new Pilot();
pilot = {
partitionKey: pilotDto.partitionKey,
rowKey: pilotDto.rowKey,
id: pilotDto.id,
name: pilotDto.name,
address: pilotDto.address,
city: pilotDto.city,
state: pilotDto.state,
postalCode: pilotDto.postalCode,
email: pilotDto.email,
phone: pilotDto.phone,
medicalClass: pilotDto.medicalClass,
medicalExpiration: pilotDto.medicalExpiration,
certificates: JSON.stringify(pilotDto.certificates),
endorsements: JSON.stringify(pilotDto.endorsements)
}
return await this.pilotService.create(pilot);
return await this.pilotService.create(pilotDto);
} catch (error) {
const customError = error as CustomError;
@@ -78,34 +56,14 @@ export class PilotController {
}
}
@UseGuards(AuthGuard)
@Put(':partitionKey/:rowKey')
// @UseGuards(AuthGuard)
@Put(':id')
async update(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string,
@Param('id') id: string,
@Body() pilotDto: PilotDto
) {
try {
let pilot = new Pilot();
pilot = {
partitionKey: pilotDto.partitionKey,
rowKey: pilotDto.rowKey,
id: pilotDto.id,
name: pilotDto.name,
address: pilotDto.address,
city: pilotDto.city,
state: pilotDto.state,
postalCode: pilotDto.postalCode,
email: pilotDto.email,
phone: pilotDto.phone,
medicalClass: pilotDto.medicalClass,
medicalExpiration: pilotDto.medicalExpiration,
certificates: JSON.stringify(pilotDto.certificates),
endorsements: JSON.stringify(pilotDto.endorsements)
}
return await this.pilotService.update(partitionKey, rowKey, pilot);
return await this.pilotService.update(id, pilotDto);
} catch (error) {
const customError = error as CustomError;
@@ -113,14 +71,13 @@ export class PilotController {
}
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey')
// @UseGuards(AuthGuard)
@Delete(':id')
async delete(
@Param('partitionKey') partitionKey: string,
@Param('rowKey') rowKey: string
@Param('id') id: string,
) {
try {
return await this.pilotService.delete(partitionKey, rowKey);
return await this.pilotService.delete(id);
} catch (error) {
const customError = error as CustomError;

View File

@@ -1,10 +1,6 @@
import { Certificate } from "./certificate/certificate.entity";
import { Endorsement } from "./endorsement/endorsement.entity";
import { LogEntity } from "src/log/log.entity";
export class PilotDto {
partitionKey: string;
rowKey: string;
id: string;
name: string;
address: string;
city: string;
@@ -12,8 +8,4 @@ export class PilotDto {
postalCode: string;
email?: string;
phone?: string;
medicalClass?: string;
medicalExpiration?: string;
certificates: Certificate;
endorsements: Endorsement
}

View File

@@ -1,41 +1,44 @@
import { Module } from '@nestjs/common';
import { PilotController } from './pilot.controller';
import { PilotService } from './pilot.service';
import { AzureTableStorageModule } from '@noahspan/azure-database';
import { Pilot } from './pilot.entity';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Log } from 'src/log/log.entity';
// import { AzureTableStorageModule } from '@noahspan/azure-database';
import { PilotEntity } from './pilot.entity';
// import { ConfigModule, ConfigService } from '@nestjs/config';
// import { Log } from 'src/log/log.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
AzureTableStorageModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
connectionString: configService.get<string>('azureStorageConnectionString')
};
},
inject: [ConfigService]
}),
AzureTableStorageModule.forFeature(Log, {
createTableIfNotExists: false,
table: 'logs'
}),
AzureTableStorageModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
return {
connectionString: configService.get<string>('azureStorageConnectionString')
};
},
inject: [ConfigService]
}),
AzureTableStorageModule.forFeature(Pilot, {
createTableIfNotExists: false,
table: 'pilots'
})
// AzureTableStorageModule.forRootAsync({
// imports: [ConfigModule],
// useFactory: async (configService: ConfigService) => {
// return {
// connectionString: configService.get<string>('azureStorageConnectionString')
// };
// },
// inject: [ConfigService]
// }),
// AzureTableStorageModule.forFeature(Log, {
// createTableIfNotExists: false,
// table: 'logs'
// }),
// AzureTableStorageModule.forRootAsync({
// imports: [ConfigModule],
// useFactory: async (configService: ConfigService) => {
// return {
// connectionString: configService.get<string>('azureStorageConnectionString')
// };
// },
// inject: [ConfigService]
// }),
// AzureTableStorageModule.forFeature(PilotEntity, {
// createTableIfNotExists: false,
// table: 'pilots'
// }),
TypeOrmModule.forFeature([PilotEntity])
],
controllers: [PilotController],
exports: [PilotService],
providers: [PilotService]
})
export class PilotModule {}

View File

@@ -1,54 +1,46 @@
import { InjectRepository, Repository } from '@noahspan/azure-database';
import { Inject, Injectable } from '@nestjs/common';
import { Pilot } from './pilot.entity';
import { Log } from 'src/log/log.entity';
import { LogService } from 'src/log/log.service';
import { Injectable } from '@nestjs/common';
import { PilotEntity } from './pilot.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm';
import { PilotDto } from './pilot.dto';
import { CustomError } from 'src/error/customError';
@Injectable()
export class PilotService {
constructor(
@InjectRepository(Pilot) private readonly pilotRepository: Repository<Pilot>,
@InjectRepository(Log) private readonly logRepository: Repository<Log>
@InjectRepository(PilotEntity) private readonly pilotRepository: Repository<PilotEntity>
) {}
async find(partitionKey: string, rowKey: string): Promise<Pilot> {
return await this.pilotRepository.find(partitionKey, rowKey);
async find(id: string): Promise<PilotEntity> {
try {
const pilotEntity = await this.pilotRepository.findOneBy({ id });
if (pilotEntity) {
return pilotEntity
} else {
throw new CustomError('Pilot not found', 'Not found', 404)
}
} catch (error) {
throw error
}
}
async findAll(): Promise<Pilot[]> {
return await this.pilotRepository.findAll();
async findAll(): Promise<PilotEntity[]> {
return await this.pilotRepository.find();
}
async create(pilot: Pilot): Promise<Pilot> {
// try {
// return await this.pilotRepository.create(pilot);
// } catch (error) {
// throw new Error(error);
// }
return await this.pilotRepository.create(pilot);
async create(pilot: PilotDto): Promise<InsertResult> {
return await this.pilotRepository.insert(pilot);
}
async update(
partitionKey: string,
rowKey: string,
pilot: Pilot
): Promise<Pilot> {
return await this.pilotRepository.update(partitionKey, rowKey, pilot);
id: string,
pilot: PilotDto
): Promise<UpdateResult> {
return await this.pilotRepository.update(id, pilot);
}
async delete(partitionKey: string, rowKey: string): Promise<void> {
const pilotLogs: Log[] = await this.logRepository.findAll({
queryOptions: {
filter: `pilotId eq '${rowKey}'`
}
})
for (const pilotLog of pilotLogs) {
await this.logRepository.delete(pilotLog.partitionKey, pilotLog.rowKey);
}
await this.pilotRepository.delete(partitionKey, rowKey);
return
async delete(id: string): Promise<DeleteResult> {
return await this.pilotRepository.delete({ id });
}
}

View File

@@ -0,0 +1,133 @@
import {
Body,
Controller,
Delete,
Get,
HttpException,
Param,
Post,
Put,
Query,
UploadedFile,
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { AuthGuard } from '@noahspan/noahspan-modules';
import { CustomError } from '../error/customError';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileService } from '../file/file.service';
import { TrackService } from './track.service';
import { TrackEntity } from './track.entity';
import { TrackDto } from './track.dto';
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
@Controller('tracks')
export class TrackController {
constructor(
private readonly fileService: FileService,
private readonly trackService: TrackService
) {}
// @Get('id')
// async fin(@Param('id') id: string): Promise<TrackEntity> {
// try {
// return await this.trackService.find(id);
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode);
// }
// }
// @Get()
// async findAdd(): Promise<TrackEntity[]> {
// try {
// return await this.trackService.findAll();
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode)
// }
// }
@Post(':logId/:order')
async create(@Param('logId') logId: string, @Param('order') order: number, @UploadedFile() file: Express.Multer.File): Promise<InsertResult> {
try {
console.log(file)
return await this.trackService.create(logId, order, file);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
// @Put(':id')
// async update(@Param('id') id: string, @Body() trackDto: TrackDto): Promise<UpdateResult> {
// try {
// return await this.trackService.update(id, trackDto);
// } catch (error) {
// const customError = error as CustomError;
// throw new HttpException(customError.message, customError.statusCode)
// }
// }
@Delete(':id')
async delete(@Param('id') id: string): Promise<DeleteResult> {
try {
return await this.trackService.delete(id);
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@UseGuards(AuthGuard)
@Post(':partitionKey/:rowKey/track')
@UseInterceptors(FileInterceptor('file'))
async createTrack(@Param('rowKey') rowKey: string, @UploadedFile() file: Express.Multer.File) {
try {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, rowKey);
return { url }
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
@Get(':partitionKey/:rowKey/track')
async downloadTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<string> {
const containerName = 'tracks';
const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName)
return downloadedFile;
}
@UseGuards(AuthGuard)
@Delete(':partitionKey/:rowKey/track')
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
try {
const containerName = 'tracks';
return await this.fileService.deleteFile(containerName, rowKey, fileName)
} catch (error) {
const customError = error as CustomError;
throw new HttpException(customError.message, customError.statusCode);
}
}
}

View File

@@ -0,0 +1,5 @@
export class TrackDto {
logId: string;
order: number;
url?: string;
}

View File

@@ -0,0 +1,18 @@
import { LogEntity } from 'src/log/log.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity({ name: 'tracks' })
export class TrackEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
url: string;
@Column()
order: number;
@ManyToOne(() => LogEntity, (log: LogEntity) => log.tracks)
@JoinColumn({ name: 'logId' })
log: LogEntity;
}

View File

@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from "@nestjs/typeorm";
import { TrackEntity } from "./track.entity";
import { TrackController } from "./track.controller";
import { FileService } from "../file/file.service";
import { TrackService } from './track.service';
import { LogModule } from 'src/log/log.module';
@Module({
imports: [
LogModule,
TypeOrmModule.forFeature([TrackEntity])
],
controllers: [TrackController],
providers: [
FileService,
TrackService
]
})
export class TrackModule {}

View File

@@ -0,0 +1,88 @@
import { DeleteResult, InsertResult, Repository, UpdateResult } from "typeorm";
import { TrackDto } from "./track.dto";
import { TrackEntity } from "./track.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { Injectable } from "@nestjs/common";
import { LogService } from "src/log/log.service";
import { LogEntity } from "src/log/log.entity";
import { CustomError } from "src/error/customError";
import { FileService } from "src/file/file.service";
@Injectable()
export class TrackService {
constructor(
@InjectRepository(TrackEntity) private readonly trackRepository: Repository<TrackEntity>,
private readonly fileService: FileService,
private readonly logService: LogService
) {}
// async find(id: string): Promise<TrackEntity> {
// return await this.trackRepository.findOneBy({ id });
// }
// async findAll(logId: string): Promise<TrackEntity[]> {
// try {
// await this.trackRepository.findOneBy({ })
// } catch (error) {
// }
// }
async create(logId: string, order: number, file: Express.Multer.File): Promise<InsertResult> {
try {
const logEntity: LogEntity = await this.logService.find(logId);
if (logEntity) {
const containerName = 'tracks';
const url = await this.fileService.uploadFile(file, containerName, logId);
const track = this.trackRepository.create({
log: logEntity,
order: order,
url: url
});
return await this.trackRepository.insert(track);
} else {
throw new CustomError('Log not found', 'Not found', 404)
}
} catch (error) {
console.log(error)
throw error;
}
}
// async update(id: string, track: TrackDto): Promise<UpdateResult> {
// return await this.trackRepository.update(id, track);
// }
async delete(id: string): Promise<DeleteResult> {
try {
} catch (error) {
}
return await this.trackRepository.delete({ id });
}
// async createTrack()
// async downloadTrack(): Promise<string> {
// const containerName = 'tracks';
// const downloadedFile: string = await this.fileService.downloadFile(containerName, rowKey, fileName);
// return downloadedFile;
// }
// async deleteTrack(): Promsie<void> {
// try {
// const containerName = 'tracks';
// return await this.fileService.deleteFile(containerName, rowKey, fileName)
// } catch (error) {
// const customError = error as CustomError;
// throw customError;
// }
// }
}

View File

@@ -12,15 +12,15 @@
},
"dependencies": {
"@azure/msal-browser": "^4.0.1",
"@azure/msal-react": "^3.0.1",
"@azure/msal-react": "3.0.1",
"@noahspan/noahspan-components": "^1.9.1",
"axios": "^1.7.2",
"dotenv": "^16.4.7",
"leaflet": "^1.9.4",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
"react-leaflet": "^5.0.0",
"react-leaflet": "^4",
"react-leaflet-kml": "^2.1.2",
"react-router-dom": "^6.23.0",
"swiper": "^11.2.6"

View File

@@ -1,7 +1,7 @@
import { FormMode } from '../../enums/formMode';
export interface ILogFormProps {
entryId?: string;
logId?: string;
isDrawerOpen: boolean;
mode: FormMode;
onOpenClose: (mode: FormMode) => void;

View File

@@ -5,5 +5,5 @@ export interface ILogFormState {
isDisabled: boolean;
isLoading: boolean;
pilotOptions: { label: string; value: string }[];
selectedEntryPilotName: string;
selectedPilotName: string;
}

View File

@@ -28,7 +28,7 @@ import { FormMode } from '../../enums/formMode';
import { usePilots } from '../../hooks/pilots/UsePilots';
const LogForm: React.FC<ILogFormProps> = ({
entryId,
logId,
isDrawerOpen,
mode,
onOpenClose
@@ -39,7 +39,6 @@ const LogForm: React.FC<ILogFormProps> = ({
const isAuthenticated = useIsAuthenticated();
const defaultValues = {
pilotId: '',
pilotName: '',
date: null,
aircraftMakeModel: '',
aircraftIdentity: '',
@@ -79,14 +78,14 @@ const LogForm: React.FC<ILogFormProps> = ({
const accessToken: string = await getAccessToken();
if (!entryId) {
if (!logId) {
await httpClient.post(`api/logs`, data, {
headers: {
Authorization: accessToken
}
});
} else {
await httpClient.put(`api/logs/log/${entryId}`, data, {
await httpClient.put(`api/logs/${logId}`, data, {
headers: {
Authorization: accessToken
}
@@ -120,21 +119,13 @@ const LogForm: React.FC<ILogFormProps> = ({
? { headers: { Authorization: await getAccessToken() } }
: {};
const response: AxiosResponse = await httpClient.get(
`api/logs/log/${entryId}`,
`api/logs/${logId}`,
config
);
const entry = response.data;
const log = response.data;
// if (mode !== FormMode.ADD) {
// const pilot = pilots?.find((pilot) => pilot.id === entry.pilotId);
// console.log(pilot.name)
// dispatch({
// type: 'SET_SELECTED_ENTRY_PILOT_NAME',
// payload: pilot.name
// });
// }
methods.reset(entry);
log.pilotId = log.pilot.id
methods.reset(log);
} catch (error) {
const axiosError = error as AxiosError;
@@ -144,10 +135,10 @@ const LogForm: React.FC<ILogFormProps> = ({
}
};
if (entryId && isDrawerOpen) {
if (logId && isDrawerOpen) {
getEntry();
}
}, [entryId]);
}, [logId]);
useEffect(() => {
if (pilots && FormMode.ADD) {
@@ -214,10 +205,6 @@ const LogForm: React.FC<ILogFormProps> = ({
(pilot) => (pilot.id = event.target.value)
);
if (pilot) {
methods.setValue('pilotName', pilot.name);
}
methods.setValue('pilotId', event.target.value);
}}
options={

View File

@@ -6,14 +6,14 @@ type Action =
| { type: 'SET_IS_DISABLED'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean }
| { type: 'SET_PILOT_OPTIONS'; payload: { label: string; value: string }[] }
| { type: 'SET_SELECTED_ENTRY_PILOT_NAME'; payload: string };
| { type: 'SET_SELECTED_PILOT_NAME'; payload: string };
export const initialState: ILogFormState = {
alert: undefined,
isDisabled: false,
isLoading: true,
pilotOptions: [],
selectedEntryPilotName: ''
selectedPilotName: ''
};
export const reducer = (
@@ -45,10 +45,10 @@ export const reducer = (
pilotOptions: action.payload
};
}
case 'SET_SELECTED_ENTRY_PILOT_NAME': {
case 'SET_SELECTED_PILOT_NAME': {
return {
...state,
selectedEntryPilotName: action.payload
selectedPilotName: action.payload
};
}
default: {

View File

@@ -29,7 +29,7 @@ const LogTracks = ({ isDrawerOpen, mode, onOpenClose, selectedRowKey }: LogTrack
const getLog = async (): Promise<ILogbookEntry> => {
const logResponse: AxiosResponse = await httpClient.get(
`api/logs/log/${selectedRowKey}`,
`api/logs/${selectedRowKey}`,
await getConfig()
);
const logData: ILogbookEntry = logResponse.data;

View File

@@ -1,8 +1,4 @@
import { ColumnDef } from "@noahspan/noahspan-components";
export interface ILogbookEntry {
partitionKey: string;
rowKey: string;
id: string;
pilotId: string;
pilotName: string;

View File

@@ -13,6 +13,6 @@ export interface ILogbookState {
isFormOpen: boolean;
isLoading: boolean;
isTracksOpen: boolean;
selectedEntryId: string | undefined;
selectedLogId: string | undefined;
tracksMode: FormMode;
}

View File

@@ -42,9 +42,9 @@ const Logbook: React.FC<unknown> = () => {
},
cell: (info: any) => (
<ActionMenu
id={info.row.original.rowKey}
onDelete={onDeleteEntry}
onOpenCloseForm={onOpenCloseEntryForm}
id={info.row.original.id}
onDelete={onDeleteLog}
onOpenCloseForm={onOpenCloseLogForm}
onOpenCloseTracks={onOpenCloseTracks}
/>
)
@@ -94,16 +94,16 @@ const Logbook: React.FC<unknown> = () => {
}
};
const onOpenCloseEntryForm = (mode: FormMode, entryId?: string) => {
const onOpenCloseLogForm = (mode: FormMode, logId?: string) => {
switch (mode) {
case FormMode.ADD:
case FormMode.EDIT:
case FormMode.VIEW:
dispatch({
type: 'SET_OPEN_CLOSE_ENTRY_FORM',
type: 'SET_OPEN_CLOSE_LOG_FORM',
payload: {
formMode: mode,
selectedEntryId: entryId,
selectedLogId: logId,
isFormOpen: true
}
});
@@ -111,10 +111,10 @@ const Logbook: React.FC<unknown> = () => {
break;
case FormMode.CANCEL:
dispatch({
type: 'SET_OPEN_CLOSE_ENTRY_FORM',
type: 'SET_OPEN_CLOSE_LOG_FORM',
payload: {
formMode: mode,
selectedEntryId: undefined,
selectedLogId: undefined,
isFormOpen: false
}
});
@@ -151,10 +151,10 @@ const Logbook: React.FC<unknown> = () => {
}
}
const onDeleteEntry = (entryId: string) => {
const onDeleteLog = (logId: string) => {
dispatch({
type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: true, selectedEntryId: entryId }
payload: { isConfirmationDialogOpen: true, selectedLogId: logId }
});
};
@@ -167,11 +167,11 @@ const Logbook: React.FC<unknown> = () => {
? { headers: { Authorization: `${token}` } }
: {};
await httpClient.delete(`api/logs/log/${state.selectedEntryId}`, config);
await httpClient.delete(`api/logs/${state.selectedLogId}`, config);
dispatch({
type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: false, selectedEntryId: undefined }
payload: { isConfirmationDialogOpen: false, selectedLogId: undefined }
});
await getLogbookEntries();
} catch (error) {
@@ -189,7 +189,7 @@ const Logbook: React.FC<unknown> = () => {
const onConfirmationDialogCancel = () => {
dispatch({
type: 'SET_DELETE',
payload: { isConfirmationDialogOpen: false, selectedEntryId: undefined }
payload: { isConfirmationDialogOpen: false, selectedLogId: undefined }
});
};
@@ -233,7 +233,7 @@ const Logbook: React.FC<unknown> = () => {
<Grid display="flex" justifyContent="right" size={isMedium ? 1 : 6}>
{isAuthenticated &&
<Button
onClick={() => onOpenCloseEntryForm(FormMode.ADD)}
onClick={() => onOpenCloseLogForm(FormMode.ADD)}
startIcon={<Icon iconName={IconName.PLUS} />}
variant="contained"
data-testid="pilot-add-button"
@@ -261,7 +261,7 @@ const Logbook: React.FC<unknown> = () => {
<Table columns={state.columns} data={state.entries} />
)}
{!isMedium && state.entries.length > 0 &&
<LogbookCard logs={state.entries} onDelete={onDeleteEntry} mode='logbook' onOpenCloseForm={onOpenCloseEntryForm} />
<LogbookCard logs={state.entries} onDelete={onDeleteLog} mode='logbook' onOpenCloseForm={onOpenCloseLogForm} />
}
</Grid>
)}
@@ -278,10 +278,10 @@ const Logbook: React.FC<unknown> = () => {
</Grid>
{state.isFormOpen && (
<LogForm
entryId={state.selectedEntryId}
logId={state.selectedLogId}
isDrawerOpen={state.isFormOpen}
mode={state.formMode}
onOpenClose={(mode) => onOpenCloseEntryForm(mode)}
onOpenClose={(mode) => onOpenCloseLogForm(mode)}
/>
)}
{state.isConfirmDialogOpen && (
@@ -299,7 +299,7 @@ const Logbook: React.FC<unknown> = () => {
isDrawerOpen={state.isTracksOpen}
mode={state.tracksMode}
onOpenClose={(mode) => onOpenCloseTracks(mode)}
selectedRowKey={state.selectedEntryId}
selectedRowKey={state.selectedLogId}
/>
}
</Box>

View File

@@ -10,7 +10,7 @@ type Action =
type: 'SET_DELETE';
payload: {
isConfirmationDialogOpen: boolean;
selectedEntryId: string | undefined;
selectedLogId: string | undefined;
};
}
| { type: 'SET_ENTRIES'; payload: ILogbookEntry[] }
@@ -19,10 +19,10 @@ type Action =
| { type: 'SET_IS_CONFIRMATION_DIALOG_LOADING'; payload: boolean }
| { type: 'SET_IS_LOADING'; payload: boolean }
| {
type: 'SET_OPEN_CLOSE_ENTRY_FORM';
type: 'SET_OPEN_CLOSE_LOG_FORM';
payload: {
formMode: FormMode;
selectedEntryId: string | undefined;
selectedLogId: string | undefined;
isFormOpen: boolean;
};
}
@@ -38,7 +38,7 @@ export const initialState: ILogbookState = {
isFormOpen: false,
isLoading: false,
isTracksOpen: false,
selectedEntryId: undefined,
selectedLogId: undefined,
tracksMode: FormMode.CANCEL
};
@@ -57,7 +57,7 @@ export const reducer = (
return {
...state,
isConfirmDialogOpen: action.payload.isConfirmationDialogOpen,
selectedEntryId: action.payload.selectedEntryId
selectedLogId: action.payload.selectedLogId
};
}
case 'SET_ENTRIES': {
@@ -90,12 +90,12 @@ export const reducer = (
isLoading: action.payload
};
}
case 'SET_OPEN_CLOSE_ENTRY_FORM': {
case 'SET_OPEN_CLOSE_LOG_FORM': {
return {
...state,
formMode: action.payload.formMode,
isFormOpen: action.payload.isFormOpen,
selectedEntryId: action.payload.selectedEntryId
selectedLogId: action.payload.selectedLogId
};
}
case 'SET_OPEN_CLOSE_TRACKS': {
@@ -103,7 +103,7 @@ export const reducer = (
...state,
tracksMode: action.payload.tracksMode,
isTracksOpen: action.payload.isTracksOpen,
selectedEntryId: action.payload.selectedRowKey
selectedLogId: action.payload.selectedRowKey
}
}
default: {

View File

@@ -36,16 +36,12 @@ const PilotForm: React.FC<IPilotFormProps> = ({
const [isPeoplePickerLoading, setIsPeoplePickerLoading] =
useState<boolean>(false);
const [selectedPerson, setSelectedPerson] = useState<Person>({
userPrincipalName: '',
displayName: ''
});
const [isLoading, setIsLoading] = useState<boolean>(false);
const { getAccessToken } = useAccessToken();
const isAuthenticated = useIsAuthenticated();
const defaultValues = {
partitionKey: 'pilot',
rowKey: 'noah@noahspannbauer.com',
id: '',
name: '',
address: '',
city: '',
@@ -53,10 +49,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
postalCode: '',
email: '',
phone: '',
medicalClass: '',
medicalExpiration: '',
certificates: [],
endorsements: []
};
const methods = useForm({
defaultValues: defaultValues
@@ -100,7 +92,6 @@ const PilotForm: React.FC<IPilotFormProps> = ({
value: Person,
_reason: string
) => {
methods.setValue('id', value.userPrincipalName!.toString());
methods.setValue('name', value.displayName!.toString());
setSelectedPerson(value);
};
@@ -159,16 +150,12 @@ const PilotForm: React.FC<IPilotFormProps> = ({
? { headers: { Authorization: await getAccessToken() } }
: {};
const response: AxiosResponse = await httpClient.get(
`api/pilots/pilot/${pilotId}`,
`api/pilots/${pilotId}`,
config
);
const pilot = response.data;
pilot.certificates = JSON.parse(pilot.certificates);
pilot.endorsements = JSON.parse(pilot.endorsements)
setSelectedPerson({
userPrincipalName: pilot.id,
displayName: pilot.name
});
methods.reset(pilot);
@@ -380,7 +367,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
</Grid>
</>
}
{isAuthenticated &&
{/* {isAuthenticated &&
<Grid size={12}>
<PilotFormMedical
isDisabled={isDisabled}
@@ -392,7 +379,7 @@ const PilotForm: React.FC<IPilotFormProps> = ({
</Grid>
<Grid size={12}>
<PilotFormEndorsements isDisabled={isDisabled} mode={mode} />
</Grid>
</Grid> */}
<Grid display="flex" gap={2} justifyContent="right" size={12}>
<Button
disabled={

View File

@@ -1,6 +1,4 @@
export interface Pilot {
partitionKey: string;
rowKey: string;
id: string;
name: string;
};

View File

@@ -36,7 +36,7 @@ const Pilots: React.FC<unknown> = () => {
const response: AxiosResponse = await httpClient.get(
`api/pilots`
);
console.log(response);
if (response.data.length > 0) {
dispatch({ type: 'SET_PILOTS', payload: response.data });
@@ -136,13 +136,14 @@ const Pilots: React.FC<unknown> = () => {
},
{
header: 'Actions',
cell: (info: any) => (
<ActionMenu
id={info.row.original.rowKey}
cell: (info: any) => {
console.log(info)
return <ActionMenu
id={info.row.original.id}
onDelete={onDeleteEntry}
onOpenCloseForm={onOpenClosePilotForm}
/>
)
}
}
];

View File

@@ -1,16 +1,16 @@
version: '3.8'
services:
# azurite:
# container_name: azurite-flying
# image: mcr.microsoft.com/azure-storage/azurite
# ports:
# - '10000:10000'
# - '10001:10001'
# - '10002:10002'
# command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
# volumes:
# - ./azurite-flying:/data
azurite:
container_name: azurite-flying
image: mcr.microsoft.com/azure-storage/azurite
ports:
- '10000:10000'
- '10001:10001'
- '10002:10002'
command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
volumes:
- ./azurite-flying:/data
restore:
container_name: restore

21759
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -25,5 +25,10 @@
},
"lint-staged": {
"**/*": "prettier --write \"**/src/**/*.ts\" \"**/test/**/*.ts\" --ignore-unknown"
}
},
"workspaces": [
"api",
"app",
"tests"
]
}

13428
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
packages:
- 'api'
- 'app'