Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #686 External API for FSP Tracker #688

Merged
merged 10 commits into from
Sep 6, 2024
12 changes: 7 additions & 5 deletions api/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ScheduleModule } from '@nestjs/schedule';
import { TypeOrmModule } from '@nestjs/typeorm';

// Core Modules
import { AttachmentModule } from './modules/attachment/attachment.module';
import { InteractionModule } from './modules/interaction/interaction.module';
import { DistrictModule } from './modules/district/district.module';
import { ForestClientModule } from './modules/forest-client/forest-client.module';
import { ProjectModule } from './modules/project/project.module';
import { InteractionModule } from './modules/interaction/interaction.module';
import { ProjectAuthModule } from './modules/project/project-auth.module';
import { ProjectModule } from './modules/project/project.module';
import { PublicCommentModule } from './modules/public-comment/public-comment.module';
import { SubmissionModule } from './modules/submission/submission.module';
import { SpatialFeatureModule } from './modules/spatial-feature/spatial-feature.module';
import { SubmissionModule } from './modules/submission/submission.module';

// Other Modules
import { ExternalModule } from '@src/app/modules/external/external.module';
import { LoggerModule } from 'nestjs-pino';
import { SecurityModule } from '../core/security/security.module';
import { AppConfigModule } from './modules/app-config/app-config.module';
import { AppConfigService } from './modules/app-config/app-config.provider';
import { SecurityModule } from '../core/security/security.module'

function getLogLevel():string {
return process.env.LOG_LEVEL || 'info';
Expand Down Expand Up @@ -66,6 +67,7 @@ function getLogLevel():string {
PublicCommentModule,
SubmissionModule,
SpatialFeatureModule,
ExternalModule
],
})

Expand Down
13 changes: 13 additions & 0 deletions api/src/app/modules/external/external.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { FspTrackerExternalModule } from '@src/app/modules/external/fsp-tracker/fsp-tracker.module';

/**
* "External" module provides external facing APIs for other system to
* interface with FOM.
*/
@Module({
imports: [
FspTrackerExternalModule
]
})
export class ExternalModule {}
42 changes: 42 additions & 0 deletions api/src/app/modules/external/fsp-tracker/fsp-tracker.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ProjectFindCriteria } from '@api-modules/project/project.service';
import { ArgumentMetadata, BadRequestException, Controller, Get, HttpStatus, Injectable, PipeTransform, Query } from '@nestjs/common';
import { ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
import { FomFspTrackerResponse } from '@src/app/modules/external/fsp-tracker/fsp-tracker.dto';
import { FspTrackerService } from '@src/app/modules/external/fsp-tracker/fsp-tracker.service';
import { PinoLogger } from 'nestjs-pino';

// Custom pipe transformer for controller parameter conversion.
@Injectable()
export class PositiveIntPipe implements PipeTransform<number, number> {
transform(value: any, metadata: ArgumentMetadata) {

if(/^\d+$/.test(value)) {
const intValue = parseInt(value);
if ( intValue > 0) {
return value;
}
}

throw new BadRequestException('Value must be positive integer.');
}
}

@ApiTags("external")
@Controller("external")
export class FspTrackerController {
constructor(
private readonly service: FspTrackerService,
private readonly _logger: PinoLogger) {
}

@Get("fsp-tracker")
@ApiQuery({ name: 'fspId', required: true})
@ApiResponse({ status: HttpStatus.OK, type: [FomFspTrackerResponse] })
basilv marked this conversation as resolved.
Show resolved Hide resolved
async fspTracker(
@Query('fspId', PositiveIntPipe) fspId: number
): Promise<FomFspTrackerResponse[]> {
const findCriteria: ProjectFindCriteria = new ProjectFindCriteria();
findCriteria.fspId = fspId
return this.service.find(findCriteria);
}
}
17 changes: 17 additions & 0 deletions api/src/app/modules/external/fsp-tracker/fsp-tracker.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { ForestClientResponse } from "@src/app/modules/forest-client/forest-client.dto";

export class FomFspTrackerResponse {

@ApiProperty()
fomId: number;

@ApiProperty()
name: string;

@ApiProperty()
fspId: number;

@ApiPropertyOptional()
forestClient: ForestClientResponse;
}
18 changes: 18 additions & 0 deletions api/src/app/modules/external/fsp-tracker/fsp-tracker.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FspTrackerController } from '@src/app/modules/external/fsp-tracker/fsp-tracker.controller';
import { FspTrackerService } from '@src/app/modules/external/fsp-tracker/fsp-tracker.service';
import { ForestClientModule } from '@src/app/modules/forest-client/forest-client.module';
import { Project } from '@src/app/modules/project/project.entity';
import { ProjectModule } from '@src/app/modules/project/project.module';

@Module({
imports: [
TypeOrmModule.forFeature([Project]),
ProjectModule,
ForestClientModule
],
controllers: [FspTrackerController],
providers: [FspTrackerService]
})
export class FspTrackerExternalModule {}
52 changes: 52 additions & 0 deletions api/src/app/modules/external/fsp-tracker/fsp-tracker.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { FomFspTrackerResponse } from "@api-modules/external/fsp-tracker/fsp-tracker.dto";
import { Project } from "@api-modules/project/project.entity";
import { DataService } from "@core";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { ForestClientService } from "@src/app/modules/forest-client/forest-client.service";
import { ProjectFindCriteria } from "@src/app/modules/project/project.service";
import { PinoLogger } from "nestjs-pino";
import { Repository } from "typeorm";

@Injectable()
export class FspTrackerService extends DataService<Project, Repository<Project>, FomFspTrackerResponse> {

constructor(
@InjectRepository(Project)
repository: Repository<Project>,
private forestClientService: ForestClientService,
logger: PinoLogger
) {
super(repository, new Project(), logger);
}

async find(findCriteria: ProjectFindCriteria):Promise<FomFspTrackerResponse[]> {
this.logger.debug('Find criteria: %o', findCriteria);

const query = this.repository.createQueryBuilder("p")
.leftJoinAndSelect("p.forestClient", "forestClient")
.addOrderBy('p.project_id', 'DESC');
findCriteria.applyFindCriteria(query);
query.limit(2500); // Can't use take(). Limit # of results to avoid system strain.

const queryResults:Project[] = await query.getMany();
if (queryResults && queryResults.length > 0) {
this.logger.debug(`${queryResults.length} project(s) found.`);
return queryResults.map(project => this.convertEntity(project));
}
this.logger.debug('No result found.');
return [];
}

convertEntity(entity: Project): FomFspTrackerResponse {
const response = new FomFspTrackerResponse();
response.fomId = entity.id
response.name = entity.name
response.fspId = entity.fspId;
if (entity.forestClient != null) {
response.forestClient = this.forestClientService.convertEntity(entity.forestClient);
}
return response;
}

}
Loading