Skip to content

Commit

Permalink
Merge pull request #256 from Vizzuality/feature/api/MARXAN-333-vl-mig…
Browse files Browse the repository at this point in the history
…rate-from-proxy-controller

Feature/api/marxan 333 vl migrate from proxy controller
  • Loading branch information
aagm authored Jun 10, 2021
2 parents 564dae0 + d112472 commit fdaa7d5
Show file tree
Hide file tree
Showing 36 changed files with 5,337 additions and 613 deletions.
18 changes: 18 additions & 0 deletions api/apps/api/src/add-swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';

export function addSwagger(app: INestApplication): OpenAPIObject {
const swaggerOptions = new DocumentBuilder()
.setTitle('MarxanCloud API')
.setDescription('MarxanCloud is a conservation planning platform.')
.setVersion(process.env.npm_package_version || 'development')
.addBearerAuth(
{
type: 'http',
},
'BearerAuth',
)
.build();

return SwaggerModule.createDocument(app, swaggerOptions);
}
6 changes: 4 additions & 2 deletions api/apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import { ApiEventsModule } from '@marxan-api/modules/api-events/api-events.modul
import { ProtectedAreasModule } from '@marxan-api/modules/protected-areas/protected-areas.module';
import { ProxyModule } from '@marxan-api/modules/proxy/proxy.module';
import { ScenariosPlanningUnitModule } from './modules/scenarios-planning-unit/scenarios-planning-unit.module';
import { PlanningUnitsProtectionLevelModule } from './modules/planning-units-protection-level';
import { AnalysisModule } from './modules/analysis/analysis.module';
import { PlanningUnitsProtectionLevelModule } from '@marxan-api/modules/planning-units-protection-level';
import { AnalysisModule } from '@marxan-api/modules/analysis/analysis.module';
import { PlanningUnitsModule } from '@marxan-api/modules/planning-units/planning-units.module';

