Skip to content

Commit

Permalink
Merge pull request #981 from Vizzuality/feat/MARXAN-1466-concatenate-…
Browse files Browse the repository at this point in the history
…export-and-import-operations

feat: connect export and import in case is a clonning proces using ar…
  • Loading branch information
angelhigueraacid authored Apr 12, 2022
2 parents db0a1f5 + ad8582b commit e9b1730
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArchiveLocation } from '@marxan/cloning/domain';
import { ArchiveLocation, ResourceId } from '@marxan/cloning/domain';
import { Failure as ArchiveReadError } from '@marxan/cloning/infrastructure/archive-reader.port';
import { UserId } from '@marxan/domain-ids';
import { Command } from '@nestjs-architects/typed-cqrs';
Expand All @@ -18,6 +18,7 @@ export class ImportProject extends Command<
constructor(
public readonly archiveLocation: ArchiveLocation,
public readonly ownerId: UserId,
public readonly importResourceId?: ResourceId,
) {
super();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class ImportProjectHandler
async execute({
archiveLocation,
ownerId,
importResourceId,
}: ImportProject): Promise<
Either<ImportProjectError, ImportProjectCommandResult>
> {
Expand All @@ -38,8 +39,10 @@ export class ImportProjectHandler
if (isLeft(exportConfigOrError)) return exportConfigOrError;

const exportConfig = exportConfigOrError.right as ProjectExportConfigContent;
const importResourceId = ResourceId.create();
const projectId = importResourceId;

const resourceId = importResourceId ?? ResourceId.create();

const projectId = resourceId;

const pieces = this.importResourcePieces.resolveForProject(
projectId,
Expand All @@ -49,7 +52,7 @@ export class ImportProjectHandler

const importRequest = this.eventPublisher.mergeObjectContext(
Import.newOne(
importResourceId,
resourceId,
ResourceKind.Project,
projectId,
ownerId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArchiveLocation } from '@marxan/cloning/domain';
import { ArchiveLocation, ResourceId } from '@marxan/cloning/domain';
import { Failure as ArchiveReadError } from '@marxan/cloning/infrastructure/archive-reader.port';
import { UserId } from '@marxan/domain-ids';
import { Command } from '@nestjs-architects/typed-cqrs';
Expand All @@ -18,6 +18,7 @@ export class ImportScenario extends Command<
constructor(
public readonly archiveLocation: ArchiveLocation,
public readonly ownerId: UserId,
public readonly importResourceId: ResourceId,
) {
super();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ const getFixtures = async () => {
}).compile();
await sandbox.init();

let resourceId: ResourceId;
const ownerId = UserId.create();

const events: IEvent[] = [];
Expand All @@ -99,6 +98,7 @@ const getFixtures = async () => {
const importResourcePieces: FakeImportResourcePieces = sandbox.get(
ImportResourcePieces,
);
const importResourceId = ResourceId.create();

return {
GivenExtractingArchiveFails: () => {
Expand All @@ -113,14 +113,13 @@ const getFixtures = async () => {
importResourcePieces.mockEqualPieces();
},
WhenRequestingImport: async () => {
const importResult = await sut.execute(
new ImportScenario(new ArchiveLocation(`whatever`), ownerId),
return sut.execute(
new ImportScenario(
new ArchiveLocation(`whatever`),
ownerId,
importResourceId,
),
);
if (isRight(importResult))
resourceId = new ResourceId(
repo.entities[importResult.right.importId].resourceId,
);
return importResult;
},
ThenRequestImportIsSaved: (
importResult: PromiseType<ReturnType<ImportScenarioHandler['execute']>>,
Expand All @@ -131,6 +130,16 @@ const getFixtures = async () => {
(importResult as Right<ImportScenarioCommandResult>).right.importId
],
).toBeDefined();
expect(
repo.entities[
(importResult as Right<ImportScenarioCommandResult>).right.importId
].resourceId,
).toEqual(importResourceId.value);
expect(
repo.entities[
(importResult as Right<ImportScenarioCommandResult>).right.importId
].ownerId,
).toEqual(ownerId.value);
},
ThenImportFails: (
importResult: PromiseType<ReturnType<ImportScenarioHandler['execute']>>,
Expand All @@ -143,7 +152,7 @@ const getFixtures = async () => {
).toEqual([
{
importId: expect.any(ImportId),
resourceId,
resourceId: importResourceId,
resourceKind: ResourceKind.Scenario,
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class ImportScenarioHandler
async execute({
archiveLocation,
ownerId,
importResourceId,
}: ImportScenario): Promise<
Either<ImportScenarioError, ImportScenarioCommandResult>
> {
Expand All @@ -38,7 +39,6 @@ export class ImportScenarioHandler
if (isLeft(exportConfigOrError)) return exportConfigOrError;

const exportConfig = exportConfigOrError.right as ScenarioExportConfigContent;
const importResourceId = ResourceId.create();

const pieces = this.importResourcePieces.resolveForScenario(
importResourceId,
Expand Down
81 changes: 76 additions & 5 deletions api/apps/api/src/modules/clone/infra/export/archive-ready.saga.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,87 @@
import {
ArchiveLocation,
ResourceId,
ResourceKind,
} from '@marxan/cloning/domain';
import { UserId } from '@marxan/domain-ids';
import { Injectable } from '@nestjs/common';
import { ICommand, ofType, Saga } from '@nestjs/cqrs';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { from, Observable } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { ExportRepository } from '../../export/application/export-repository.port';
import { ArchiveReady } from '../../export/domain';
import { ImportProject } from '../../import/application/import-project.command';
import { ImportScenario } from '../../import/application/import-scenario.command';
import { MarkExportAsFinished } from './mark-export-as-finished.command';

type CommandMapper = (
archiveLocation: string,
ownerId: string,
importResourceId: string,
) => ICommand;

@Injectable()
export class ArchiveReadySaga {
constructor(private readonly exportRepository: ExportRepository) {}

private commandMapper: Record<ResourceKind, CommandMapper> = {
project: (
archiveLocation: string,
ownerId: string,
importResourceId: string,
) =>
new ImportProject(
new ArchiveLocation(archiveLocation),
new UserId(ownerId),
new ResourceId(importResourceId),
),
scenario: (
archiveLocation: string,
ownerId: string,
importResourceId: string,
) =>
new ImportScenario(
new ArchiveLocation(archiveLocation),
new UserId(ownerId),
new ResourceId(importResourceId),
),
};

private async getCommands(event: ArchiveReady) {
const exportInstance = await this.exportRepository.find(event.exportId);

if (!exportInstance) throw new Error('cant find export');

const {
resourceKind,
archiveLocation,
importResourceId,
ownerId,
} = exportInstance.toSnapshot();

if (!exportInstance.isClonning())
return [new MarkExportAsFinished(event.exportId)];

if (!archiveLocation || !importResourceId)
throw new Error(
'When clonning, the archiveLocation and importResourceId should be ready',
);

const importCommand = this.commandMapper[resourceKind](
archiveLocation,
ownerId,
importResourceId,
);

return [new MarkExportAsFinished(event.exportId), importCommand];
}

@Saga()
emitApiEvents = (events$: Observable<any>): Observable<ICommand> =>
events$.pipe(
emitApiEvents = (events$: Observable<any>) => {
return events$.pipe(
ofType(ArchiveReady),
map((event) => new MarkExportAsFinished(event.exportId)),
mergeMap((event) => from(this.getCommands(event))),
mergeMap((commands) => from(commands)),
);
};
}
3 changes: 2 additions & 1 deletion api/apps/api/src/modules/projects/projects.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ export class ProjectsController {
projectId,
req.user.id,
dto.scenarioIds,
false,
);

if (isLeft(result)) {
Expand All @@ -624,7 +625,7 @@ export class ProjectsController {
}
}
return {
id: result.right,
id: result.right.exportId.value,
};
}

Expand Down
17 changes: 12 additions & 5 deletions api/apps/api/src/modules/projects/projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
} from '../clone/import/application/import-project.command';
import { PlanningUnitGridShape } from '@marxan/scenarios-planning-unit';
import { UserId } from '@marxan/domain-ids';
import { ExportProjectCommandResult } from '../clone/export/application/export-project.command';

export { validationFailed } from '../planning-areas';

Expand Down Expand Up @@ -341,7 +342,13 @@ export class ProjectsService {
projectId: string,
userId: string,
scenarioIds: string[],
): Promise<Either<typeof forbiddenError | typeof projectNotFound, string>> {
clonning: boolean,
): Promise<
Either<
typeof forbiddenError | typeof projectNotFound,
ExportProjectCommandResult
>
> {
await this.blockGuard.ensureThatProjectIsNotBlocked(projectId);

const canExportProject = await this.projectAclService.canExportProject(
Expand All @@ -350,17 +357,17 @@ export class ProjectsService {
);

if (!canExportProject) return left(forbiddenError);
//TODO add clonning logic and return value
const { exportId, importResourceId } = await this.commandBus.execute(

const res = await this.commandBus.execute(
new ExportProject(
new ResourceId(projectId),
scenarioIds,
new UserId(userId),
false,
clonning,
),
);

return right(exportId.value);
return right(res);
}

async remove(
Expand Down

0 comments on commit e9b1730

Please sign in to comment.