@masterportal/masterportalapi 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -1
- package/package.json +2 -1
- package/src/index.js +1 -1
- package/src/layer/wfs.js +226 -5
- package/src/maps/ol/olMap.js +1 -1
- package/test/layer/wfs.test.js +633 -14
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,35 @@
|
|
|
3
3
|
|
|
4
4
|
The [Semantic Versioning](https://semver.org/spec/v2.0.0.html) is used.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Unreleased - in development
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
### Deprecated
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
## 2.22.0 - 2023-07-25
|
|
7
20
|
### Added
|
|
8
21
|
- VectorStyle: Add Styling rules for Geoemtries of type `Circle`.
|
|
9
22
|
- Add Styling and zIndex to draw interactions and improve the draw behavior.
|
|
10
23
|
|
|
11
24
|
### Changed
|
|
12
25
|
- Changed peerDependency from cesium 1.106.0 to @cesium/engine 2.4.1.
|
|
26
|
+
- WFS:
|
|
27
|
+
- Migrated writeTransaction and sendTransaction from masterportal the masterportalAPI.
|
|
28
|
+
- Exports in src/layer/wfs.js changed to export default {} to provide ES-Syntax.
|
|
29
|
+
### Deprecated
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- WFS: fixed a bug that prevented parsing GeoServer responses.
|
|
13
35
|
|
|
14
36
|
---
|
|
15
37
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@masterportal/masterportalapi",
|
|
3
3
|
"author": "Implementierungspartnerschaft Masterportal <info@masterportal.org> (https://www.masterportal.org)",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.22.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Basic functions of the Masterportal as api",
|
|
7
7
|
"repository": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"jest": "^29.5.0",
|
|
39
39
|
"jest-canvas-mock": "^2.5.0",
|
|
40
40
|
"jest-environment-jsdom": "^29.5.0",
|
|
41
|
+
"jest-fetch-mock": "^3.0.3",
|
|
41
42
|
"jsdoc": "^4.0.2",
|
|
42
43
|
"parcel": "^2.8.3",
|
|
43
44
|
"process": "^0.11.10",
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import drawInteractions from "./maps/interactions/drawInteractions";
|
|
|
4
4
|
import rawLayerList from "./rawLayerList";
|
|
5
5
|
import * as wms from "./layer/wms";
|
|
6
6
|
import wmts from "./layer/wmts";
|
|
7
|
-
import
|
|
7
|
+
import wfs from "./layer/wfs";
|
|
8
8
|
import * as geojson from "./layer/geojson";
|
|
9
9
|
import * as vectorBase from "./layer/vectorBase";
|
|
10
10
|
import * as vectorTile from "./layer/vectorTile";
|
package/src/layer/wfs.js
CHANGED
|
@@ -154,7 +154,7 @@ function createUrl (rawLayer, version, projection, bboxParam) {
|
|
|
154
154
|
* {@link https://openlayers.org/en/latest/apidoc/module-ol_source_Vector-VectorSource.html failure/success see}
|
|
155
155
|
* @returns {(ol.source.VectorSource|ol.source.Cluster)} VectorSource or Cluster, depending on whether clusterDistance is set.
|
|
156
156
|
*/
|
|
157
|
-
|
|
157
|
+
function createLayerSource (rawLayer, options = {}) {
|
|
158
158
|
if (!options.loadingStrategy) {
|
|
159
159
|
options.loadingStrategy = bbox;
|
|
160
160
|
}
|
|
@@ -175,10 +175,15 @@ export function createLayerSource (rawLayer, options = {}) {
|
|
|
175
175
|
}
|
|
176
176
|
else {
|
|
177
177
|
const bboxParam = options.loadingStrategy === bbox ? `&bbox=${extent.join(",")},${projection.getCode()}` : "";
|
|
178
|
-
let url = createUrl(rawLayer, version, projection, bboxParam)
|
|
178
|
+
let url = createUrl(rawLayer, version, projection, bboxParam),
|
|
179
|
+
params = {};
|
|
179
180
|
|
|
180
181
|
if (options.loadingParams) {
|
|
181
182
|
for (const key in options.loadingParams) {
|
|
183
|
+
if (key === "xhrParameters") {
|
|
184
|
+
params = options.loadingParams.xhrParameters;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
182
187
|
const option = Array.isArray(options.loadingParams[key]) ? options.loadingParams[key].join(",") : options.loadingParams[key];
|
|
183
188
|
|
|
184
189
|
if (option !== undefined && key === "bbox") {
|
|
@@ -189,7 +194,7 @@ export function createLayerSource (rawLayer, options = {}) {
|
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
}
|
|
192
|
-
loadWFS(url,
|
|
197
|
+
loadWFS(url, params, source, {onErrorFn: onError, success, failure}, options);
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
format.featureType = rawLayer.featureType;
|
|
@@ -224,7 +229,7 @@ export function createLayerSource (rawLayer, options = {}) {
|
|
|
224
229
|
* @param {options} [optionalParams.options] - additional options
|
|
225
230
|
* @returns {ol.Layer} Layer that can be added to map.
|
|
226
231
|
*/
|
|
227
|
-
|
|
232
|
+
function createLayer (rawLayer = {}, {layerParams = {}, options = {}} = {}) {
|
|
228
233
|
let layer, source;
|
|
229
234
|
|
|
230
235
|
// use WebGL render pipeline, if specified
|
|
@@ -260,13 +265,227 @@ export function createLayer (rawLayer = {}, {layerParams = {}, options = {}} = {
|
|
|
260
265
|
return layer;
|
|
261
266
|
}
|
|
262
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Writes a wfs transaction including the given feature for given layer
|
|
270
|
+
* inserting, updated or deleting the feature depending on the given selectedInteraction.
|
|
271
|
+
*
|
|
272
|
+
* @param {ol/Feature} feature Feature to be inserted, updated or deleted.
|
|
273
|
+
* @param {Object} transactionOptions Information about the layer to be manipulated.
|
|
274
|
+
* @param {string} transactionMethod Which transaction to perform. Possible values are: "insert"|"delete"|"selectedUpdate"
|
|
275
|
+
* @param {string} srsName EPSG code currently used by the map.
|
|
276
|
+
* @returns {string} WFS Transaction as an XML Node as String.
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
function writeTransactionBody (feature, transactionOptions, transactionMethod, version = "1.1.0") {
|
|
280
|
+
const transactionPosition = {
|
|
281
|
+
insert: 0,
|
|
282
|
+
selectedUpdate: 1,
|
|
283
|
+
delete: 2
|
|
284
|
+
},
|
|
285
|
+
transaction = [[], [], []];
|
|
286
|
+
|
|
287
|
+
if (transactionMethod !== "insert" && transactionMethod !== "delete" && transactionMethod !== "selectedUpdate") {
|
|
288
|
+
throw new Error("transactionMethod has to be \"insert\", \"selectedUpdate\" or \"delete\".");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
transaction[transactionPosition[transactionMethod]].push(feature);
|
|
292
|
+
return new XMLSerializer()
|
|
293
|
+
.serializeToString(new WFS({version})
|
|
294
|
+
.writeTransaction(...transaction, transactionOptions));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Checks if it is an unknown or special error and returns the code and the error message
|
|
299
|
+
*
|
|
300
|
+
* @param {Object} xmlDocument with the error
|
|
301
|
+
* @returns {Object} the code and the specific or unknown error looking like this {code, message}
|
|
302
|
+
*/
|
|
303
|
+
function getExceptionFromTransactionResponse (xmlDocument) {
|
|
304
|
+
const response = {code: null, message: "genericFailedTransaction"},
|
|
305
|
+
exception = xmlDocument.getElementsByTagName(`${xmlDocument.getElementsByTagName("Exception").length === 0 ? "ows:" : ""}Exception`)[0],
|
|
306
|
+
exceptionText = exception.getElementsByTagName(`${xmlDocument.getElementsByTagName("ExceptionText").length === 0 ? "ows:" : ""}ExceptionText`)[0];
|
|
307
|
+
|
|
308
|
+
if (exceptionText !== undefined) {
|
|
309
|
+
response.message = exceptionText.textContent;
|
|
310
|
+
console.error("WfsTransaction: An error occurred when sending the transaction to the service.", exceptionText.textContent);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
response.message = "WfsTransaction: An unkown error occurred when sending the transaction to the service.";
|
|
314
|
+
console.error(response.message);
|
|
315
|
+
}
|
|
316
|
+
if (exception?.attributes.getNamedItem("code") || exception?.attributes.getNamedItem("exceptionCode")) {
|
|
317
|
+
response.code = exception.attributes.getNamedItem(`${exception?.attributes.getNamedItem("code") ? "c" : "exceptionC"}ode`).textContent;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return response;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Sends a transaction to the service and returns the feature.
|
|
326
|
+
*
|
|
327
|
+
* @param {string} srsName of the coordinate reference system
|
|
328
|
+
* @param {ol/Feature} feature Feature to by inserted / updated / deleted.
|
|
329
|
+
* @param {string} url of the wfs-t service
|
|
330
|
+
* @param {Object} layer the configured representation of the layer in the Masterportal
|
|
331
|
+
* @param {string} transactionMethod which transaction to perform. Possible values are: "insert"|"delete"|"selectedUpdate"
|
|
332
|
+
* @returns {ol/Feature} the given feature
|
|
333
|
+
* @throws {Error} error if occurs
|
|
334
|
+
*/
|
|
335
|
+
async function sendTransaction (srsName, feature, url, layer, transactionMethod) {
|
|
336
|
+
let exception,
|
|
337
|
+
response,
|
|
338
|
+
xmlDocument = null,
|
|
339
|
+
transactionSummary = null,
|
|
340
|
+
data = null;
|
|
341
|
+
|
|
342
|
+
const {featureNS, featurePrefix, featureType, version} = layer;
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
response = await fetch(url, {
|
|
346
|
+
method: "POST",
|
|
347
|
+
headers: {"Content-Type": "text/xml"},
|
|
348
|
+
credentials: layer.isSecured ? "include" : "omit",
|
|
349
|
+
body: writeTransactionBody(feature,
|
|
350
|
+
{featureNS, featurePrefix, featureType, version, srsName},
|
|
351
|
+
transactionMethod,
|
|
352
|
+
version),
|
|
353
|
+
responseType: "text"
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
data = await response.text();
|
|
357
|
+
|
|
358
|
+
xmlDocument = new DOMParser().parseFromString(data, "text/xml");
|
|
359
|
+
transactionSummary = xmlDocument.getElementsByTagName("wfs:TransactionSummary");
|
|
360
|
+
|
|
361
|
+
// NOTE: WFS-T services respond errors with the transaction as an XML response, even though it's the http code indicates different...
|
|
362
|
+
if (transactionSummary.length === 0) {
|
|
363
|
+
exception = getExceptionFromTransactionResponse(xmlDocument);
|
|
364
|
+
throw new Error(exception.code ? exception.code + ": " + exception.message : exception.message);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
console.error(e);
|
|
369
|
+
throw e;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return feature;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Parses the response of a DescribeFeatureType request
|
|
377
|
+
* and prepares its values for later use as an input value for the user.
|
|
378
|
+
*
|
|
379
|
+
* @param {string} responseData XML response data
|
|
380
|
+
* @param {string} featureType Name of the feature type of the service.
|
|
381
|
+
* @returns {Array} If an <element> with a name of the featureType is present, an array of prepared feature properties; else an empty Array.
|
|
382
|
+
*/
|
|
383
|
+
function parseDescribeFeatureTypeResponse (responseData, featureType) {
|
|
384
|
+
|
|
385
|
+
const parsedResponse = new DOMParser().parseFromString(responseData, "application/xml");
|
|
386
|
+
|
|
387
|
+
let prefix = "",
|
|
388
|
+
foundElement = null;
|
|
389
|
+
|
|
390
|
+
if (parsedResponse.getElementsByTagName("xsd:element").length > 0) {
|
|
391
|
+
prefix = "xsd:";
|
|
392
|
+
}
|
|
393
|
+
else if (parsedResponse.getElementsByTagName("xs:element").length > 0) {
|
|
394
|
+
prefix = "xs";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Check if featureType applies to the response
|
|
398
|
+
foundElement = Object.values(parsedResponse.getElementsByTagName(prefix + "element")).find(element => element.getAttribute("name") === featureType);
|
|
399
|
+
|
|
400
|
+
if (foundElement) {
|
|
401
|
+
let sequenceElements;
|
|
402
|
+
|
|
403
|
+
if (foundElement.hasChildNodes()) {
|
|
404
|
+
sequenceElements = Object.values(foundElement.getElementsByTagName(prefix + "sequence"));
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
sequenceElements = Object.values(parsedResponse.getElementsByTagName(prefix + "sequence"));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const elements = [];
|
|
411
|
+
|
|
412
|
+
sequenceElements.forEach(sequenceElement => {
|
|
413
|
+
elements.push(...Object.values(sequenceElement.getElementsByTagName(prefix + "element")));
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
return elements.map(el => Object.values(el.attributes).reduce((obj, att) => {
|
|
417
|
+
if (att.localName === "minOccurs") {
|
|
418
|
+
obj.required = att.value === "1";
|
|
419
|
+
}
|
|
420
|
+
else if (att.localName === "type") {
|
|
421
|
+
if (att.value.trim().startsWith("gml")) {
|
|
422
|
+
obj.type = "geometry";
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
let value = att.value;
|
|
426
|
+
|
|
427
|
+
if (prefix.length > 0) {
|
|
428
|
+
value = value.replace(prefix, "");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
switch (value) {
|
|
432
|
+
case "long":
|
|
433
|
+
obj.type = "integer";
|
|
434
|
+
break;
|
|
435
|
+
default:
|
|
436
|
+
obj.type = value;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else if (att.localName === "name") {
|
|
441
|
+
obj.label = obj.key = att.value;
|
|
442
|
+
}
|
|
443
|
+
return obj;
|
|
444
|
+
}, {
|
|
445
|
+
label: "",
|
|
446
|
+
key: "",
|
|
447
|
+
value: null,
|
|
448
|
+
type: "string",
|
|
449
|
+
required: false
|
|
450
|
+
}));
|
|
451
|
+
}
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Requests the possible properties of a feature and further values;
|
|
457
|
+
* for more {@see FeatureProperty}.
|
|
458
|
+
*
|
|
459
|
+
* @param {string} url from the WFS-T
|
|
460
|
+
* @param {string} version of the WFS-T
|
|
461
|
+
* @param {string} featureType Name of the FeatureType according to the capabilities document
|
|
462
|
+
* @param {boolean} isSecured Whether the layer is secured by BasicAuth and should include or omit the credentials prompt
|
|
463
|
+
* @returns {Promise<Array>} If the request is successful, an array of prepared feature properties.
|
|
464
|
+
*/
|
|
465
|
+
async function receivePossibleProperties (url, version, featureType, isSecured) {
|
|
466
|
+
const baseUrl = `${url}?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=${version}&TYPENAME=${featureType}`;
|
|
467
|
+
let response;
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
response = await fetch(baseUrl, {
|
|
471
|
+
responseType: "text/xml",
|
|
472
|
+
credentials: isSecured ? "include" : "omit"
|
|
473
|
+
});
|
|
474
|
+
return parseDescribeFeatureTypeResponse(await response.text(), featureType);
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
console.error(e);
|
|
478
|
+
throw e;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
263
482
|
/**
|
|
264
483
|
* Load the features manually.
|
|
265
484
|
* @param {Object} layerAttributes raw layer attributes.
|
|
266
485
|
* @param {module:ol/vector/Source} layerSource - the source of the layer
|
|
267
486
|
* @returns {void}
|
|
268
487
|
*/
|
|
269
|
-
|
|
488
|
+
function loadFeaturesManually (layerAttributes, layerSource) {
|
|
270
489
|
const getUrl = createUrl(layerAttributes, layerAttributes.version, {getCode: () => layerAttributes.crs}, "");
|
|
271
490
|
|
|
272
491
|
fetch(getUrl, layerSource)
|
|
@@ -278,3 +497,5 @@ export function loadFeaturesManually (layerAttributes, layerSource) {
|
|
|
278
497
|
console.error(error);
|
|
279
498
|
});
|
|
280
499
|
}
|
|
500
|
+
|
|
501
|
+
export default {createLayerSource, createLayer, sendTransaction, receivePossibleProperties, parseDescribeFeatureTypeResponse, writeTransactionBody, loadFeaturesManually};
|
package/src/maps/ol/olMap.js
CHANGED
|
@@ -6,7 +6,7 @@ import defaults from "../../defaults";
|
|
|
6
6
|
import * as wms from "../../layer/wms";
|
|
7
7
|
import wmts from "../../layer/wmts";
|
|
8
8
|
import * as geojson from "../../layer/geojson";
|
|
9
|
-
import
|
|
9
|
+
import wfs from "../../layer/wfs";
|
|
10
10
|
import * as vectorBase from "../../layer/vectorBase";
|
|
11
11
|
import * as vectortile from "../../layer/vectorTile";
|
|
12
12
|
import * as oaf from "../../layer/oaf";
|
package/test/layer/wfs.test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {enableFetchMocks} from "jest-fetch-mock";
|
|
1
2
|
import VectorLayer from "ol/layer/Vector.js";
|
|
2
3
|
import VectorSource from "ol/source/Vector.js";
|
|
3
4
|
import Cluster from "ol/source/Cluster.js";
|
|
@@ -5,10 +6,13 @@ import {Style, Icon} from "ol/style.js";
|
|
|
5
6
|
import map from "../../src/maps/map.js";
|
|
6
7
|
import defaults from "../../src/defaults";
|
|
7
8
|
import {WFS} from "ol/format.js";
|
|
8
|
-
import
|
|
9
|
+
import wfs from "../../src/layer/wfs";
|
|
9
10
|
import {featureCollection} from "./resources/wfsFeatures";
|
|
10
11
|
import {wfsFilterQuery} from "./resources/wfsFilter";
|
|
11
12
|
import * as webgl from "../../src/renderer/webgl.js";
|
|
13
|
+
import {Feature} from "ol";
|
|
14
|
+
import Polygon from "ol/geom/Polygon";
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
jest.mock("../../src/rawLayerList.js", () => {
|
|
14
18
|
const original = jest.requireActual("../../src/rawLayerList.js");
|
|
@@ -26,6 +30,22 @@ jest.mock("../../src/renderer/webgl.js", () => {
|
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
describe("wfs.js", function () {
|
|
33
|
+
|
|
34
|
+
const originalConsoleError = console.error;
|
|
35
|
+
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
console.error = jest.fn();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterAll(() => {
|
|
41
|
+
console.error = originalConsoleError;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
enableFetchMocks();
|
|
46
|
+
fetch.resetMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
29
49
|
describe("createLayer", function () {
|
|
30
50
|
it("creates a VectorLayer without id", function () {
|
|
31
51
|
const layer = wfs.createLayer({version: "1.1.0"});
|
|
@@ -123,17 +143,12 @@ describe("wfs.js", function () {
|
|
|
123
143
|
});
|
|
124
144
|
});
|
|
125
145
|
describe("createLayerSource", function () {
|
|
126
|
-
const consoleError = console.error;
|
|
127
|
-
|
|
128
146
|
beforeEach(() => {
|
|
129
147
|
if (global.fetch) {
|
|
130
148
|
global.fetch.mockClear();
|
|
131
149
|
}
|
|
132
150
|
});
|
|
133
|
-
|
|
134
|
-
afterEach(() => {
|
|
135
|
-
console.error = consoleError;
|
|
136
|
-
});
|
|
151
|
+
|
|
137
152
|
it("creates a VectorSource and beforeLoading, afterLoading and featuresFilter are called", function () {
|
|
138
153
|
global.fetch = jest.fn().mockImplementationOnce(() => {
|
|
139
154
|
return new Promise((resolve) => {
|
|
@@ -312,8 +327,6 @@ describe("wfs.js", function () {
|
|
|
312
327
|
});
|
|
313
328
|
});
|
|
314
329
|
});
|
|
315
|
-
// to reset show error on load in console when testing 'onLoadingError'
|
|
316
|
-
console.error = jest.fn();
|
|
317
330
|
|
|
318
331
|
const map2D = map.createMap(defaults),
|
|
319
332
|
url = "https://url.de",
|
|
@@ -413,9 +426,618 @@ describe("wfs.js", function () {
|
|
|
413
426
|
source.getListeners("featuresloadend")[0]({features: []});
|
|
414
427
|
expect(webgl.afterLoading).toHaveBeenCalled();
|
|
415
428
|
});
|
|
429
|
+
it("creates a vectorSource with credentials: include as params", () => {
|
|
430
|
+
global.fetch = jest.fn().mockImplementationOnce(() => {
|
|
431
|
+
// NOTE: fetch only rejects if a network error occurs, which do not apply to 4xx or 5xx http codes.
|
|
432
|
+
return new Promise((resolve) => {
|
|
433
|
+
resolve({
|
|
434
|
+
ok: true,
|
|
435
|
+
status: 200,
|
|
436
|
+
text: () => {
|
|
437
|
+
return featureCollection;
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const map2D = map.createMap(defaults),
|
|
444
|
+
url = "https://url.de",
|
|
445
|
+
rawLayer = {
|
|
446
|
+
id: "id",
|
|
447
|
+
url: url,
|
|
448
|
+
featureNS: "http://www.deegree.org/app",
|
|
449
|
+
featureType: "krankenhaeuser_hh",
|
|
450
|
+
version: "2.0.0"
|
|
451
|
+
},
|
|
452
|
+
options = {
|
|
453
|
+
loadingParams: {xhrParameters: {credentials: "include"}}
|
|
454
|
+
},
|
|
455
|
+
layer = wfs.createLayer(rawLayer, {options});
|
|
456
|
+
|
|
457
|
+
layer.getSource().loadFeatures([-10000, -10000, 10000, 10000],
|
|
458
|
+
1,
|
|
459
|
+
map2D.getView().getProjection());
|
|
460
|
+
|
|
461
|
+
expect(fetch).toHaveBeenCalledWith("https://url.de/?service=WFS&version=2.0.0&request=GetFeature&srsName=EPSG%3A25832&typeNames=krankenhaeuser_hh&bbox=-10000,-10000,10000,10000,EPSG:25832", {"credentials": "include"});
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
describe("parseDescribeFeatureTypeResponse", () => {
|
|
465
|
+
const exampleDescribeFeatureType = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
466
|
+
"<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:app=\"http://www.deegree.org/app\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" targetNamespace=\"http://www.deegree.org/app\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" +
|
|
467
|
+
" <import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>\n" +
|
|
468
|
+
" <element name=\"wfstgeom\" substitutionGroup=\"gml:_Feature\">\n" +
|
|
469
|
+
" <complexType>\n" +
|
|
470
|
+
" <complexContent>\n" +
|
|
471
|
+
" <extension base=\"gml:AbstractFeatureType\">\n" +
|
|
472
|
+
" <sequence>\n" +
|
|
473
|
+
" <element name=\"name\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
474
|
+
" <element name=\"nummer\" minOccurs=\"0\" type=\"integer\"/>\n" +
|
|
475
|
+
" <element name=\"bemerkung\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
476
|
+
" <element name=\"datum\" minOccurs=\"0\" type=\"date\"/>\n" +
|
|
477
|
+
" <element name=\"geom\" minOccurs=\"0\" type=\"gml:GeometryPropertyType\"/>\n" +
|
|
478
|
+
" </sequence>\n" +
|
|
479
|
+
" </extension>\n" +
|
|
480
|
+
" </complexContent>\n" +
|
|
481
|
+
" </complexType>\n" +
|
|
482
|
+
" </element>\n" +
|
|
483
|
+
"</schema>",
|
|
484
|
+
exampleProperties = [
|
|
485
|
+
{
|
|
486
|
+
key: "name",
|
|
487
|
+
label: "name",
|
|
488
|
+
required: false,
|
|
489
|
+
type: "string",
|
|
490
|
+
value: null
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
key: "nummer",
|
|
494
|
+
label: "nummer",
|
|
495
|
+
required: false,
|
|
496
|
+
type: "integer",
|
|
497
|
+
value: null
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
key: "bemerkung",
|
|
501
|
+
label: "bemerkung",
|
|
502
|
+
required: false,
|
|
503
|
+
type: "string",
|
|
504
|
+
value: null
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
key: "datum",
|
|
508
|
+
label: "datum",
|
|
509
|
+
required: false,
|
|
510
|
+
type: "date",
|
|
511
|
+
value: null
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
key: "geom",
|
|
515
|
+
label: "geom",
|
|
516
|
+
required: false,
|
|
517
|
+
type: "geometry",
|
|
518
|
+
value: null
|
|
519
|
+
}
|
|
520
|
+
],
|
|
521
|
+
|
|
522
|
+
exampleDescribeFeatureType2 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
|
523
|
+
"<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml/3.2\" xmlns:gsm_wfs=\"gsm_wfs\" xmlns:wfs=\"http://www.opengis.net/wfs/2.0\" elementFormDefault=\"qualified\" targetNamespace=\"gsm_wfs\">\n" +
|
|
524
|
+
"<xsd:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"https://geoportal-konfig.muenchen.de/geoserver/schemas/gml/3.2.1/gml.xsd\"/>\n" +
|
|
525
|
+
"<xsd:complexType name=\"wfstgeomType\">\n" +
|
|
526
|
+
"<xsd:complexContent>\n" +
|
|
527
|
+
"<xsd:extension base=\"gml:AbstractFeatureType\">\n" +
|
|
528
|
+
"<xsd:sequence>\n" +
|
|
529
|
+
"<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"name\" nillable=\"true\" type=\"xsd:string\"/>\n" +
|
|
530
|
+
"<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"nummer\" nillable=\"true\" type=\"xsd:double\"/>\n" +
|
|
531
|
+
"<xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"shape\" nillable=\"true\" type=\"gml:GeometryPropertyType\"/>\n" +
|
|
532
|
+
"</xsd:sequence>\n" +
|
|
533
|
+
"</xsd:extension>\n" +
|
|
534
|
+
"</xsd:complexContent>\n" +
|
|
535
|
+
"</xsd:complexType>\n" +
|
|
536
|
+
"<xsd:element name=\"wfstgeom\" substitutionGroup=\"gml:AbstractFeature\" type=\"gsm_wfs:kultur_rbe1_museenType\"/>\n" +
|
|
537
|
+
"</xsd:schema>",
|
|
538
|
+
|
|
539
|
+
exampleProperties2 = [
|
|
540
|
+
{
|
|
541
|
+
key: "name",
|
|
542
|
+
label: "name",
|
|
543
|
+
required: false,
|
|
544
|
+
type: "string",
|
|
545
|
+
value: null
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
key: "nummer",
|
|
549
|
+
label: "nummer",
|
|
550
|
+
required: false,
|
|
551
|
+
type: "double",
|
|
552
|
+
value: null
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
key: "shape",
|
|
556
|
+
label: "shape",
|
|
557
|
+
required: false,
|
|
558
|
+
type: "geometry",
|
|
559
|
+
value: null
|
|
560
|
+
}
|
|
561
|
+
];
|
|
562
|
+
|
|
563
|
+
let featureType;
|
|
564
|
+
|
|
565
|
+
it("should retrieve the element values (required, type, key) from the parsed XML string if an element with name = featureType can be found in the XML for Example 1", () => {
|
|
566
|
+
featureType = "wfstgeom";
|
|
567
|
+
|
|
568
|
+
const featureProperties = wfs.parseDescribeFeatureTypeResponse(exampleDescribeFeatureType, featureType);
|
|
569
|
+
|
|
570
|
+
expect(Array.isArray(featureProperties)).toBe(true);
|
|
571
|
+
expect(featureProperties.length).toEqual(5);
|
|
572
|
+
exampleProperties.forEach(property => {
|
|
573
|
+
const parsedProperty = featureProperties.find(({key}) => key === property.key);
|
|
574
|
+
|
|
575
|
+
expect(parsedProperty).not.toEqual(undefined);
|
|
576
|
+
expect(parsedProperty).toEqual(property);
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
it("should return an empty array if no element with name = featureType can be found in the XML Example 1", () => {
|
|
580
|
+
featureType = "myCoolType";
|
|
581
|
+
|
|
582
|
+
const featureProperties = wfs.parseDescribeFeatureTypeResponse(exampleDescribeFeatureType, featureType);
|
|
583
|
+
|
|
584
|
+
expect(Array.isArray(featureProperties)).toBe(true);
|
|
585
|
+
expect(featureProperties.length).toEqual(0);
|
|
586
|
+
});
|
|
587
|
+
it("should retrieve the element values (required, type, key) from the parsed XML string if an element with name = featureType can be found in the XML for Example 2", () => {
|
|
588
|
+
featureType = "wfstgeom";
|
|
589
|
+
|
|
590
|
+
const featureProperties = wfs.parseDescribeFeatureTypeResponse(exampleDescribeFeatureType2, featureType);
|
|
591
|
+
|
|
592
|
+
expect(Array.isArray(featureProperties)).toBe.true;
|
|
593
|
+
expect(featureProperties.length).toEqual(3);
|
|
594
|
+
|
|
595
|
+
exampleProperties2.forEach(property => {
|
|
596
|
+
const parsedProperty = featureProperties.find(({key}) => key === property.key);
|
|
597
|
+
|
|
598
|
+
expect(parsedProperty).not.toEqual(undefined);
|
|
599
|
+
expect(parsedProperty).toEqual(property);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
it("should return an empty array if no element with name = featureType can be found in the XML Example 2", () => {
|
|
603
|
+
featureType = "myCoolType";
|
|
604
|
+
|
|
605
|
+
const featureProperties = wfs.parseDescribeFeatureTypeResponse(exampleDescribeFeatureType2, featureType);
|
|
606
|
+
|
|
607
|
+
expect(Array.isArray(featureProperties)).toBe(true);
|
|
608
|
+
expect(featureProperties.length).toEqual(0);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
});
|
|
612
|
+
describe("writeTransactionBody", () => {
|
|
613
|
+
const feature = new Feature({
|
|
614
|
+
geometry: new Polygon([
|
|
615
|
+
[
|
|
616
|
+
[
|
|
617
|
+
9.17782024967994,
|
|
618
|
+
50.20836600730087
|
|
619
|
+
],
|
|
620
|
+
[
|
|
621
|
+
9.200676227149245,
|
|
622
|
+
50.20836600730087
|
|
623
|
+
],
|
|
624
|
+
[
|
|
625
|
+
9.200676227149245,
|
|
626
|
+
50.20873353776312
|
|
627
|
+
],
|
|
628
|
+
[
|
|
629
|
+
9.17782024967994,
|
|
630
|
+
50.20873353776312
|
|
631
|
+
],
|
|
632
|
+
[
|
|
633
|
+
9.17782024967994,
|
|
634
|
+
50.20836600730087
|
|
635
|
+
]
|
|
636
|
+
]]),
|
|
637
|
+
name: "My Polygon"
|
|
638
|
+
}),
|
|
639
|
+
|
|
640
|
+
transactionOptions = {
|
|
641
|
+
featureNS: "myNS",
|
|
642
|
+
featurePrefix: "myPrefix",
|
|
643
|
+
featureType: "Polygon",
|
|
644
|
+
srsName: "EPSG:4326"
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
it("should write a transaction body for inserting a feature", () => {
|
|
649
|
+
const transactionBody = wfs.writeTransactionBody(feature, transactionOptions, "insert"),
|
|
650
|
+
expectedBody = "<Transaction xmlns=\"http://www.opengis.net/wfs\" service=\"WFS\" version=\"1.1.0\" xmlns:ns1=\"http://www.w3.org/2001/XMLSchema-instance\" ns1:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"><Insert><Polygon xmlns=\"myNS\"><geometry><Polygon xmlns=\"http://www.opengis.net/gml\" srsName=\"EPSG:4326\"><exterior><LinearRing srsName=\"EPSG:4326\"><posList srsDimension=\"2\">50.20836600730087 9.17782024967994 50.20836600730087 9.200676227149245 50.20873353776312 9.200676227149245 50.20873353776312 9.17782024967994 50.20836600730087 9.17782024967994</posList></LinearRing></exterior></Polygon></geometry><name>My Polygon</name></Polygon></Insert></Transaction>";
|
|
651
|
+
|
|
652
|
+
expect(transactionBody).toEqual(expectedBody);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it("should write a transaction body for updating a feature", () => {
|
|
656
|
+
feature.setId(1);
|
|
657
|
+
const transactionBody = wfs.writeTransactionBody(feature, transactionOptions, "selectedUpdate"),
|
|
658
|
+
expectedBody = "<Transaction xmlns=\"http://www.opengis.net/wfs\" service=\"WFS\" version=\"1.1.0\" xmlns:ns1=\"http://www.w3.org/2001/XMLSchema-instance\" ns1:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"><Update typeName=\"myPrefix:Polygon\" xmlns:myPrefix=\"myNS\"><Property><Name>geometry</Name><Value><Polygon xmlns=\"http://www.opengis.net/gml\" srsName=\"EPSG:4326\"><exterior><LinearRing srsName=\"EPSG:4326\"><posList srsDimension=\"2\">50.20836600730087 9.17782024967994 50.20836600730087 9.200676227149245 50.20873353776312 9.200676227149245 50.20873353776312 9.17782024967994 50.20836600730087 9.17782024967994</posList></LinearRing></exterior></Polygon></Value></Property><Property><Name>name</Name><Value>My Polygon</Value></Property><Filter xmlns=\"http://www.opengis.net/ogc\"><FeatureId fid=\"1\"/></Filter></Update></Transaction>";
|
|
659
|
+
|
|
660
|
+
expect(transactionBody).toEqual(expectedBody);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it("should write a transaction body for deleting a feature", () => {
|
|
664
|
+
feature.setId(1);
|
|
665
|
+
const transactionBody = wfs.writeTransactionBody(feature, transactionOptions, "delete"),
|
|
666
|
+
expectedBody = "<Transaction xmlns=\"http://www.opengis.net/wfs\" service=\"WFS\" version=\"1.1.0\" xmlns:ns1=\"http://www.w3.org/2001/XMLSchema-instance\" ns1:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"><Delete typeName=\"myPrefix:Polygon\" xmlns:myPrefix=\"myNS\"><Filter xmlns=\"http://www.opengis.net/ogc\"><FeatureId fid=\"1\"/></Filter></Delete></Transaction>";
|
|
667
|
+
|
|
668
|
+
expect(transactionBody).toEqual(expectedBody);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it("should throw error if transactionMethod is invalid", () => {
|
|
672
|
+
expect(() => wfs.writeTransactionBody(feature, transactionOptions, "remove")).toThrow(Error);
|
|
673
|
+
expect(() => wfs.writeTransactionBody(feature, transactionOptions, "remove")).toThrow("transactionMethod has to be \"insert\", \"selectedUpdate\" or \"delete\".");
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
describe("receivePossibleProperties", () => {
|
|
678
|
+
const exampleDescribeFeatureType = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
679
|
+
"<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml\" xmlns:sf=\"http://cite.opengeospatial.org/gmlsf\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" targetNamespace=\"http://cite.opengeospatial.org/gmlsf\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n" +
|
|
680
|
+
" <import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>\n" +
|
|
681
|
+
" <element name=\"wfstpoint\" substitutionGroup=\"gml:_Feature\">\n" +
|
|
682
|
+
" <complexType>\n" +
|
|
683
|
+
" <complexContent>\n" +
|
|
684
|
+
" <extension base=\"gml:AbstractFeatureType\">\n" +
|
|
685
|
+
" <sequence>\n" +
|
|
686
|
+
" <element name=\"geometry\" minOccurs=\"0\" type=\"gml:PointPropertyType\"/>\n" +
|
|
687
|
+
" <element name=\"name\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
688
|
+
" <element name=\"baumnummer\" minOccurs=\"0\" type=\"integer\"/>\n" +
|
|
689
|
+
" <element name=\"bemerkung\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
690
|
+
" <element name=\"datum\" minOccurs=\"0\" type=\"date\"/>\n" +
|
|
691
|
+
" <element name=\"isNew\" minOccurs=\"0\" type=\"boolean\"/>\n" +
|
|
692
|
+
" <element name=\"isUpdate\" minOccurs=\"0\" type=\"boolean\"/>\n" +
|
|
693
|
+
" </sequence>\n" +
|
|
694
|
+
" </extension>\n" +
|
|
695
|
+
" </complexContent>\n" +
|
|
696
|
+
" </complexType>\n" +
|
|
697
|
+
" </element>\n" +
|
|
698
|
+
" <element name=\"wfstline\" substitutionGroup=\"gml:_Feature\">\n" +
|
|
699
|
+
" <complexType>\n" +
|
|
700
|
+
" <complexContent>\n" +
|
|
701
|
+
" <extension base=\"gml:AbstractFeatureType\">\n" +
|
|
702
|
+
" <sequence>\n" +
|
|
703
|
+
" <element name=\"geometry\" minOccurs=\"0\" type=\"gml:LineStringPropertyType\"/>\n" +
|
|
704
|
+
" <element name=\"name\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
705
|
+
" <element name=\"nummer\" minOccurs=\"0\" type=\"integer\"/>\n" +
|
|
706
|
+
" <element name=\"bemerkung\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
707
|
+
" <element name=\"datum\" minOccurs=\"0\" type=\"date\"/>\n" +
|
|
708
|
+
" </sequence>\n" +
|
|
709
|
+
" </extension>\n" +
|
|
710
|
+
" </complexContent>\n" +
|
|
711
|
+
" </complexType>\n" +
|
|
712
|
+
" </element>\n" +
|
|
713
|
+
" <element name=\"wfstpolygon\" substitutionGroup=\"gml:_Feature\">\n" +
|
|
714
|
+
" <complexType>\n" +
|
|
715
|
+
" <complexContent>\n" +
|
|
716
|
+
" <extension base=\"gml:AbstractFeatureType\">\n" +
|
|
717
|
+
" <sequence>\n" +
|
|
718
|
+
" <element name=\"name\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
719
|
+
" <element name=\"nummer\" minOccurs=\"0\" type=\"integer\"/>\n" +
|
|
720
|
+
" <element name=\"bemerkung\" minOccurs=\"0\" type=\"string\"/>\n" +
|
|
721
|
+
" <element name=\"datum\" minOccurs=\"0\" type=\"date\"/>\n" +
|
|
722
|
+
" <element name=\"geom\" minOccurs=\"0\" type=\"gml:GeometryPropertyType\"/>\n" +
|
|
723
|
+
" </sequence>\n" +
|
|
724
|
+
" </extension>\n" +
|
|
725
|
+
" </complexContent>\n" +
|
|
726
|
+
" </complexType>\n" +
|
|
727
|
+
" </element>\n" +
|
|
728
|
+
"</schema>",
|
|
729
|
+
geoserverDescribeFeatureType = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml/3.2\" xmlns:test=\"http://opengeo.org/test\" xmlns:wfs=\"http://www.opengis.net/wfs/2.0\" elementFormDefault=\"qualified\" targetNamespace=\"http://opengeo.org/test\">\n" +
|
|
730
|
+
" <xsd:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"https://geoservice.norderstedt.de/geoserver/schemas/gml/3.2.1/gml.xsd\"/>\n" +
|
|
731
|
+
" <xsd:complexType name=\"test_polygonType\">\n" +
|
|
732
|
+
" <xsd:complexContent>\n" +
|
|
733
|
+
" <xsd:extension base=\"gml:AbstractFeatureType\">\n" +
|
|
734
|
+
" <xsd:sequence>\n" +
|
|
735
|
+
" <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"id\" nillable=\"true\" type=\"xsd:long\"/>\n" +
|
|
736
|
+
" <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"geom\" nillable=\"true\" type=\"gml:SurfacePropertyType\"/>\n" +
|
|
737
|
+
" <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"hochkomma\" nillable=\"true\" type=\"xsd:string\"/>\n" +
|
|
738
|
+
" </xsd:sequence>\n" +
|
|
739
|
+
" </xsd:extension>\n" +
|
|
740
|
+
" </xsd:complexContent>\n" +
|
|
741
|
+
" </xsd:complexType>\n" +
|
|
742
|
+
" <xsd:element name=\"test_polygon\" substitutionGroup=\"gml:AbstractFeature\" type=\"test:test_polygonType\"/>\n" +
|
|
743
|
+
"</xsd:schema>";
|
|
744
|
+
|
|
745
|
+
it("should receive the possible properties", async () => {
|
|
746
|
+
fetch.mockResponseOnce(exampleDescribeFeatureType);
|
|
747
|
+
|
|
748
|
+
const properties = await wfs.receivePossibleProperties("https://team-waas-was-here.lgln", "1.1.0", "wfstpolygon", false),
|
|
749
|
+
exampleProperties = [
|
|
750
|
+
{
|
|
751
|
+
key: "name",
|
|
752
|
+
label: "name",
|
|
753
|
+
required: false,
|
|
754
|
+
type: "string",
|
|
755
|
+
value: null
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
key: "nummer",
|
|
759
|
+
label: "nummer",
|
|
760
|
+
required: false,
|
|
761
|
+
type: "integer",
|
|
762
|
+
value: null
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
key: "bemerkung",
|
|
766
|
+
label: "bemerkung",
|
|
767
|
+
required: false,
|
|
768
|
+
type: "string",
|
|
769
|
+
value: null
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
key: "datum",
|
|
773
|
+
label: "datum",
|
|
774
|
+
required: false,
|
|
775
|
+
type: "date",
|
|
776
|
+
value: null
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
key: "geom",
|
|
780
|
+
label: "geom",
|
|
781
|
+
required: false,
|
|
782
|
+
type: "geometry",
|
|
783
|
+
value: null
|
|
784
|
+
}
|
|
785
|
+
];
|
|
786
|
+
|
|
787
|
+
expect(Array.isArray(properties)).toBe(true);
|
|
788
|
+
expect(properties.length).toEqual(5);
|
|
789
|
+
exampleProperties.forEach(property => {
|
|
790
|
+
const parsedProperty = properties.find(({key}) => key === property.key);
|
|
791
|
+
|
|
792
|
+
expect(parsedProperty).not.toEqual(undefined);
|
|
793
|
+
expect(parsedProperty).toEqual(property);
|
|
794
|
+
});
|
|
795
|
+
expect(fetch).toHaveBeenCalledWith("https://team-waas-was-here.lgln?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=wfstpolygon", {"responseType": "text/xml", "credentials": "omit"});
|
|
796
|
+
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it("should receive the possible properties from a geoserver", async () => {
|
|
800
|
+
fetch.mockResponseOnce(geoserverDescribeFeatureType);
|
|
801
|
+
|
|
802
|
+
const properties = await wfs.receivePossibleProperties("https://team-waas-was-here.lgln", "1.1.0", "test_polygon", false),
|
|
803
|
+
exampleProperties = [
|
|
804
|
+
{
|
|
805
|
+
key: "id",
|
|
806
|
+
label: "id",
|
|
807
|
+
required: false,
|
|
808
|
+
type: "integer",
|
|
809
|
+
value: null
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
key: "geom",
|
|
813
|
+
label: "geom",
|
|
814
|
+
required: false,
|
|
815
|
+
type: "geometry",
|
|
816
|
+
value: null
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
key: "hochkomma",
|
|
820
|
+
label: "hochkomma",
|
|
821
|
+
required: false,
|
|
822
|
+
type: "string",
|
|
823
|
+
value: null
|
|
824
|
+
}
|
|
825
|
+
];
|
|
826
|
+
|
|
827
|
+
expect(Array.isArray(properties)).toBe(true);
|
|
828
|
+
expect(properties.length).toEqual(3);
|
|
829
|
+
exampleProperties.forEach(property => {
|
|
830
|
+
const parsedProperty = properties.find(({key}) => key === property.key);
|
|
831
|
+
|
|
832
|
+
expect(parsedProperty).not.toEqual(undefined);
|
|
833
|
+
expect(parsedProperty).toEqual(property);
|
|
834
|
+
});
|
|
835
|
+
expect(fetch).toHaveBeenCalledWith("https://team-waas-was-here.lgln?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=test_polygon", {"responseType": "text/xml", "credentials": "omit"});
|
|
836
|
+
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it("should receive the possible properties with credentials", async () => {
|
|
840
|
+
fetch.mockResponseOnce(exampleDescribeFeatureType);
|
|
841
|
+
|
|
842
|
+
const properties = await wfs.receivePossibleProperties("https://team-waas-was-here.lgln", "1.1.0", "wfstpolygon", true),
|
|
843
|
+
exampleProperties = [
|
|
844
|
+
{
|
|
845
|
+
key: "name",
|
|
846
|
+
label: "name",
|
|
847
|
+
required: false,
|
|
848
|
+
type: "string",
|
|
849
|
+
value: null
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
key: "nummer",
|
|
853
|
+
label: "nummer",
|
|
854
|
+
required: false,
|
|
855
|
+
type: "integer",
|
|
856
|
+
value: null
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
key: "bemerkung",
|
|
860
|
+
label: "bemerkung",
|
|
861
|
+
required: false,
|
|
862
|
+
type: "string",
|
|
863
|
+
value: null
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
key: "datum",
|
|
867
|
+
label: "datum",
|
|
868
|
+
required: false,
|
|
869
|
+
type: "date",
|
|
870
|
+
value: null
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
key: "geom",
|
|
874
|
+
label: "geom",
|
|
875
|
+
required: false,
|
|
876
|
+
type: "geometry",
|
|
877
|
+
value: null
|
|
878
|
+
}
|
|
879
|
+
];
|
|
880
|
+
|
|
881
|
+
expect(Array.isArray(properties)).toBe(true);
|
|
882
|
+
expect(properties.length).toEqual(5);
|
|
883
|
+
exampleProperties.forEach(property => {
|
|
884
|
+
const parsedProperty = properties.find(({key}) => key === property.key);
|
|
885
|
+
|
|
886
|
+
expect(parsedProperty).not.toEqual(undefined);
|
|
887
|
+
expect(parsedProperty).toEqual(property);
|
|
888
|
+
});
|
|
889
|
+
expect(fetch).toHaveBeenCalledWith("https://team-waas-was-here.lgln?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=wfstpolygon", {"responseType": "text/xml", "credentials": "include"});
|
|
890
|
+
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it("should throw an error if the properties can not be fetched", async () => {
|
|
894
|
+
fetch.mockAbortOnce();
|
|
895
|
+
|
|
896
|
+
await expect(async () => {
|
|
897
|
+
await wfs.receivePossibleProperties("https://team-waas-was-here.lgln", "1.1.0", "wfstgeom", false);
|
|
898
|
+
}).rejects.toThrow(Error);
|
|
899
|
+
});
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
describe("sendTransaction", () => {
|
|
903
|
+
const layer = {
|
|
904
|
+
id: Symbol("layerId"),
|
|
905
|
+
url: "some.good.url",
|
|
906
|
+
isSecured: false,
|
|
907
|
+
featureNS: "wfsgeom",
|
|
908
|
+
featurePrefix: "test",
|
|
909
|
+
featureType: "Polygon",
|
|
910
|
+
version: "1.1.0"
|
|
911
|
+
},
|
|
912
|
+
feature = new Feature({
|
|
913
|
+
geometry: new Polygon([
|
|
914
|
+
[
|
|
915
|
+
[
|
|
916
|
+
9.17782024967994,
|
|
917
|
+
50.20836600730087
|
|
918
|
+
],
|
|
919
|
+
[
|
|
920
|
+
9.200676227149245,
|
|
921
|
+
50.20836600730087
|
|
922
|
+
],
|
|
923
|
+
[
|
|
924
|
+
9.200676227149245,
|
|
925
|
+
50.20873353776312
|
|
926
|
+
],
|
|
927
|
+
[
|
|
928
|
+
9.17782024967994,
|
|
929
|
+
50.20873353776312
|
|
930
|
+
],
|
|
931
|
+
[
|
|
932
|
+
9.17782024967994,
|
|
933
|
+
50.20836600730087
|
|
934
|
+
]
|
|
935
|
+
]]),
|
|
936
|
+
name: "My Polygon"
|
|
937
|
+
}),
|
|
938
|
+
srsName = "EPSG:4326",
|
|
939
|
+
url = "https://team-waas-was-here.lgln",
|
|
940
|
+
insertTransaction = "insert",
|
|
941
|
+
updateTransaction = "selectedUpdate",
|
|
942
|
+
deleteTransaction = "delete",
|
|
943
|
+
fetchResponseInsert = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
944
|
+
"<wfs:TransactionResponse xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\" xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns:ogc=\"http://www.opengis.net/ogc\" version=\"1.1.0\">\n" +
|
|
945
|
+
" <wfs:TransactionSummary>\n" +
|
|
946
|
+
" <wfs:totalInserted>1</wfs:totalInserted>\n" +
|
|
947
|
+
" <wfs:totalUpdated>0</wfs:totalUpdated>\n" +
|
|
948
|
+
" <wfs:totalDeleted>0</wfs:totalDeleted>\n" +
|
|
949
|
+
" </wfs:TransactionSummary>\n" +
|
|
950
|
+
" <wfs:InsertResults>\n" +
|
|
951
|
+
" <wfs:Feature>\n" +
|
|
952
|
+
" <ogc:FeatureId fid=\"wfst.59\"/>\n" +
|
|
953
|
+
" </wfs:Feature>\n" +
|
|
954
|
+
" </wfs:InsertResults>\n" +
|
|
955
|
+
"</wfs:TransactionResponse>",
|
|
956
|
+
fetchResponseUpdate = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
957
|
+
"<wfs:TransactionResponse xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\" xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns:ogc=\"http://www.opengis.net/ogc\" version=\"1.1.0\">\n" +
|
|
958
|
+
" <wfs:TransactionSummary>\n" +
|
|
959
|
+
" <wfs:totalInserted>0</wfs:totalInserted>\n" +
|
|
960
|
+
" <wfs:totalUpdated>1</wfs:totalUpdated>\n" +
|
|
961
|
+
" <wfs:totalDeleted>0</wfs:totalDeleted>\n" +
|
|
962
|
+
" </wfs:TransactionSummary>\n" +
|
|
963
|
+
"</wfs:TransactionResponse>",
|
|
964
|
+
fetchResponseDelete = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
965
|
+
"<wfs:TransactionResponse xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\" xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns:ogc=\"http://www.opengis.net/ogc\" version=\"1.1.0\">\n" +
|
|
966
|
+
" <wfs:TransactionSummary>\n" +
|
|
967
|
+
" <wfs:totalInserted>0</wfs:totalInserted>\n" +
|
|
968
|
+
" <wfs:totalUpdated>0</wfs:totalUpdated>\n" +
|
|
969
|
+
" <wfs:totalDeleted>1</wfs:totalDeleted>\n" +
|
|
970
|
+
" </wfs:TransactionSummary>\n" +
|
|
971
|
+
"</wfs:TransactionResponse>";
|
|
972
|
+
|
|
973
|
+
it("should return the inserted feature if the transaction was successful", async function () {
|
|
974
|
+
fetch.mockResponseOnce(fetchResponseInsert);
|
|
975
|
+
|
|
976
|
+
expect(await wfs.sendTransaction(srsName, feature, url, layer, insertTransaction)).toEqual(feature);
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it("should return the inserted feature if the transaction was successful", async function () {
|
|
980
|
+
fetch.mockResponseOnce(fetchResponseInsert);
|
|
981
|
+
layer.isSecured = true;
|
|
982
|
+
|
|
983
|
+
expect(await wfs.sendTransaction(srsName, feature, url, layer, insertTransaction)).toEqual(feature);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
it("should return the updated feature if the transaction was successful", async function () {
|
|
987
|
+
fetch.mockResponseOnce(fetchResponseUpdate);
|
|
988
|
+
feature.setId(1);
|
|
989
|
+
|
|
990
|
+
expect(await wfs.sendTransaction(srsName, feature, url, layer, updateTransaction)).toEqual(feature);
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
it("should return the deleted feature if the transaction was successful", async function () {
|
|
994
|
+
fetch.mockResponseOnce(fetchResponseDelete);
|
|
995
|
+
feature.setId(1);
|
|
996
|
+
|
|
997
|
+
expect(await wfs.sendTransaction(srsName, feature, url, layer, deleteTransaction)).toEqual(feature);
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
it("should throw an error if fetch fails", async function () {
|
|
1001
|
+
fetch.mockAbortOnce();
|
|
1002
|
+
|
|
1003
|
+
await expect(async () => wfs.sendTransaction(srsName, feature, url, layer, insertTransaction)).rejects.toThrow(Error);
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it("should throw an error if transaction type is not insert, selectedUpdate or delete", async function () {
|
|
1007
|
+
fetch.mockResponseOnce(fetchResponseDelete);
|
|
1008
|
+
feature.setId(1);
|
|
1009
|
+
|
|
1010
|
+
await expect(async () => wfs.sendTransaction(srsName, feature, url, layer, "löschen")).rejects.toThrow(Error);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it("should send a request and show an alert with a generic error message if no transactionsSummary is present in the XML response", async () => {
|
|
1014
|
+
const failedResponse = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
1015
|
+
"<ows:ExceptionReport xmlns:ows=\"http://www.opengis.net/ows\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd\" version=\"1.0.0\">\n" +
|
|
1016
|
+
" <ows:Exception" +
|
|
1017
|
+
" <ows:ExceptionText>Cannot perform insert operation: Error in XML document (line: 1, column: 234, character offset: 233): Feature type \"{wrong}wfst\" is unknown.</ows:ExceptionText>\n" +
|
|
1018
|
+
" </ows:Exception>\n" +
|
|
1019
|
+
"</ows:ExceptionReport>";
|
|
1020
|
+
|
|
1021
|
+
fetch.mockResponseOnce(failedResponse);
|
|
1022
|
+
|
|
1023
|
+
await expect(async () => wfs.sendTransaction(srsName, feature, url, layer, insertTransaction)).rejects.toThrow(Error);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it("should send a request, show an alert with a generic error message if no transactionsSummary is present in the XML response and log an error if an exceptionText is present in the XML response", async () => {
|
|
1027
|
+
const failedResponse = "<?xml version='1.0' encoding='UTF-8'?>\n" +
|
|
1028
|
+
"<ows:ExceptionReport xmlns:ows=\"http://www.opengis.net/ows\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.opengis.net/ows http://schemas.opengis.net/ows/1.0.0/owsExceptionReport.xsd\" version=\"1.0.0\">\n" +
|
|
1029
|
+
" <ows:Exception>" +
|
|
1030
|
+
" <ows:ExceptionText>Cannot perform insert operation: Error in XML document (line: 1, column: 234, character offset: 233): Feature type \"{wrong}wfst\" is unknown.</ows:ExceptionText>\n" +
|
|
1031
|
+
" </ows:Exception>\n" +
|
|
1032
|
+
"</ows:ExceptionReport>";
|
|
1033
|
+
|
|
1034
|
+
fetch.mockResponseOnce(failedResponse);
|
|
1035
|
+
|
|
1036
|
+
await expect(async () => wfs.sendTransaction(srsName, feature, url, layer, insertTransaction)).rejects.toThrow("Cannot perform insert operation: Error in XML document (line: 1, column: 234, character offset: 233): Feature type \"{wrong}wfst\" is unknown.");
|
|
1037
|
+
});
|
|
416
1038
|
});
|
|
1039
|
+
|
|
417
1040
|
describe("loadFeaturesManually", function () {
|
|
418
|
-
const consoleError = console.error;
|
|
419
1041
|
let tick;
|
|
420
1042
|
|
|
421
1043
|
beforeEach(() => {
|
|
@@ -429,10 +1051,6 @@ describe("wfs.js", function () {
|
|
|
429
1051
|
global.fetch.mockClear();
|
|
430
1052
|
}
|
|
431
1053
|
});
|
|
432
|
-
// to reset show error on load in console
|
|
433
|
-
afterEach(() => {
|
|
434
|
-
console.error = consoleError;
|
|
435
|
-
});
|
|
436
1054
|
|
|
437
1055
|
it("load features manually", async function () {
|
|
438
1056
|
global.fetch = jest.fn().mockImplementationOnce(() => {
|
|
@@ -468,3 +1086,4 @@ describe("wfs.js", function () {
|
|
|
468
1086
|
});
|
|
469
1087
|
});
|
|
470
1088
|
|
|
1089
|
+
|