Skip to content

Commit

Permalink
feat(api): pass assets to marxan run job
Browse files Browse the repository at this point in the history
  • Loading branch information
Dyostiq committed Jul 14, 2021
1 parent 966a1f1 commit 929bf3a
Show file tree
Hide file tree
Showing 23 changed files with 347 additions and 93 deletions.
3 changes: 3 additions & 0 deletions api/apps/api/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
"host": "REDIS_HOST"
}
},
"api": {
"url": "API_SERVICE_URL"
},
"storage": {
"sharedFileStorage": {
"localPath": "API_SHARED_FILE_STORAGE_LOCAL_PATH"
Expand Down
3 changes: 3 additions & 0 deletions api/apps/api/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
},
"concurrency": 50
},
"api": {
"url": "http://api:3000"
},
"jobOptions":{
"removeOnComplete": 100,
"removeOnFail": 1000,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { PromiseType } from 'utility-types';
import { Test } from '@nestjs/testing';
import { apiUrlToken, AssetsService } from './assets.service';
import { IoSettings, ioSettingsToken } from './io-settings';

let fixtures: PromiseType<ReturnType<typeof getFixtures>>;
let service: AssetsService;

beforeEach(async () => {
fixtures = await getFixtures();

service = fixtures.getAssetsService();
});

it(`should return valid config`, async () => {
const random = fixtures.random;
expect(await service.forScenario(`scenario-${random}`)).toStrictEqual([
{
url: `https://api-endpoint${random}.test:3000/api/v1/marxan-run/scenarios/scenario-${random}/marxan/dat/input.dat`,
relativeDestination: `input.dat`,
},
{
url: `https://api-endpoint${random}.test:3000/api/v1/marxan-run/scenarios/scenario-${random}/marxan/dat/pu.dat`,
relativeDestination: `inputDir${random}/pu-name-file${random}`,
},
{
url: `https://api-endpoint${random}.test:3000/api/v1/marxan-run/scenarios/scenario-${random}/marxan/dat/bound.dat`,
relativeDestination: `inputDir${random}/bound-name-file${random}`,
},
{
url: `https://api-endpoint${random}.test:3000/api/v1/marxan-run/scenarios/scenario-${random}/marxan/dat/spec.dat`,
relativeDestination: `inputDir${random}/spec-name-file${random}`,
},
{
url: `https://api-endpoint${random}.test:3000/api/v1/marxan-run/scenarios/scenario-${random}/marxan/dat/puvspr.dat`,
relativeDestination: `inputDir${random}/puv-spr-name-file${random}`,
},
]);
});

async function getFixtures() {
const random = Math.random().toString(36);
const ioSettings: IoSettings = {
BOUNDNAME: `bound-name-file${random}`,
INPUTDIR: `inputDir${random}`,
OUTPUTDIR: `OUTPUTDIR.file${random}`,
PUNAME: `pu-name-file${random}`,
PUVSPRNAME: `puv-spr-name-file${random}`,
SPECNAME: `spec-name-file${random}`,
};
const testingModule = await Test.createTestingModule({
providers: [
{
provide: ioSettingsToken,
useValue: ioSettings,
},
{
provide: apiUrlToken,
useValue: `https://api-endpoint${random}.test:3000`,
},
AssetsService,
],
}).compile();
return {
random,
getAssetsService() {
return testingModule.get(AssetsService);
},
};
}
45 changes: 45 additions & 0 deletions api/apps/api/src/modules/scenarios/marxan-run/assets.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Assets } from '@marxan/scenario-run-queue';
import { FactoryProvider, Inject, Injectable } from '@nestjs/common';
import { AppConfig } from '@marxan-api/utils/config.utils';
import { IoSettings, ioSettingsToken } from './io-settings';

export const apiUrlToken = Symbol('api url token');
export const apiUrlProvider: FactoryProvider<string> = {
provide: apiUrlToken,
useFactory: () => AppConfig.get<string>('api.url'),
};

