Skip to content

Commit

Permalink
fix(api): keep only current specification data in the table for proce…
Browse files Browse the repository at this point in the history
…ssing
  • Loading branch information
Dyostiq committed Aug 26, 2021
1 parent 186bc51 commit 7cde07c
Show file tree
Hide file tree
Showing 22 changed files with 350 additions and 167 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { API_EVENT_KINDS } from '@marxan/api-events';
import { ApiEventsService } from '@marxan-api/modules/api-events/api-events.service';
import { SpecificationApiEntity } from '@marxan-api/modules/specification/adapters/specification.api.entity';
import { SpecificationActivated } from '../domain';
import { SpecificationProcessingFinishedEvent } from './specification-processing-finished.event';

@EventsHandler(SpecificationActivated)
@EventsHandler(SpecificationProcessingFinishedEvent)
export class SpecificationActivatedHandler
implements IEventHandler<SpecificationActivated> {
constructor(
private readonly apiEvents: ApiEventsService,
@InjectRepository(SpecificationApiEntity)
private readonly specifications: Repository<SpecificationApiEntity>,
) {}
implements IEventHandler<SpecificationProcessingFinishedEvent> {
constructor(private readonly apiEvents: ApiEventsService) {}

async handle(event: SpecificationActivated) {
const specification = await this.specifications.findOne(
event.specificationId.value,
);
if (!specification) return;
async handle(event: SpecificationProcessingFinishedEvent) {
await this.apiEvents.create({
kind: API_EVENT_KINDS.scenario__specification__finished__v1__alpha1,
topic: specification.scenarioId,
topic: event.scenarioId,
data: { ...event },
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IEvent } from '@nestjs/cqrs';

export class SpecificationProcessingFinishedEvent implements IEvent {
constructor(
public readonly scenarioId: string,
public readonly specificationId: string,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const getFixtures = async () => {
},
ThenSpecificationIsActivated: () => {
expect(events).toEqual([
new SpecificationActivated(currentCandidateSpecificationId),
new SpecificationActivated(scenarioId, currentCandidateSpecificationId),
]);
},
ThenSpecificationIsNotActivated: async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const getFixtures = () => {
expect(result.right).toEqual(void 0);
expect(scenarioSpecification.getUncommittedEvents()).toEqual([
new SpecificationActivated(
scenarioSpecification.scenarioId,
scenarioSpecification.currentActiveSpecification!,
),
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ import { IEvent } from '@nestjs/cqrs';
import { SpecificationId } from '../specification.id';

export class SpecificationActivated implements IEvent {
constructor(public readonly specificationId: SpecificationId) {}
constructor(
public readonly scenarioId: string,
public readonly specificationId: SpecificationId,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class ScenarioSpecification extends AggregateRoot {

this.#active = this.#candidate;
this.#candidate = undefined;
this.apply(new SpecificationActivated(this.#active));
this.apply(new SpecificationActivated(this.scenarioId, this.#active));
return right(void 0);
}

Expand Down
2 changes: 2 additions & 0 deletions api/apps/api/src/modules/scenario-specification/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { ScenarioSpecificationModule } from './scenario-specification.module';
export { SpecificationActivated } from './domain';
export { SpecificationProcessingFinishedEvent } from './adapters/specification-processing-finished.event';
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class CopyOperation {

async copy(data: {
scenarioId: string;
specificationId: string;
input: FeatureConfigCopy;
}): Promise<{ id: string }[]> {
await this.events.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import { FeatureConfigCopy } from '@marxan-api/modules/specification';
@Injectable()
export class CopyQuery {
public prepareStatement(
command: { scenarioId: string; input: FeatureConfigCopy },
command: {
scenarioId: string;
specificationId: string;
input: FeatureConfigCopy;
},
planningAreaLocation: { id: string; tableName: string } | undefined,
protectedAreaFilterByIds: string[],
project: Pick<Project, 'bbox'>,
): { query: string; parameters: (string | number)[] } {
const parameters: (string | number)[] = [];
const fields = {
scenarioId: `$${parameters.push(command.scenarioId)}`,
specificationId: `$${parameters.push(command.specificationId)}`,
fpf: isDefined(command.input.fpf)
? `$${parameters.push(command.input.fpf)}`
: `NULL`,
Expand Down Expand Up @@ -63,15 +68,17 @@ export class CopyQuery {
? `left join ${planningAreaLocation.tableName} as pa on pa.id = ${fields.planningAreaId}`
: ``;
const query = `
insert into scenario_features_data as sfd (feature_class_id,
scenario_id,
fpf,
target,
prop,
total_area,
current_pa)
insert into scenario_features_preparation as sfp (feature_class_id,
scenario_id,
specification_id,
fpf,
target,
prop,
total_area,
current_pa)
select fd.id,
${fields.scenarioId},
${fields.specificationId},
${fields.fpf},
${fields.target},
${fields.prop},
Expand All @@ -88,7 +95,7 @@ export class CopyQuery {
${fields.bbox[3]},
4326
), fd.the_geom)
returning sfd.id as id;
returning sfp.id as id;
`;
return { parameters, query };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class CreateFeaturesHandler
case SpecificationOperation.Copy: {
const ids = await this.copyOperation.copy({
scenarioId: command.scenarioId,
specificationId: command.specificationId,
input: command.input,
});
this.eventBus.publish(
Expand All @@ -43,7 +44,8 @@ export class CreateFeaturesHandler
}
case SpecificationOperation.Split: {
const ids = await this.splitOperation.split({
...command,
scenarioId: command.scenarioId,
specificationId: command.specificationId,
input: command.input,
});
this.eventBus.publish(
Expand Down
1 change: 1 addition & 0 deletions api/apps/api/src/modules/scenarios-features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { ScenarioFeaturesModule } from './scenario-features.module';
export { ScenarioFeaturesService } from './scenario-features.service';
export { ScenariosFeaturesView } from './scenario-features.dto';
export { FeaturesCreated } from './features-created.event';
export { SpecificationProcessingFinishedEvent } from '@marxan-api/modules/scenario-specification/adapters/specification-processing-finished.event';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Command } from '@nestjs-architects/typed-cqrs';

export class MoveDataFromPreparationCommand extends Command<void> {
constructor(
public readonly scenarioId: string,
public readonly specificationId: string,
) {
super();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
CommandHandler,
EventBus,
IInferredCommandHandler,
} from '@nestjs/cqrs';
import { EntityManager } from 'typeorm';
import { InjectEntityManager } from '@nestjs/typeorm';
import {
ScenarioFeaturesPreparation,
ScenarioFeaturesData,
} from '@marxan/features';
import { DbConnections } from '@marxan-api/ormconfig.connections';
import { SpecificationProcessingFinishedEvent } from '@marxan-api/modules/scenario-specification';
import { MoveDataFromPreparationCommand } from './move-data-from-preparation.command';

@CommandHandler(MoveDataFromPreparationCommand)
export class MoveDataFromPreparationHandler
implements IInferredCommandHandler<MoveDataFromPreparationCommand> {
constructor(
@InjectEntityManager(DbConnections.geoprocessingDB)
private readonly entityManager: EntityManager,
private readonly eventBus: EventBus,
) {}

async execute(command: MoveDataFromPreparationCommand): Promise<void> {
await this.entityManager.transaction(async (transactionalEntityManager) => {
await transactionalEntityManager.delete(ScenarioFeaturesData, {
scenarioId: command.scenarioId,
});
await transactionalEntityManager.query(
`
insert into scenario_features_data as sfd (id,
feature_class_id,
scenario_id,
total_area,
current_pa,
fpf,
target,
prop,
target2,
targetocc,
sepnum,
created_by,
metadata,
specification_id)
select id,
feature_class_id,
scenario_id,
total_area,
current_pa,
fpf,
target,
prop,
target2,
targetocc,
sepnum,
created_by,
metadata,
specification_id
from scenario_features_preparation sfp
where sfp.specification_id = $1
`,
[command.specificationId],
);
await transactionalEntityManager.delete(ScenarioFeaturesPreparation, {
specificationId: command.specificationId,
});
});

this.eventBus.publish(
new SpecificationProcessingFinishedEvent(
command.scenarioId,
command.specificationId,
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ICommand, ofType, Saga } from '@nestjs/cqrs';
import { Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SpecificationActivated } from '@marxan-api/modules/scenario-specification';
import { MoveDataFromPreparationCommand } from './move-data-from-preparation.command';

@Injectable()
export class MoveDataFromPreparationSaga {
@Saga()
specificationActivated = (events$: Observable<any>): Observable<ICommand> => {
return events$.pipe(
ofType(SpecificationActivated),
map(
(event) =>
new MoveDataFromPreparationCommand(
event.scenarioId,
event.specificationId.value,
),
),
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { CopyDataProvider, CopyOperation, CopyQuery } from './copy';
import { SplitDataProvider, SplitOperation, SplitQuery } from './split';
import { ScenarioFeaturesGapDataService } from './scenario-features-gap-data.service';
import { ScenarioFeaturesOutputGapDataService } from './scenario-features-output-gap-data.service';
import { MoveDataFromPreparationSaga } from './move-data-from-preparation.saga';
import { MoveDataFromPreparationHandler } from './move-data-from-preparation.handler';

@Module({
imports: [
Expand Down Expand Up @@ -49,6 +51,8 @@ import { ScenarioFeaturesOutputGapDataService } from './scenario-features-output
SplitQuery,
SplitDataProvider,
SplitOperation,
MoveDataFromPreparationSaga,
MoveDataFromPreparationHandler,
],
exports: [
ScenarioFeaturesService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ export class SplitOperation {
private readonly events: ApiEventsService,
) {}

async split(data: { scenarioId: string; input: FeatureConfigSplit }) {
async split(data: {
scenarioId: string;
specificationId: string;
input: FeatureConfigSplit;
}) {
await this.events.create({
topic: data.scenarioId,
kind: API_EVENT_KINDS.scenario__geofeatureSplit__submitted__v1__alpha1,
Expand All @@ -36,6 +40,7 @@ export class SplitOperation {
const { parameters, query } = this.splitQuery.prepareQuery(
data.input,
data.scenarioId,
data.specificationId,
planningAreaLocation,
protectedAreaFilterByIds,
project,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class SplitQuery {
prepareQuery(
input: FeatureConfigSplit,
scenarioId: string,
specificationId: string,
planningAreaLocation: { id: string; tableName: string } | undefined,
protectedAreaFilterByIds: string[],
project: Pick<Project, 'bbox'>,
Expand All @@ -19,6 +20,7 @@ export class SplitQuery {
),
splitByProperty: `$${parameters.push(input.splitByProperty)}`,
scenarioId: `$${parameters.push(scenarioId)}`,
specificationId: `$${parameters.push(specificationId)}`,
planningAreaId: isDefined(planningAreaLocation)
? `$${parameters.push(planningAreaLocation.id)}`
: `NULL`,
Expand Down Expand Up @@ -53,13 +55,14 @@ export class SplitQuery {

const hasSubSetFilter = (input.selectSubSets ?? []).length > 0;
const query = `
insert into scenario_features_data as sfd (feature_class_id,
scenario_id,
fpf,
target,
prop,
total_area,
current_pa)
insert into scenario_features_preparation as sfp (feature_class_id,
scenario_id,
specification_id,
fpf,
target,
prop,
total_area,
current_pa)
WITH split as (
WITH subsets as (
select value as sub_value, target, fpf, prop
Expand All @@ -86,6 +89,7 @@ export class SplitQuery {
)
select fd.id,
${fields.scenarioId},
${fields.specificationId},
split.fpf,
split.target,
split.prop,
Expand All @@ -103,7 +107,7 @@ export class SplitQuery {
${fields.bbox[3]},
4326
), fd.the_geom)
returning sfd.id as id;
returning sfp.id as id;
`;
return { parameters, query };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddSpecificationIdToScenarioFeaturesData1629831194337
implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE scenario_features_data ADD COLUMN specification_id uuid`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE scenario_features_data DROP COLUMN specification_id;
`);
}
}
Loading

0 comments on commit 7cde07c

Please sign in to comment.