Skip to content

Commit

Permalink
Add editing service (create feature) (#42) (#282)
Browse files Browse the repository at this point in the history
Co-authored-by: Antonia van Eek <a.vaneek@conterra.de>
Co-authored-by: Matheisen, Alexander <alexander.matheisen@it.nrw.de>
Co-authored-by: ChristophMaskos <148751670+ChristophMaskos@users.noreply.github.com>
Co-authored-by: Dennis Payk <d.payk@conterra.de>
Co-authored-by: Sven Reissig <s.reissig@conterra.de>
  • Loading branch information
6 people committed Feb 22, 2024
1 parent b960396 commit ac7fdd1
Show file tree
Hide file tree
Showing 31 changed files with 1,799 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-ads-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@open-pioneer/editing": minor
---

Initial release.
5 changes: 5 additions & 0 deletions .changeset/two-brooms-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@open-pioneer/map": patch
---

Update documentation
29 changes: 29 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/packages/editing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @open-pioneer/editing
201 changes: 201 additions & 0 deletions src/packages/editing/EditingServiceImpl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it, vi } from "vitest";
import { EditingServiceImpl } from "./EditingServiceImpl";
import { setupMap } from "@open-pioneer/map-test-utils";
import { HttpService } from "@open-pioneer/http";
import { createService } from "@open-pioneer/test-utils/services";
import { FlatStyleLike } from "ol/style/flat";
import { EditingWorkflowImpl } from "./EditingWorkflowImpl";

const OGC_API_URL_TEST = new URL("https://example.org/ogc");

const HTTP_SERVICE: HttpService = {
fetch: vi.fn().mockResolvedValue(
new Response("", {
headers: {
Location: OGC_API_URL_TEST + "/test_id_1"
},
status: 201
})
)
} satisfies Partial<HttpService> as HttpService;

const POLYGON_DRAW_STYLE: FlatStyleLike = {
"stroke-color": "yellow",
"stroke-width": 2,
"fill-color": "rgba(0, 0, 0, 0.1)",
"circle-radius": 5,
"circle-fill-color": "rgba(0, 0, 255, 0.2)",
"circle-stroke-color": "rgba(0, 0, 255, 0.7)",
"circle-stroke-width": 2
};

describe("tests for starting an editing", () => {
it("should start an editing", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

const workflow = editingService.start(map, OGC_API_URL_TEST);
expect(workflow instanceof EditingWorkflowImpl).toBe(true);
});

it("should throw an error if start editing twice for the same map id", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

expect(() => editingService.start(map, OGC_API_URL_TEST)).toThrowError(
"EditingWorkflow could not be started. EditingWorkflow already in progress for this map."
);
});
});

describe("tests for stopping an editing", () => {
it("should stop an editing", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

const stop = editingService.stop(mapId);
expect(stop).toBeUndefined;
});

it("should return no error if editing will be stop twice for a given map id", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

editingService.stop(mapId);
const stop = editingService.stop(mapId);
expect(stop).toBeUndefined;
});

it("should return an error if editing will be stop for a non existing map id", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

const stop = editingService.stop("mapId");
expect(stop instanceof Error).toBe(true);
expect(stop?.message).toBe("No workflow found for mapId: mapId");
});
});

describe("tests for resetting an editing", () => {
it("should reset an editing", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

const reset = editingService.reset(mapId);
expect(reset).toBeUndefined;
});

it("should return no error if editing will be reset twice for a given map id", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

editingService.reset(mapId);
const reset = editingService.reset(mapId);
expect(reset).toBeUndefined;
});

it("should return an error if editing will be reset for a non existing map id", async () => {
const { mapId, registry } = await setupMap();
const map = await registry.expectMapModel(mapId);

const editingService = await createService(EditingServiceImpl, {
references: {
mapRegistry: registry,
httpService: HTTP_SERVICE
},
properties: {
POLYGON_DRAW_STYLE
}
});

editingService.start(map, OGC_API_URL_TEST);

const reset = editingService.reset("mapId");
expect(reset instanceof Error).toBe(true);
expect(reset?.message).toBe("No workflow found for mapId: mapId");
});
});
74 changes: 74 additions & 0 deletions src/packages/editing/EditingServiceImpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import { MapModel, MapRegistry } from "@open-pioneer/map";
import { EditingService } from "./api";
import { EditingWorkflowImpl } from "./EditingWorkflowImpl";
import { FlatStyleLike } from "ol/style/flat";
import { ServiceOptions } from "@open-pioneer/runtime";
import { HttpService } from "@open-pioneer/http";

export interface References {
mapRegistry: MapRegistry;
httpService: HttpService;
}

export class EditingServiceImpl implements EditingService {
private _serviceOptions: ServiceOptions<References>;
private _workflows: Map<string, EditingWorkflowImpl>;

constructor(serviceOptions: ServiceOptions<References>) {
this._serviceOptions = serviceOptions;
this._workflows = new Map();
}

start(map: MapModel, ogcApiFeatureLayerUrl: URL): EditingWorkflowImpl {
if (!ogcApiFeatureLayerUrl || !map || !map.id) {
throw new Error("Map, mapId or url is undefined.");
}

const mapId = map.id;

let workflow = this._workflows.get(mapId);
if (workflow) {
throw new Error(
"EditingWorkflow could not be started. EditingWorkflow already in progress for this map."
);
}

workflow = new EditingWorkflowImpl({
map,
ogcApiFeatureLayerUrl,
polygonDrawStyle: this._serviceOptions.properties.polygonDrawStyle as FlatStyleLike,
httpService: this._serviceOptions.references.httpService,
intl: this._serviceOptions.intl
});
this._workflows.set(mapId, workflow);
this._connectToWorkflowComplete(workflow, mapId);

return workflow;
}

stop(mapId: string): Error | void {
const workflow = this._workflows.get(mapId);
if (workflow) {
workflow.stop();
} else {
return new Error("No workflow found for mapId: " + mapId);
}
}

reset(mapId: string): Error | void {
const workflow = this._workflows.get(mapId);
if (workflow) {
workflow.reset();
} else {
return new Error("No workflow found for mapId: " + mapId);
}
}

_connectToWorkflowComplete(workflow: EditingWorkflowImpl, mapId: string) {
workflow.whenComplete().finally(() => {
this._workflows.delete(mapId);
});
}
}
Loading

0 comments on commit ac7fdd1

Please sign in to comment.