Skip to content

Commit

Permalink
Feat/archive reader adapter (#824)
Browse files Browse the repository at this point in the history
* feat: fake archive reader adapter

* feat: initial archive reader implementation

* fix: typo and error handling

* feat: added clone pieces data

* ref: clone pieces relative paths

* feat: import resource pieces

* fix: export resource pieces adapter

* fix: export resource pieces adapter

* fix: removes testing controller

* ref: zip file extractor ResourceId constructor validation

* fix: ResourceKind validation in archive-reader adapter

* fix: import resource pieces adapter

* fix: wrong import

Co-authored-by: Ángel Higuera Vaquero <angelhiguera@acidtango.com>
  • Loading branch information
2 people authored and rubvalave committed Feb 16, 2022
1 parent 789c84d commit 9233caf
Show file tree
Hide file tree
Showing 34 changed files with 583 additions and 286 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { DiscoveryModule } from '@golevelup/nestjs-discovery';
import { FileRepositoryModule } from '@marxan/files-repository';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Scenario } from '../../../scenarios/scenario.api.entity';
import { ArchiveCreator } from '../application/archive-creator.port';
import { ExportRepository } from '../application/export-repository.port';
import { ResourcePieces } from '../application/resource-pieces.port';
import { ExportResourcePieces } from '../application/export-resource-pieces.port';
import { ExportComponentLocationEntity } from './entities/export-component-locations.api.entity';
import { ExportComponentEntity } from './entities/export-components.api.entity';
import { ExportEntity } from './entities/exports.api.entity';
import { ExportResourcePiecesAdapter } from './export-resource-pieces.adapter';
import { NodeArchiveCreator } from './node-archive-creator';
import { ResourcePiecesAdapter } from './resource-pieces.adapter';
import { ProjectResourcePiecesAdapter } from './resource-pieces/project-resource-pieces.adapter';
import { ScenarioResourcePiecesAdapter } from './resource-pieces/scenario-resource-pieces.adapter';
import { TypeormExportRepository } from './typeorm-export.repository';

@Module({
imports: [
FileRepositoryModule,
DiscoveryModule,
TypeOrmModule.forFeature([
Scenario,
ExportEntity,
Expand All @@ -32,16 +28,14 @@ import { TypeormExportRepository } from './typeorm-export.repository';
useClass: TypeormExportRepository,
},
{
provide: ResourcePieces,
useClass: ResourcePiecesAdapter,
provide: ExportResourcePieces,
useClass: ExportResourcePiecesAdapter,
},
{
provide: ArchiveCreator,
useClass: NodeArchiveCreator,
},
ProjectResourcePiecesAdapter,
ScenarioResourcePiecesAdapter,
],
exports: [ExportRepository, ResourcePieces, ArchiveCreator],
exports: [ExportRepository, ExportResourcePieces, ArchiveCreator],
})
export class ExportAdaptersModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { ClonePiece, ResourceId, ResourceKind } from '@marxan/cloning/domain';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Scenario } from '../../../scenarios/scenario.api.entity';
import { ExportResourcePieces } from '../application/export-resource-pieces.port';
import { ExportComponent } from '../domain';