@Injectable()
export class AssetsService {
constructor(
@Inject(ioSettingsToken)
private readonly settings: IoSettings,
@Inject(apiUrlToken)
private readonly apiUrlToken: string,
) {}

async forScenario(id: string): Promise<Assets> {
return [
{
url: `${this.apiUrlToken}/api/v1/marxan-run/scenarios/${id}/marxan/dat/input.dat`,
relativeDestination: 'input.dat',
},
{
url: `${this.apiUrlToken}/api/v1/marxan-run/scenarios/${id}/marxan/dat/pu.dat`,
relativeDestination: `${this.settings.INPUTDIR}/${this.settings.PUNAME}`,
},
{
url: `${this.apiUrlToken}/api/v1/marxan-run/scenarios/${id}/marxan/dat/bound.dat`,
relativeDestination: `${this.settings.INPUTDIR}/${this.settings.BOUNDNAME}`,
},
{
url: `${this.apiUrlToken}/api/v1/marxan-run/scenarios/${id}/marxan/dat/spec.dat`,
relativeDestination: `${this.settings.INPUTDIR}/${this.settings.SPECNAME}`,
},
{
url: `${this.apiUrlToken}/api/v1/marxan-run/scenarios/${id}/marxan/dat/puvspr.dat`,
relativeDestination: `${this.settings.INPUTDIR}/${this.settings.PUVSPRNAME}`,
},
];
}
}
9 changes: 9 additions & 0 deletions api/apps/api/src/modules/scenarios/marxan-run/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {
RunService,
notFound,
NotFound,
runQueueProvider,
runQueueEventsProvider,
} from './run.service';
export { InputParameterFileProvider } from './input-parameter-file.provider';
export { MarxanRunModule } from './marxan-run.module';
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { PromiseType } from 'utility-types';
import { Test } from '@nestjs/testing';
import {
InputParameterFileProvider,
ioSettingsToken,
} from './input-parameter-file.provider';
import { JobStatus, Scenario, ScenarioType } from './scenario.api.entity';
import { ScenariosCrudService } from './scenarios-crud.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JobStatus, Scenario, ScenarioType } from '../scenario.api.entity';
import { InputParameterFileProvider } from './input-parameter-file.provider';
import { ioSettingsToken } from './io-settings';

jest.useFakeTimers('modern').setSystemTime(new Date('2020-01-01').getTime());

