@geogirafe/lib-geoportal 1.1.0-dev.2402672760 → 1.1.0-dev.2407038460
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/models/serverwfs.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ declare class ServerWfs<WfsXmlTypes = XmlTypes> {
|
|
|
12
12
|
};
|
|
13
13
|
initialized: boolean;
|
|
14
14
|
constructor(name: string, url: string);
|
|
15
|
+
addLayer(layer: string): void;
|
|
16
|
+
removeLayer(layer: string): void;
|
|
15
17
|
addLayerAttribute(layer: string, name: string, type: string): void;
|
|
16
18
|
getGeometryColumnNameToFeatureTypes(featureTypes: string[]): Record<string, string[]>;
|
|
17
19
|
}
|
package/models/serverwfs.js
CHANGED
|
@@ -9,11 +9,17 @@ class ServerWfs {
|
|
|
9
9
|
this.url = url;
|
|
10
10
|
this.initialized = false;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
if (!
|
|
14
|
-
// Layer does not exists yet
|
|
12
|
+
addLayer(layer) {
|
|
13
|
+
if (!this.layers[layer]) {
|
|
15
14
|
this.layers[layer] = [];
|
|
16
15
|
}
|
|
16
|
+
}
|
|
17
|
+
removeLayer(layer) {
|
|
18
|
+
delete this.layers[layer];
|
|
19
|
+
delete this.featureTypeToGeometryColumnName[layer];
|
|
20
|
+
}
|
|
21
|
+
addLayerAttribute(layer, name, type) {
|
|
22
|
+
this.addLayer(layer);
|
|
17
23
|
this.layers[layer].push({
|
|
18
24
|
name: name,
|
|
19
25
|
type: type
|
package/package.json
CHANGED
package/service-worker.js
CHANGED
|
@@ -60,8 +60,9 @@ function handleMessage(event) {
|
|
|
60
60
|
audience = data.audience ?? [];
|
|
61
61
|
log(`audience changed: ${audience}`);
|
|
62
62
|
}
|
|
63
|
+
audienceExcludedPaths = [];
|
|
63
64
|
if (data.audienceExcludedPaths) {
|
|
64
|
-
audienceExcludedPaths = data.audienceExcludedPaths
|
|
65
|
+
audienceExcludedPaths = data.audienceExcludedPaths.map((str) => new RegExp(str));
|
|
65
66
|
log(`audienceExcludedPaths changed: ${audienceExcludedPaths}`);
|
|
66
67
|
}
|
|
67
68
|
if (data.access_token) {
|
|
@@ -253,6 +254,6 @@ async function openIndexedDB() {
|
|
|
253
254
|
}
|
|
254
255
|
function log(str, error) {
|
|
255
256
|
if (logLevel === 'debug') {
|
|
256
|
-
console.debug(`SW: ${str}`, error);
|
|
257
|
+
console.debug(`SW: ${str}`, error ?? '');
|
|
257
258
|
}
|
|
258
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.1.0-dev.
|
|
1
|
+
{"version":"1.1.0-dev.2407038460", "build":"2407038460", "date":"25/03/2026"}
|
package/tools/wfs/wfsclient.d.ts
CHANGED
|
@@ -32,9 +32,10 @@ export default class WfsClient<WfsXmlTypes = XmlTypes> {
|
|
|
32
32
|
private configMaxFeatures;
|
|
33
33
|
getServerWfs(): Promise<ServerWfs<WfsXmlTypes>>;
|
|
34
34
|
protected describeFeatureType(): Promise<ServerWfs<WfsXmlTypes>>;
|
|
35
|
-
private
|
|
35
|
+
private requestDescribeFeatureType;
|
|
36
|
+
private parseDescribeFeatureTypeResponse;
|
|
36
37
|
private initializeAttribute;
|
|
37
|
-
protected manageLayerAttribute(serverWfs: ServerWfs<WfsXmlTypes>, element: Element, featureType: string):
|
|
38
|
+
protected manageLayerAttribute(serverWfs: ServerWfs<WfsXmlTypes>, element: Element, featureType: string): void;
|
|
38
39
|
protected validateLayerAttributeType(type: string): boolean;
|
|
39
40
|
protected getElementToTypeName(xml: Document): Record<string, string>;
|
|
40
41
|
protected getDescribeFeatureTypeUrl(): string;
|
package/tools/wfs/wfsclient.js
CHANGED
|
@@ -48,7 +48,7 @@ export default class WfsClient {
|
|
|
48
48
|
}
|
|
49
49
|
describeFeatureType() {
|
|
50
50
|
if (!this.serverWfs) {
|
|
51
|
-
this.serverWfs = this.
|
|
51
|
+
this.serverWfs = this.parseDescribeFeatureTypeResponse();
|
|
52
52
|
this.serverWfs.catch((error) => {
|
|
53
53
|
const msg = `WFS server with URL ${this.wfsUrl} could not be initialized. Error: `;
|
|
54
54
|
console.error(msg, error);
|
|
@@ -57,12 +57,15 @@ export default class WfsClient {
|
|
|
57
57
|
}
|
|
58
58
|
return this.serverWfs;
|
|
59
59
|
}
|
|
60
|
-
async
|
|
61
|
-
const serverWfs = new ServerWfs('', this.wfsUrl);
|
|
60
|
+
async requestDescribeFeatureType() {
|
|
62
61
|
const url = this.getDescribeFeatureTypeUrl();
|
|
63
62
|
const response = await fetch(url);
|
|
64
63
|
const content = await response.text();
|
|
65
|
-
|
|
64
|
+
return new DOMParser().parseFromString(content, 'text/xml');
|
|
65
|
+
}
|
|
66
|
+
async parseDescribeFeatureTypeResponse() {
|
|
67
|
+
const serverWfs = new ServerWfs('', this.wfsUrl);
|
|
68
|
+
const xml = await this.requestDescribeFeatureType();
|
|
66
69
|
// First, find all direct "element" children
|
|
67
70
|
const elementTypeToName = this.getElementToTypeName(xml);
|
|
68
71
|
// Then, find all "complexType" elements
|
|
@@ -83,48 +86,39 @@ export default class WfsClient {
|
|
|
83
86
|
}
|
|
84
87
|
const featureType = elementTypeToName[typeName];
|
|
85
88
|
const elements = tag.getElementsByTagName('sequence')[0].getElementsByTagName('element');
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
try {
|
|
90
|
+
serverWfs.addLayer(featureType);
|
|
91
|
+
for (const element of elements) {
|
|
92
|
+
this.manageLayerAttribute(serverWfs, element, featureType);
|
|
93
|
+
}
|
|
94
|
+
// If we didn't find any geometry attribute for this featureType, wfs querying won't be possible
|
|
95
|
+
if (!serverWfs.featureTypeToGeometryColumnName[featureType]) {
|
|
96
|
+
throw new Error(`No Geometry column for the type ${featureType}`);
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
throw new Error('No Geometry column for the type ' + featureType);
|
|
99
|
+
catch (error) {
|
|
100
|
+
serverWfs.removeLayer(featureType);
|
|
101
|
+
console.error(`Error while parsing feature type ${typeName}: ${error}`);
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
manageLayerAttribute(serverWfs, element, featureType) {
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (geometryAttributeName) {
|
|
105
|
-
serverWfs.featureTypeToGeometryColumnName[featureType] = geometryAttributeName;
|
|
106
|
-
geometryAttributeFound = true;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
throw new Error('Why is geometryAttributeName null here ?');
|
|
110
|
-
}
|
|
105
|
+
const attributeType = element.getAttribute('type');
|
|
106
|
+
const attributeName = element.getAttribute('name');
|
|
107
|
+
if (!attributeName || !attributeType) {
|
|
108
|
+
console.warn(`Error while loading attributes for layer ${featureType}. Attribute name and/or type is missing. Element ignored.`);
|
|
109
|
+
return;
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
serverWfs.addLayerAttribute(featureType, attrName, attrType);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
console.warn(`Unmanaged layer attribute type: ${attrType} for attribute ${attrName} of featureType ${featureType}. ${attrName} ignored.`);
|
|
125
|
-
}
|
|
111
|
+
// Handle the geometry attribute
|
|
112
|
+
if (attributeType.startsWith('gml:')) {
|
|
113
|
+
serverWfs.featureTypeToGeometryColumnName[featureType] = attributeName;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Handle all remaining regular attributes
|
|
117
|
+
if (!this.validateLayerAttributeType(attributeType)) {
|
|
118
|
+
console.warn(`Unmanaged layer attribute type: ${attributeType} for attribute ${attributeName} of featureType ${featureType}. ${attributeName} ignored.`);
|
|
119
|
+
return;
|
|
126
120
|
}
|
|
127
|
-
|
|
121
|
+
serverWfs.addLayerAttribute(featureType, attributeName, attributeType);
|
|
128
122
|
}
|
|
129
123
|
validateLayerAttributeType(type) {
|
|
130
124
|
return xmlTypesStrList.includes(type);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
import { vi, afterAll,
|
|
2
|
+
import { vi, afterAll, describe, expect, it, beforeEach } from 'vitest';
|
|
3
3
|
import { WfsClientMapServer } from './wfsclient';
|
|
4
4
|
import ServerOgc from '../../models/serverogc';
|
|
5
5
|
import MockHelper from '../tests/mockhelper';
|
|
@@ -9,8 +9,9 @@ describe('WfsClient', () => {
|
|
|
9
9
|
let context;
|
|
10
10
|
let server;
|
|
11
11
|
let client;
|
|
12
|
-
|
|
12
|
+
beforeEach(() => {
|
|
13
13
|
context = MockHelper.startMocking();
|
|
14
|
+
vi.resetAllMocks();
|
|
14
15
|
server = new ServerOgc('testOgcServer', {
|
|
15
16
|
url: 'https://wms-1.test.url',
|
|
16
17
|
wfsSupport: true,
|
|
@@ -116,4 +117,82 @@ describe('WfsClient', () => {
|
|
|
116
117
|
expect(completeOptions[1].geometryName).toBe('geom');
|
|
117
118
|
});
|
|
118
119
|
});
|
|
120
|
+
describe('describeFeatureType', () => {
|
|
121
|
+
const xmlResponse = `
|
|
122
|
+
<schema
|
|
123
|
+
targetNamespace="http://mapserver.gis.umn.edu/mapserver"
|
|
124
|
+
xmlns:ms="http://mapserver.gis.umn.edu/mapserver"
|
|
125
|
+
xmlns:ogc="http://www.opengis.net/ogc"
|
|
126
|
+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
|
127
|
+
xmlns="http://www.w3.org/2001/XMLSchema"
|
|
128
|
+
xmlns:gml="http://www.opengis.net/gml"
|
|
129
|
+
elementFormDefault="qualified" version="0.1" >
|
|
130
|
+
<import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd" />
|
|
131
|
+
|
|
132
|
+
<element name="testlayer1" type="ms:testlayer1Type" substitutionGroup="gml:_Feature" />
|
|
133
|
+
<complexType name="testlayer1Type">
|
|
134
|
+
<complexContent>
|
|
135
|
+
<extension base="gml:AbstractFeatureType">
|
|
136
|
+
<sequence>
|
|
137
|
+
<element name="geom" type="gml:SurfacePropertyType" minOccurs="0" maxOccurs="1"/>
|
|
138
|
+
<element name="text_de" minOccurs="0" type="string"/>
|
|
139
|
+
</sequence>
|
|
140
|
+
</extension>
|
|
141
|
+
</complexContent>
|
|
142
|
+
</complexType>
|
|
143
|
+
</schema>
|
|
144
|
+
`;
|
|
145
|
+
const mockDescribeFeatureRequest = async (xmlResponse) => {
|
|
146
|
+
const describeFeatureTypeResponse = new DOMParser().parseFromString(xmlResponse, 'application/xml');
|
|
147
|
+
// @ts-ignore
|
|
148
|
+
vi.spyOn(client, 'requestDescribeFeatureType').mockReturnValue(
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
new Promise((resolve) => resolve(describeFeatureTypeResponse)));
|
|
151
|
+
};
|
|
152
|
+
it('registers the features type described in the response', async () => {
|
|
153
|
+
await mockDescribeFeatureRequest(xmlResponse);
|
|
154
|
+
const server = await client.getServerWfs();
|
|
155
|
+
expect(server.layers).toBeDefined();
|
|
156
|
+
expect(Object.keys(server.layers)).toContain('testlayer1');
|
|
157
|
+
expect(server.layers['testlayer1']).toEqual([{ name: 'text_de', type: 'string' }]);
|
|
158
|
+
});
|
|
159
|
+
it('registers the feature type even if it does not have any attributes except a geometry', async () => {
|
|
160
|
+
const xmlNoAttributes = xmlResponse.replace('<element name="text_de" minOccurs="0" type="string"/>', '');
|
|
161
|
+
await mockDescribeFeatureRequest(xmlNoAttributes);
|
|
162
|
+
const server = await client.getServerWfs();
|
|
163
|
+
expect(server.layers).toBeDefined();
|
|
164
|
+
expect(Object.keys(server.layers)).toContain('testlayer1');
|
|
165
|
+
expect(server.layers['testlayer1']).toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
it('ignores an attribute if its type is missing', async () => {
|
|
168
|
+
const xmlNoType = xmlResponse.replace('<element name="text_de" minOccurs="0" type="string"/>', '<element name="text_de" minOccurs="0"/>');
|
|
169
|
+
await mockDescribeFeatureRequest(xmlNoType);
|
|
170
|
+
const server = await client.getServerWfs();
|
|
171
|
+
expect(server.layers).toBeDefined();
|
|
172
|
+
expect(Object.keys(server.layers)).toContain('testlayer1');
|
|
173
|
+
expect(server.layers['testlayer1']).toEqual([]);
|
|
174
|
+
});
|
|
175
|
+
it('ignores an attribute if its name is missing', async () => {
|
|
176
|
+
const xmlNoAttributeName = xmlResponse.replace('<element name="text_de" minOccurs="0" type="string"/>', '<element minOccurs="0" type="string"/>');
|
|
177
|
+
await mockDescribeFeatureRequest(xmlNoAttributeName);
|
|
178
|
+
const server = await client.getServerWfs();
|
|
179
|
+
expect(server.layers).toBeDefined();
|
|
180
|
+
expect(Object.keys(server.layers)).toContain('testlayer1');
|
|
181
|
+
expect(server.layers['testlayer1']).toEqual([]);
|
|
182
|
+
});
|
|
183
|
+
it('ignores the feature type if it does not contain a geometry attribute', async () => {
|
|
184
|
+
const xmlNoGeometry = xmlResponse.replace('<element name="geom" type="gml:SurfacePropertyType" minOccurs="0" maxOccurs="1"/>', '');
|
|
185
|
+
await mockDescribeFeatureRequest(xmlNoGeometry);
|
|
186
|
+
const server = await client.getServerWfs();
|
|
187
|
+
expect(server.layers).toBeDefined();
|
|
188
|
+
expect(Object.keys(server.layers)).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
it('ignores the feature type if the geometry attribute has no geometry name', async () => {
|
|
191
|
+
const xmlNoGeometryName = xmlResponse.replace('<element name="geom" type="gml:SurfacePropertyType" minOccurs="0" maxOccurs="1"/>', '<element type="gml:SurfacePropertyType" minOccurs="0" maxOccurs="1"/>');
|
|
192
|
+
await mockDescribeFeatureRequest(xmlNoGeometryName);
|
|
193
|
+
const server = await client.getServerWfs();
|
|
194
|
+
expect(server.layers).toBeDefined();
|
|
195
|
+
expect(Object.keys(server.layers)).toEqual([]);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
119
198
|
});
|