@Injectable()
export class ExportResourcePiecesAdapter implements ExportResourcePieces {
private resolverMapping: Record<
ResourceKind,
(id: ResourceId, kind: ResourceKind) => Promise<ExportComponent[]>
> = {
project: this.resolveForProject.bind(this),
scenario: this.resolveForScenario.bind(this),
};

constructor(
@InjectRepository(Scenario)
private readonly scenarioRepository: Repository<Scenario>,
) {}

resolveFor(id: ResourceId, kind: ResourceKind): Promise<ExportComponent[]> {
return this.resolverMapping[kind](id, kind);
}

private async resolveForProject(
id: ResourceId,
kind: ResourceKind,
): Promise<ExportComponent[]> {
const scenarios = await this.scenarioRepository.find({
where: { projectId: id.value },
});

const scenarioPieces = await Promise.all(
scenarios.map((scenario) =>
this.resolveForScenario(new ResourceId(scenario.id), kind),
),
);

return [
ExportComponent.newOne(id, ClonePiece.ProjectMetadata),
ExportComponent.newOne(id, ClonePiece.ExportConfig),
ExportComponent.newOne(id, ClonePiece.PlanningAreaCustom),
ExportComponent.newOne(id, ClonePiece.PlanningAreaGAdm),
ExportComponent.newOne(id, ClonePiece.PlanningAreaGridCustom),
...scenarioPieces.flat(),
];
}

private async resolveForScenario(
id: ResourceId,
kind: ResourceKind,
): Promise<ExportComponent[]> {
const pieces: ExportComponent[] = [
ExportComponent.newOne(id, ClonePiece.ScenarioMetadata),
];

if (kind === ResourceKind.Scenario) {
pieces.push(ExportComponent.newOne(id, ClonePiece.ExportConfig));
}

return pieces;
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '../domain';

import { ExportProjectHandler } from './export-project.handler';
import { ResourcePieces } from './resource-pieces.port';
import { ExportResourcePieces } from './export-resource-pieces.port';
import { ExportRepository } from './export-repository.port';
import { ExportProject } from './export-project.command';
import { InMemoryExportRepo } from '../adapters/in-memory-export.repository';
Expand All @@ -38,7 +38,7 @@ const getFixtures = async () => {
imports: [CqrsModule],
providers: [
{
provide: ResourcePieces,
provide: ExportResourcePieces,
useClass: FakePiecesProvider,
},
{
Expand All @@ -54,7 +54,7 @@ const getFixtures = async () => {

const sut = sandbox.get(ExportProjectHandler);
const repo: InMemoryExportRepo = sandbox.get(ExportRepository);
const piecesResolver: FakePiecesProvider = sandbox.get(ResourcePieces);
const piecesResolver: FakePiecesProvider = sandbox.get(ExportResourcePieces);
sandbox.get(EventBus).subscribe((event) => {
events.push(event);
});
Expand Down Expand Up @@ -136,8 +136,10 @@ const getFixtures = async () => {
};

@Injectable()
class FakePiecesProvider implements ResourcePieces {
resolveMock: jest.MockedFunction<ResourcePieces['resolveFor']> = jest.fn();
class FakePiecesProvider implements ExportResourcePieces {
resolveMock: jest.MockedFunction<
ExportResourcePieces['resolveFor']
> = jest.fn();

async resolveFor(
id: ResourceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { ResourceKind } from '@marxan/cloning/domain';
import { Export, ExportId } from '../domain';

import { ExportProject } from './export-project.command';
import { ResourcePieces } from './resource-pieces.port';
import { ExportResourcePieces } from './export-resource-pieces.port';
import { ExportRepository } from './export-repository.port';

@CommandHandler(ExportProject)
export class ExportProjectHandler
implements IInferredCommandHandler<ExportProject> {
constructor(
private readonly resourcePieces: ResourcePieces,
private readonly resourcePieces: ExportResourcePieces,
private readonly exportRepository: ExportRepository,
private readonly eventPublisher: EventPublisher,
) {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ResourceId, ResourceKind } from '@marxan/cloning/domain';
import { ExportComponent } from '../domain';

export abstract class ResourcePieces {
export abstract class ExportResourcePieces {
abstract resolveFor(
id: ResourceId,
kind: ResourceKind,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { ResourceKind } from '@marxan/cloning/domain';
import { Export, ExportId } from '../domain';

import { ExportScenario } from './export-scenario.command';
import { ResourcePieces } from './resource-pieces.port';
import { ExportResourcePieces } from './export-resource-pieces.port';
import { ExportRepository } from './export-repository.port';

@CommandHandler(ExportScenario)
export class ExportScenarioHandler
implements IInferredCommandHandler<ExportScenario> {
constructor(
private readonly resourcePieces: ResourcePieces,
private readonly resourcePieces: ExportResourcePieces,
private readonly exportRepository: ExportRepository,
private readonly eventPublisher: EventPublisher,
) {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ArchiveLocation,
ClonePiece,
ResourceId,
ResourceKind,
} from '@marxan/cloning/domain';
import { ClonePieceRelativePaths } from '@marxan/cloning/infrastructure/clone-piece-data';
import { FileRepository } from '@marxan/files-repository';
import { fileNotFound } from '@marxan/files-repository/file.repository';
import { extractFile } from '@marxan/utils';
import { Injectable } from '@nestjs/common';
import { Either, isLeft, left, right } from 'fp-ts/lib/Either';
import {
archiveCorrupted,
ArchiveReader,
Failure,
invalidFiles,
} from '../application/archive-reader.port';
import { ImportResourcePieces } from '../application/import-resource-pieces.port';
import { Import } from '../domain';

@Injectable()
export class ArchiveReaderAdapter implements ArchiveReader {
constructor(
private readonly fileRepository: FileRepository,
private readonly importResourcePieces: ImportResourcePieces,
) {}

async get(location: ArchiveLocation): Promise<Either<Failure, Import>> {
const readableOrError = await this.fileRepository.get(location.value);
if (isLeft(readableOrError)) return left(fileNotFound);

const exportConfigOrError = await extractFile(
readableOrError.right,
new RegExp(ClonePieceRelativePaths[ClonePiece.ExportConfig].config),
);
if (isLeft(exportConfigOrError)) return left(archiveCorrupted);
const exportConfig = JSON.parse(exportConfigOrError.right);

const resourceId = new ResourceId(exportConfig.resourceId);
const resourceKind = exportConfig.resourceKind;

const validResourceKind = Object.values(ResourceKind).includes(
resourceKind,
);
if (!validResourceKind) return left(invalidFiles);

const pieces = await this.importResourcePieces.resolveFor(
resourceId,
resourceKind,
location,
);

return right(Import.newOne(resourceId, resourceKind, location, pieces));
}
}
Loading

0 comments on commit 9233caf

Please sign in to comment.