Expand Down Expand Up @@ -119,10 +118,16 @@ OUTPUTDIR output`);
});

async function getFixtures() {
class FakeScenario implements Pick<ScenariosCrudService, 'getById'> {
class FakeScenario implements Pick<Repository<Scenario>, 'findOne'> {
db: Record<string, Scenario> = {};

async getById(scenarioId: string): Promise<Scenario> {
async findOne(scenarioId: any, ...rest: any[]): Promise<Scenario> {
expect(rest).toStrictEqual([
{
relations: ['project', 'project.organization'],
},
]);
if (typeof scenarioId !== 'string') fail();
return this.db[scenarioId];
}
}
Expand All @@ -131,9 +136,10 @@ async function getFixtures() {
imports: [],
providers: [
InputParameterFileProvider,
FakeScenario,
{
provide: ScenariosCrudService,
useClass: FakeScenario,
provide: getRepositoryToken(Scenario),
useExisting: FakeScenario,
},
{
provide: ioSettingsToken,
Expand All @@ -148,7 +154,7 @@ async function getFixtures() {
},
],
}).compile();
const fakeRepo: FakeScenario = testingModule.get(ScenariosCrudService);
const fakeRepo: FakeScenario = testingModule.get(FakeScenario);

return {
async hasInDb(scenario: Scenario) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { Inject, Injectable } from '@nestjs/common';
import { omit, pick } from 'lodash';
import { isDefined } from '@marxan/utils';
import { ScenariosCrudService } from './scenarios-crud.service';

export const ioSettingsToken = Symbol('Marxan IO settings token');

export interface IoSettings {
INPUTDIR: string;
PUNAME: string;
SPECNAME: string;
PUVSPRNAME: string;
BOUNDNAME: string;
OUTPUTDIR: string;
}
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { assertDefined, isDefined } from '@marxan/utils';
import { Scenario } from '../scenario.api.entity';
import { IoSettings, ioSettingsToken } from './io-settings';

class InputParameterFile {
private ioSettingsKeys: (keyof IoSettings)[] = [
Expand Down Expand Up @@ -78,15 +70,17 @@ class InputParameterFile {
@Injectable()
export class InputParameterFileProvider {
constructor(
private readonly scenariosService: ScenariosCrudService,
@InjectRepository(Scenario)
private readonly scenarioRepository: Repository<Scenario>,
@Inject(ioSettingsToken)
private readonly ioSettings: IoSettings,
) {}

async getInputParameterFile(scenarioId: string): Promise<string> {
const scenario = await this.scenariosService.getById(scenarioId, {
include: ['project', 'project.organization'],
const scenario = await this.scenarioRepository.findOne(scenarioId, {
relations: ['project', 'project.organization'],
});
assertDefined(scenario);
const inputParameterFile = new InputParameterFile(
this.ioSettings,
scenario.boundaryLengthModifier,
Expand Down
24 changes: 24 additions & 0 deletions api/apps/api/src/modules/scenarios/marxan-run/io-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { AppConfig } from '@marxan-api/utils/config.utils';
import { assertDefined } from '@marxan/utils';

export const ioSettingsToken = Symbol('Marxan IO settings token');

export interface IoSettings {
INPUTDIR: string;
PUNAME: string;
SPECNAME: string;
PUVSPRNAME: string;
BOUNDNAME: string;
OUTPUTDIR: string;
}

export const ioSettingsProvider = {
provide: ioSettingsToken,
useFactory: () => {
const config = AppConfig.get<IoSettings>(
'marxan.inputFiles.inputDat.ioSettings',
);
assertDefined(config);
return config;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Injectable } from '@nestjs/common';
import * as stream from 'stream';

import { CostSurfaceViewService } from '../cost-surface-readmodel/cost-surface-view.service';
import { InputParameterFileProvider } from '../input-parameter-file.provider';
import { PuvsprDatService } from '../input-files/puvspr.dat.service';
import { BoundDatService } from '../input-files/bound.dat.service';
import { InputParameterFileProvider } from './input-parameter-file.provider';

@Injectable()
export class MarxanRunService {
export class MarxanFilesService {
constructor(
private readonly costSurfaceService: CostSurfaceViewService,
private readonly inputParameterFileProvider: InputParameterFileProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import {
import { Response } from 'express';
import { apiGlobalPrefixes } from '@marxan-api/api.config';
import { XApiGuard } from '@marxan-api/guards/x-api.guard';
import { MarxanRunService } from './marxan-run.service';
import { MarxanFilesService } from './marxan-files.service';

@UseGuards(XApiGuard)
@Controller(`${apiGlobalPrefixes.v1}/marxan-run/scenarios`)
export class MarxanRunController {
constructor(private readonly service: MarxanRunService) {}
constructor(private readonly service: MarxanFilesService) {}

@Header('Content-Type', 'text/csv')
@ApiOkResponse({
Expand Down
44 changes: 44 additions & 0 deletions api/apps/api/src/modules/scenarios/marxan-run/marxan-run.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Module } from '@nestjs/common';
import { MarxanRunController } from './marxan-run.controller';
import { MarxanFilesService } from './marxan-files.service';
import {
runQueueEventsProvider,
runQueueProvider,
RunService,
} from './run.service';
import { apiUrlProvider, AssetsService } from './assets.service';
import { ioSettingsProvider } from './io-settings';
import { InputParameterFileProvider } from './input-parameter-file.provider';
import { QueueModule } from '@marxan-api/modules/queue';
import { ApiEventsModule } from '@marxan-api/modules/api-events/api-events.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Scenario } from '../scenario.api.entity';
import { CostSurfaceViewModule } from '../cost-surface-readmodel/cost-surface-view.module';
import { SpecDatModule } from '@marxan-api/modules/scenarios/input-files/spec.dat.module';
import { PuvsprDatModule } from '@marxan-api/modules/scenarios/input-files/puvspr.dat.module';
import { BoundDatModule } from '@marxan-api/modules/scenarios/input-files/bound.dat.module';

@Module({
imports: [
QueueModule.register(),
ApiEventsModule,
TypeOrmModule.forFeature([Scenario]),
CostSurfaceViewModule,
SpecDatModule,
PuvsprDatModule,
BoundDatModule,
],
providers: [
MarxanFilesService,
runQueueProvider,
runQueueEventsProvider,
RunService,
AssetsService,
ioSettingsProvider,
apiUrlProvider,
InputParameterFileProvider,
],
controllers: [MarxanRunController],
exports: [RunService, InputParameterFileProvider],
})
export class MarxanRunModule {}
Loading

0 comments on commit 929bf3a

Please sign in to comment.