96 switch from azure table storage to sqlite (#97)
* adding typeorm to api * switching to sqlite * switching to sqlite * switching to sqlite * migrating to sqlite * updating terraform * updating infrastructure
This commit was merged in pull request #97.
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
||||
import { Observable, map } from 'rxjs';
|
||||
|
||||
export class LogInterceptor implements NestInterceptor {
|
||||
intercept(context: ExecutionContext, handler: CallHandler): Observable<any> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
console.log(token)
|
||||
|
||||
if (!token) {
|
||||
return handler.handle().pipe(
|
||||
map((data) => {
|
||||
if (data.length) {
|
||||
const logs = data.map((log) => {
|
||||
return {
|
||||
partitionKey: log.partitionKey,
|
||||
rowKey: log.rowKey,
|
||||
pilotId: log.pilotId,
|
||||
pilotName: log.pilotName,
|
||||
date: log.date,
|
||||
aircraftMakeModel: log.aircraftMakeModel,
|
||||
routeFrom: log.routeFrom,
|
||||
routeTo: log.routeTo,
|
||||
durationOfFlight: log.durationOfFlight,
|
||||
tracks: log.tracks,
|
||||
notes: log.notes
|
||||
};
|
||||
});
|
||||
|
||||
return logs;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return handler.handle().pipe(map((data) => data));
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,23 @@ 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 { AuthGuard, Public } from '@noahspan/noahspan-modules';
|
||||
import { LogInterceptor } from './log.interceptor';
|
||||
import { FileService } from '../file/file.service';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { DeleteResult, InsertResult, UpdateResult } from 'typeorm';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
|
||||
const reflector = new Reflector();
|
||||
|
||||
@Controller('logs')
|
||||
@UseInterceptors(new LogInterceptor(reflector))
|
||||
export class LogController {
|
||||
constructor(
|
||||
private readonly fileService: FileService,
|
||||
@@ -30,116 +31,73 @@ export class LogController {
|
||||
) {}
|
||||
|
||||
|
||||
@Get(':partitionKey/:rowKey')
|
||||
@UseInterceptors(new LogInterceptor())
|
||||
@Get(':id')
|
||||
@Public()
|
||||
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);
|
||||
console.log(id)
|
||||
return await this.logService.find(id);
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
console.log(error)
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UseInterceptors(new LogInterceptor())
|
||||
async findAll(): Promise<Log[]> {
|
||||
@Public()
|
||||
async findAll(): Promise<LogEntity[]> {
|
||||
try {
|
||||
console.log('blah')
|
||||
return await this.logService.findAll();
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
console.log(error)
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Post()
|
||||
async create(@Body() logDto: LogDto): Promise<Log> {
|
||||
try {
|
||||
const log = new Log();
|
||||
|
||||
Object.assign(log, logDto);
|
||||
|
||||
return await this.logService.create(log);
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Put(':partitionKey/:rowKey')
|
||||
async update(
|
||||
@Param('partitionKey') partitionKey: string,
|
||||
@Param('rowKey') rowKey: string,
|
||||
@Body() logDto: LogDto
|
||||
): Promise<Log> {
|
||||
try {
|
||||
const log = new Log();
|
||||
|
||||
Object.assign(log, logDto);
|
||||
|
||||
return await this.logService.update(partitionKey, rowKey, log);
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete(':partitionKey/:rowKey')
|
||||
async delete(
|
||||
@Param('partitionKey') partitionKey: string,
|
||||
@Param('rowKey') rowKey: string
|
||||
): Promise<void> {
|
||||
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 }
|
||||
@Post()
|
||||
@UseGuards(AuthGuard)
|
||||
async create(@Body() logDto: LogDto): Promise<InsertResult> {
|
||||
try {
|
||||
return await this.logService.create(logDto);
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
console.log(error)
|
||||
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;
|
||||
@Put(':id')
|
||||
@UseGuards(AuthGuard)
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() logDto: LogDto
|
||||
): Promise<UpdateResult> {
|
||||
try {
|
||||
console.log(id)
|
||||
console.log(logDto)
|
||||
return await this.logService.update(id, logDto);
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
console.log(error)
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete(':partitionKey/:rowKey/track')
|
||||
async deleteTrack(@Param('rowKey') rowKey: string, @Query('fileName') fileName: string): Promise<void> {
|
||||
async delete(
|
||||
@Param('id') id: string,
|
||||
): Promise<DeleteResult> {
|
||||
try {
|
||||
const containerName = 'tracks';
|
||||
|
||||
return await this.fileService.deleteFile(containerName, rowKey, fileName)
|
||||
return await this.logService.delete(id);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
const customError = error as CustomError;
|
||||
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import { PilotEntity } from "src/pilot/pilot.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;
|
||||
tracks?: []
|
||||
}
|
||||
|
||||
@@ -1,29 +1,85 @@
|
||||
export class Log {
|
||||
partitionKey: string;
|
||||
rowKey: string;
|
||||
import { PilotEntity } from 'src/pilot/pilot.entity';
|
||||
import { TrackEntity } from 'src/track/track.entity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'logs' })
|
||||
export class LogEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string
|
||||
|
||||
@Column()
|
||||
pilotId: string;
|
||||
pilotName: string;
|
||||
date: string;
|
||||
|
||||
@Column()
|
||||
date: Date;
|
||||
|
||||
@Column()
|
||||
aircraftMakeModel: string;
|
||||
|
||||
@Column()
|
||||
aircraftIdentity: string;
|
||||
|
||||
@Column()
|
||||
routeFrom: string;
|
||||
|
||||
@Column()
|
||||
routeTo: string;
|
||||
durationOfFlight: number | null;
|
||||
|
||||
@Column()
|
||||
durationOfFlight: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
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;
|
||||
}
|
||||
|
||||
@Column({ nullable: true })
|
||||
simulatorAtd: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
landingsDay: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
landingsNight: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
groundTrainingReceived: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
flightTrainingReceived: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
crossCountry: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
night: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
solo: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
pilotInCommand: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
instrumentActual: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
instrumentSimulated: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
instrumentApproaches: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
instrumentHolds: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
instrumentNavTrack: number | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
notes: string | null;
|
||||
|
||||
@OneToMany(() => TrackEntity, (track: TrackEntity) => track.log)
|
||||
tracks: TrackEntity[]
|
||||
|
||||
@ManyToOne(() => PilotEntity, (pilot: PilotEntity) => pilot.logs, {onDelete: 'CASCADE', onUpdate: 'CASCADE'})
|
||||
@JoinColumn({ name: 'pilotId' })
|
||||
pilot: PilotEntity;
|
||||
}
|
||||
59
api/src/log/log.interceptor.ts
Normal file
59
api/src/log/log.interceptor.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { CallHandler, ExecutionContext, NestInterceptor, UnauthorizedException } from '@nestjs/common';
|
||||
import { Observable, map } from 'rxjs';
|
||||
import { LogEntity } from './log.entity';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { CustomJwtPayload } from 'src/interfaces/customJwtPayload.interface';
|
||||
import { IS_PUBLIC_KEY } from '@noahspan/noahspan-modules';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
|
||||
export class LogInterceptor implements NestInterceptor {
|
||||
constructor(private reflector: Reflector) {}
|
||||
|
||||
intercept(context: ExecutionContext, handler: CallHandler): Observable<any> {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
return handler.handle().pipe(
|
||||
map((data: LogEntity[]) => {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
const limitData = (data) => {
|
||||
return data.map((log: LogEntity) => {
|
||||
return {
|
||||
id: log.id,
|
||||
pilot: {
|
||||
name: log.pilot.name
|
||||
},
|
||||
date: log.date,
|
||||
aircraftMakeModel: log.aircraftMakeModel,
|
||||
routeFrom: log.routeFrom,
|
||||
routeTo: log.routeTo,
|
||||
durationOfFlight: log.durationOfFlight,
|
||||
tracks: log.tracks,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (req.headers.authorization) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
const jwtPayload: CustomJwtPayload = jwtDecode(token);
|
||||
|
||||
if (jwtPayload.roles.includes('Flying.Read')) {
|
||||
const logs = limitData(data);
|
||||
|
||||
return logs;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
} else if (!req.headers.authorization && isPublic) {
|
||||
const publicData = limitData(data)
|
||||
const logs = publicData.slice(0, 5)
|
||||
|
||||
return logs;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,19 @@
|
||||
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 { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { LogEntity } from './log.entity';
|
||||
import { 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'
|
||||
}),
|
||||
PilotModule,
|
||||
TypeOrmModule.forFeature([LogEntity])
|
||||
],
|
||||
controllers: [LogController],
|
||||
exports: [LogService],
|
||||
providers: [
|
||||
ConfigService,
|
||||
FileService,
|
||||
|
||||
@@ -1,34 +1,63 @@
|
||||
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 { FileService } from 'src/file/file.service';
|
||||
|
||||
@Injectable()
|
||||
export class LogService {
|
||||
constructor(
|
||||
@InjectRepository(Log) private readonly logRepository: Repository<Log>
|
||||
@InjectRepository(LogEntity) private readonly logRepository: Repository<LogEntity>,
|
||||
private readonly fileService: FileService,
|
||||
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']
|
||||
});
|
||||
|
||||
return logEntity;
|
||||
}
|
||||
|
||||
async findAll(): Promise<Log[]> {
|
||||
return await this.logRepository.findAll();
|
||||
async findAll(): Promise<LogEntity[]> {
|
||||
return await this.logRepository.find({
|
||||
relations: ['pilot', 'tracks']
|
||||
});
|
||||
}
|
||||
|
||||
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> {
|
||||
await this.fileService.deleteFolder('tracks', id);
|
||||
|
||||
return await this.logRepository.delete({ id });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user