@Module({
imports: [
Expand Down Expand Up @@ -55,6 +56,7 @@ import { AnalysisModule } from './modules/analysis/analysis.module';
ScenariosPlanningUnitModule,
PlanningUnitsProtectionLevelModule,
AnalysisModule,
PlanningUnitsModule,
],
controllers: [AppController, PingController],
providers: [
Expand Down
38 changes: 38 additions & 0 deletions api/apps/api/src/bootstrap-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

import * as helmet from 'helmet';
import { CorsUtils } from './utils/cors.utils';
import { AppConfig } from '@marxan-api/utils/config.utils';
import { ValidationPipe } from '@nestjs/common';

export async function bootstrapSetUp() {
const app = await NestFactory.create(AppModule);

// We forcibly prevent the app from starting if no `API_AUTH_JWT_SECRET`
// environment variable has been set.
if (!AppConfig.get('auth.jwt.secret')) {
throw new Error(
'No secret configured for the signing of JWT tokens. Please set the `API_AUTH_JWT_SECRET` environment variable.',
);
}

app.use(helmet());
app.enableCors({
allowedHeaders: 'Content-Type,Authorization,Content-Disposition',
exposedHeaders: 'Authorization',
origin: CorsUtils.originHandler,
});

app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);

return app;

//await app.listen(3000);
}
4 changes: 3 additions & 1 deletion api/apps/api/src/decorators/shapefile.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export function ApiConsumesShapefile(withGeoJsonResponse = true) {
type: 'object',
properties: {
file: {
type: 'Zip file containing .shp, .dbj, .prj and .shx files',
description:
'Zip file containing .shp, .dbj, .prj and .shx files',
type: 'string',
format: 'binary',
},
},
Expand Down
11 changes: 11 additions & 0 deletions api/apps/api/src/generate-swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { OpenAPIObject } from '@nestjs/swagger';
import { writeFileSync } from 'fs';
import { addSwagger } from './add-swagger';
import { bootstrapSetUp } from './bootstrap-app';

export async function generateSwagger() {
const app = await bootstrapSetUp();
const swaggerDocument = addSwagger(app);
writeFileSync('./swagger.json', JSON.stringify(swaggerDocument));
}
generateSwagger();
53 changes: 7 additions & 46 deletions api/apps/api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,14 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { bootstrapSetUp } from '@marxan-api/bootstrap-app';
import { addSwagger } from '@marxan-api/add-swagger';
import { SwaggerModule } from '@nestjs/swagger';

import * as helmet from 'helmet';
import { CorsUtils } from './utils/cors.utils';
import { AppConfig } from '@marxan-api/utils/config.utils';
import { ValidationPipe } from '@nestjs/common';
import { AllExceptionsFilter } from '@marxan-api/filters/all-exceptions.exception.filter';
export async function bootstrap() {
const app = await bootstrapSetUp();
const swaggerDocument = addSwagger(app);

async function bootstrap() {
const app = await NestFactory.create(AppModule);

// We forcibly prevent the app from starting if no `API_AUTH_JWT_SECRET`
// environment variable has been set.
if (!AppConfig.get('auth.jwt.secret')) {
throw new Error(
'No secret configured for the signing of JWT tokens. Please set the `API_AUTH_JWT_SECRET` environment variable.',
);
}

app.use(helmet());
app.enableCors({
allowedHeaders: 'Content-Type,Authorization,Content-Disposition',
exposedHeaders: 'Authorization',
origin: CorsUtils.originHandler,
});

// OpenAPI documentation module - setup
const swaggerOptions = new DocumentBuilder()
.setTitle('MarxanCloud API')
.setDescription('MarxanCloud is a conservation planning platform.')
.setVersion(process.env.npm_package_version || 'development')
.addBearerAuth({
type: 'apiKey',
in: 'header',
name: 'Authorization',
})
.build();
const swaggerDocument = SwaggerModule.createDocument(app, swaggerOptions);
SwaggerModule.setup('/swagger', app, swaggerDocument);

app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);

await app.listen(3000);
}

bootstrap();
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class PropertyListForFeatures1619777176256
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
await queryRunner.query(`
ALTER TABLE features
DROP COLUMN list_property_keys;
`);
Expand Down
75 changes: 73 additions & 2 deletions api/apps/api/src/modules/admin-areas/admin-areas.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
import {
Controller,
Get,
Param,
Query,
Req,
Res,
UseGuards,
} from '@nestjs/common';
import { adminAreaResource, AdminAreaResult } from './admin-area.geo.entity';
import { AdminAreaLevel, AdminAreasService } from './admin-areas.service';
import {
Expand All @@ -18,13 +26,18 @@ import {
FetchSpecification,
ProcessFetchSpecification,
} from 'nestjs-base-service';
import { ProxyService } from '@marxan-api/modules/proxy/proxy.service';
import { Request, Response } from 'express';

@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiTags(adminAreaResource.className)
@Controller(`${apiGlobalPrefixes.v1}`)
export class AdminAreasController {
constructor(public readonly service: AdminAreasService) {}
constructor(
public readonly service: AdminAreasService,
private readonly proxyService: ProxyService,
) {}

@ApiOperation({
description: 'Find administrative areas within a given country.',
Expand Down Expand Up @@ -62,6 +75,64 @@ export class AdminAreasController {
return this.service.serialize(results.data, results.metadata);
}

@ApiOperation({
description:
'Find administrative areas within a given country in mvt format.',
})
/**
*@todo Change ApiOkResponse mvt type
*/
@ApiOkResponse({
description: 'Binary protobuffer mvt tile',
type: String,
})
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
@ApiParam({
name: 'z',
description: 'The zoom level ranging from 0 - 20',
type: Number,
required: true,
})
@ApiParam({
name: 'x',
description: 'The tile x offset on Mercator Projection',
type: Number,
required: true,
})
@ApiParam({
name: 'y',
description: 'The tile y offset on Mercator Projection',
type: Number,
required: true,
})
@ApiParam({
name: 'level',
description:
'Specific level to filter the administrative areas (0, 1 or 2)',
type: Number,
required: true,
example: '1',
})
@ApiQuery({
name: 'guid',
description: 'Parent country of administrative areas in guid code',
type: String,
required: false,
example: 'BRA.1_1',
})
@ApiQuery({
name: 'bbox',
description: 'Bounding box of the project [xMin, xMax, yMin, yMax]',
type: [Number],
required: false,
example: [-1, 40, 1, 42],
})
@Get('/administrative-areas/:level/preview/tiles/:z/:x/:y.mvt')
async proxyAdminAreaTile(@Req() request: Request, @Res() response: Response) {
return this.proxyService.proxyTileRequest(request, response);
}

@ApiOperation({
description: 'Find administrative areas that are children of a given one.',
})
Expand Down
3 changes: 2 additions & 1 deletion api/apps/api/src/modules/admin-areas/admin-areas.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { AdminArea } from '@marxan/admin-regions';
import { AdminAreasController } from './admin-areas.controller';
import { AdminAreasService } from './admin-areas.service';
import { apiConnections } from '../../ormconfig';
import { ProxyService } from '@marxan-api/modules/proxy/proxy.service';

@Module({
imports: [
TypeOrmModule.forFeature([AdminArea], apiConnections.geoprocessingDB.name),
],
providers: [AdminAreasService],
providers: [AdminAreasService, ProxyService],
controllers: [AdminAreasController],
exports: [AdminAreasService],
})
Expand Down
59 changes: 57 additions & 2 deletions api/apps/api/src/modules/geo-features/geo-features.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
import { Controller, Get, Param, Req, Res, UseGuards } from '@nestjs/common';
import { geoFeatureResource, GeoFeatureResult } from './geo-feature.geo.entity';
import { GeoFeaturesService } from './geo-features.service';
import {
ApiBearerAuth,
ApiForbiddenResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiQuery,
ApiTags,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
Expand All @@ -19,6 +21,8 @@ import {
FetchSpecification,
ProcessFetchSpecification,
} from 'nestjs-base-service';
import { Request, Response } from 'express';
import { ProxyService } from '@marxan-api/modules/proxy/proxy.service';

@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
Expand All @@ -27,7 +31,10 @@ import {
`${apiGlobalPrefixes.v1}/${geoFeatureResource.moduleControllerPrefix}`,
)
export class GeoFeaturesController {
constructor(public readonly service: GeoFeaturesService) {}
constructor(
public readonly service: GeoFeaturesService,
private readonly proxyService: ProxyService,
) {}

@ApiOperation({
description: 'Find all geo features',
Expand All @@ -46,6 +53,54 @@ export class GeoFeaturesController {
return this.service.serialize(results.data, results.metadata);
}

@ApiOperation({
description: 'Get tile for a feature by id.',
})
/**
*@todo Change ApiOkResponse mvt type
*/
@ApiOkResponse({
description: 'Binary protobuffer mvt tile',
type: String,
})
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
@ApiParam({
name: 'z',
description: 'The zoom level ranging from 0 - 20',
type: Number,
required: true,
})
@ApiParam({
name: 'x',
description: 'The tile x offset on Mercator Projection',
type: Number,
required: true,
})
@ApiParam({
name: 'y',
description: 'The tile y offset on Mercator Projection',
type: Number,
required: true,
})
@ApiParam({
name: 'id',
description: 'Specific id of the feature',
type: String,
required: true,
})
@ApiQuery({
name: 'bbox',
description: 'Bounding box of the project [xMin, xMax, yMin, yMax]',
type: [Number],
required: false,
example: [-1, 40, 1, 42],
})
@Get(':id/preview/tiles/:z/:x/:y.mvt')
async proxyFeaturesTile(@Req() request: Request, @Res() response: Response) {
return this.proxyService.proxyTileRequest(request, response);
}

@ApiOperation({ description: 'Find geo feature by id' })
@ApiOkResponse({ type: GeoFeatureResult })
@JSONAPISingleEntityQueryParams()
Expand Down
3 changes: 2 additions & 1 deletion api/apps/api/src/modules/geo-features/geo-features.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { GeoFeaturesController } from './geo-features.controller';
import { GeoFeaturesService } from './geo-features.service';
import { apiConnections } from '../../ormconfig';
import { ProxyService } from '@marxan-api/modules/proxy/proxy.service';

@Module({
imports: [
Expand All @@ -19,7 +20,7 @@ import { apiConnections } from '../../ormconfig';
),
TypeOrmModule.forFeature([GeoFeature, Project]),
],
providers: [GeoFeaturesService],
providers: [GeoFeaturesService, ProxyService],
controllers: [GeoFeaturesController],
exports: [GeoFeaturesService],
})
Expand Down
Loading

0 comments on commit fdaa7d5

Please sign in to comment.