geoserver-node-client 0.0.2 → 0.0.6
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/.github/workflows/ci-geoserver-node-client.yml +2 -2
- package/DOCS_HOME.md +4 -2
- package/LICENSE +25 -0
- package/README.md +33 -3
- package/demo/index.js +19 -2
- package/geoserver-rest-client.js +5 -2
- package/package.json +8 -8
- package/src/datastore.js +12 -3
- package/src/layer.js +4 -2
- package/src/namespace.js +142 -0
- package/test/test.js +38 -0
- package/demo/dev.js +0 -275
|
@@ -10,7 +10,7 @@ jobs:
|
|
|
10
10
|
run-tests-maintenance:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
env:
|
|
13
|
-
GEOSERVER_VERSION: 2.
|
|
13
|
+
GEOSERVER_VERSION: 2.19.3
|
|
14
14
|
steps:
|
|
15
15
|
- uses: actions/checkout@v2
|
|
16
16
|
|
|
@@ -33,7 +33,7 @@ jobs:
|
|
|
33
33
|
run-tests-stable:
|
|
34
34
|
runs-on: ubuntu-latest
|
|
35
35
|
env:
|
|
36
|
-
GEOSERVER_VERSION: 2.
|
|
36
|
+
GEOSERVER_VERSION: 2.20.1
|
|
37
37
|
steps:
|
|
38
38
|
- uses: actions/checkout@v2
|
|
39
39
|
|
package/DOCS_HOME.md
CHANGED
|
@@ -8,8 +8,10 @@ Node.js / JavaScript Client for the [GeoServer REST API](https://docs.geoserver.
|
|
|
8
8
|
|
|
9
9
|
Compatible with [GeoServer](https://geoserver.org)
|
|
10
10
|
|
|
11
|
-
- v2.
|
|
12
|
-
- v2.
|
|
11
|
+
- v2.20.x
|
|
12
|
+
- v2.19.x
|
|
13
|
+
- v2.18.x (no more maintained and officially deprecated)
|
|
14
|
+
- v2.17.x (no more maintained and officially deprecated)
|
|
13
15
|
|
|
14
16
|
### Who do I talk to? ###
|
|
15
17
|
|
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021, meggsimum (Dipl.-Ing. (FH) Christian Mayer)
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# GeoServer Node Client
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/geoserver-node-client)
|
|
4
|
+

|
|
5
|
+
|
|
3
6
|
Node.js / JavaScript Client for the [GeoServer REST API](https://docs.geoserver.org/stable/en/user/rest/).
|
|
4
7
|
|
|
5
8
|
**CAUTION: This is highly bleeding edge, heavily under development and therefore breaking changes can be made at every time!**
|
|
@@ -12,8 +15,9 @@ Detailed API-Docs can be found [here](https://meggsimum.github.io/geoserver-node
|
|
|
12
15
|
|
|
13
16
|
Compatible with [GeoServer](https://geoserver.org)
|
|
14
17
|
|
|
18
|
+
- v2.20.x
|
|
15
19
|
- v2.19.x
|
|
16
|
-
- v2.18.x
|
|
20
|
+
- v2.18.x (no more maintained and officially deprecated)
|
|
17
21
|
- v2.17.x (no more maintained and officially deprecated)
|
|
18
22
|
|
|
19
23
|
### Setup
|
|
@@ -39,15 +43,41 @@ First start a GeoServer, e.g. by using this Docker container:
|
|
|
39
43
|
docker run \
|
|
40
44
|
-p 8080:8080 \
|
|
41
45
|
-v /path/to/geoserver_mnt:/opt/geoserver_data \
|
|
42
|
-
meggsimum/geoserver:2.
|
|
46
|
+
meggsimum/geoserver:2.20.1
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
Then, in an other terminal, run:
|
|
46
50
|
|
|
47
51
|
```shell
|
|
48
|
-
|
|
52
|
+
# specify the GeoServer version and run the test suite
|
|
53
|
+
GEOSERVER_VERSION=2.20.1 npm run test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Release
|
|
57
|
+
|
|
58
|
+
The release to GitHub and npm is done via [release-it](https://github.com/release-it/release-it). This is the workflow for releasing:
|
|
59
|
+
|
|
60
|
+
1. Make sure a `GITHUB_TOKEN` is available as environment variable. See [here](https://github.com/release-it/release-it/blob/master/docs/github-releases.md) for more information.
|
|
61
|
+
|
|
62
|
+
```shell
|
|
63
|
+
export GITHUB_TOKEN=ADD-YOUR-TOKEN-HERE
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
2. Make sure you are logged in to npm and ensure you have the rights to make a release.
|
|
67
|
+
|
|
68
|
+
```shell
|
|
69
|
+
npm login
|
|
70
|
+
# then enter your credentials
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
3. Locally checkout the latest `master` branch that you would like to release, then run:
|
|
74
|
+
|
|
75
|
+
```shell
|
|
76
|
+
npm run release
|
|
49
77
|
```
|
|
50
78
|
|
|
79
|
+
4. Follow the questions in the commandline.
|
|
80
|
+
|
|
51
81
|
### Who do I talk to? ###
|
|
52
82
|
|
|
53
83
|
* meggsimum (Christian Mayer) - info __at## meggsimum ~~dot** de
|
package/demo/index.js
CHANGED
|
@@ -18,6 +18,23 @@ grc.getVersion().then(versionInfo => {
|
|
|
18
18
|
console.log('GeoServer REST version info', prettyJson(versionInfo));
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
// NAMESPACES
|
|
22
|
+
|
|
23
|
+
// const nsPrefix = 'example-namespace';
|
|
24
|
+
// const nsUri = 'http://www.example.com'
|
|
25
|
+
// grc.namespaces.create(nsPrefix, nsUri).then(retVal => {
|
|
26
|
+
// console.log('Created GeoServer NS', prettyJson(retVal));
|
|
27
|
+
// });
|
|
28
|
+
// grc.namespaces.getAll().then(gsNamespaces => {
|
|
29
|
+
// console.log('GeoServer All NS', prettyJson(gsNamespaces));
|
|
30
|
+
// });
|
|
31
|
+
// grc.namespaces.get(nsPrefix).then(gsNamespaces => {
|
|
32
|
+
// console.log('GeoServer NS', prettyJson(gsNamespaces));
|
|
33
|
+
// });
|
|
34
|
+
// grc.namespaces.delete(nsPrefix).then(gsNamespaces => {
|
|
35
|
+
// console.log('Deleted GeoServer NS', prettyJson(gsNamespaces));
|
|
36
|
+
// });
|
|
37
|
+
|
|
21
38
|
// WORKSPACES
|
|
22
39
|
|
|
23
40
|
// grc.workspaces.getAll().then(gsWorkspaces => {
|
|
@@ -70,9 +87,9 @@ grc.getVersion().then(versionInfo => {
|
|
|
70
87
|
// grc.datastores.createWmsStore(ws, 'testWmsDs', wmsUrl).then(retVal => {
|
|
71
88
|
// console.log('Created WMS data store', prettyJson(retVal));
|
|
72
89
|
// });
|
|
73
|
-
// const wfsCapsUrl = 'https://
|
|
90
|
+
// const wfsCapsUrl = 'https://services.meggsimum.de/geoserver/ows?service=wfs&version=1.1.0&request=GetCapabilities';
|
|
74
91
|
// const namespaceUrl = 'http://test';
|
|
75
|
-
// grc.datastores.createWfsStore(ws, 'testWfsDs', wfsCapsUrl, namespaceUrl).then(retVal => {
|
|
92
|
+
// grc.datastores.createWfsStore(ws, 'testWfsDs', wfsCapsUrl, namespaceUrl, false).then(retVal => {
|
|
76
93
|
// console.log('Created WFS data store', prettyJson(retVal));
|
|
77
94
|
// });
|
|
78
95
|
// grc.datastores.deleteDataStore(ws, 'testWfsDs', false).then(gsWmsStores => {
|
package/geoserver-rest-client.js
CHANGED
|
@@ -6,6 +6,7 @@ import DatastoreClient from './src/datastore.js';
|
|
|
6
6
|
import ImageMosaicClient from './src/imagemosaic.js';
|
|
7
7
|
import SecurityClient from './src/security.js';
|
|
8
8
|
import SettingsClient from './src/settings.js';
|
|
9
|
+
import NamespaceClient from './src/namespace.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Client for GeoServer REST API.
|
|
@@ -33,6 +34,8 @@ export default class GeoServerRestClient {
|
|
|
33
34
|
this.styles = new StyleClient(this.url, this.user, this.password);
|
|
34
35
|
/** @member {WorkspaceClient} workspaces GeoServer REST client instance for workspaces */
|
|
35
36
|
this.workspaces = new WorkspaceClient(this.url, this.user, this.password);
|
|
37
|
+
/** @member {NamespaceClient} namespaces GeoServer REST client instance for namespaces */
|
|
38
|
+
this.namespaces = new NamespaceClient(this.url, this.user, this.password);
|
|
36
39
|
/** @member {DatastoreClient} datastores GeoServer REST client instance for data stores */
|
|
37
40
|
this.datastores = new DatastoreClient(this.url, this.user, this.password);
|
|
38
41
|
/** @member {ImageMosaicClient} imagemosaics GeoServer REST client instance for image mosaics */
|
|
@@ -45,7 +48,7 @@ export default class GeoServerRestClient {
|
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
50
|
* Get the GeoServer version.
|
|
48
|
-
*
|
|
51
|
+
*
|
|
49
52
|
* @returns {String|Boolean} The version of GeoServer or 'false'
|
|
50
53
|
*/
|
|
51
54
|
async getVersion () {
|
|
@@ -67,7 +70,7 @@ export default class GeoServerRestClient {
|
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* Checks if the configured GeoServer REST connection exists.
|
|
70
|
-
*
|
|
73
|
+
*
|
|
71
74
|
* @returns {Boolean} If the connection exists
|
|
72
75
|
*/
|
|
73
76
|
async exists () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "geoserver-node-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Node.js client for GeoServer REST API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "geoserver-rest-client.js",
|
|
@@ -20,18 +20,18 @@
|
|
|
20
20
|
"author": "C. Mayer, meggsimum (info_at*meggsimum?dot?de)",
|
|
21
21
|
"license": "BSD-2-Clause",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"node-fetch": "^2.6.
|
|
23
|
+
"node-fetch": "^2.6.5"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"chai": "^4.3.
|
|
27
|
-
"eslint": "^7.
|
|
26
|
+
"chai": "^4.3.4",
|
|
27
|
+
"eslint": "^7.32.0",
|
|
28
28
|
"eslint-config-standard": "^14.1.1",
|
|
29
|
-
"eslint-plugin-import": "^2.
|
|
29
|
+
"eslint-plugin-import": "^2.25.2",
|
|
30
30
|
"eslint-plugin-node": "^11.1.0",
|
|
31
31
|
"eslint-plugin-promise": "^4.3.1",
|
|
32
32
|
"eslint-plugin-standard": "^4.1.0",
|
|
33
|
-
"jsdoc": "^3.6.
|
|
34
|
-
"mocha": "^
|
|
35
|
-
"release-it": "^14.
|
|
33
|
+
"jsdoc": "^3.6.7",
|
|
34
|
+
"mocha": "^9.1.3",
|
|
35
|
+
"release-it": "^14.11.6"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/src/datastore.js
CHANGED
|
@@ -228,6 +228,7 @@ s */
|
|
|
228
228
|
* Creates a PostGIS based data store.
|
|
229
229
|
*
|
|
230
230
|
* @param {String} workspace The WS to create the data store in
|
|
231
|
+
* @param {String} namespaceUri The namespace URI of the workspace
|
|
231
232
|
* @param {String} dataStore The data store name to be created
|
|
232
233
|
* @param {String} pgHost The PostGIS DB host
|
|
233
234
|
* @param {String} pgPort The PostGIS DB port
|
|
@@ -239,12 +240,15 @@ s */
|
|
|
239
240
|
*
|
|
240
241
|
* @returns {Boolean} If the store could be created
|
|
241
242
|
*/
|
|
242
|
-
async createPostgisStore (workspace, dataStore, pgHost, pgPort, pgUser, pgPassword, pgSchema, pgDb, exposePk) {
|
|
243
|
+
async createPostgisStore (workspace, namespaceUri, dataStore, pgHost, pgPort, pgUser, pgPassword, pgSchema, pgDb, exposePk) {
|
|
243
244
|
const body = {
|
|
244
245
|
dataStore: {
|
|
245
246
|
name: dataStore,
|
|
246
247
|
type: 'PostGIS',
|
|
247
248
|
enabled: true,
|
|
249
|
+
workspace: {
|
|
250
|
+
name: workspace
|
|
251
|
+
},
|
|
248
252
|
connectionParameters: {
|
|
249
253
|
entry: [
|
|
250
254
|
{
|
|
@@ -273,7 +277,7 @@ s */
|
|
|
273
277
|
},
|
|
274
278
|
{
|
|
275
279
|
'@key': 'namespace',
|
|
276
|
-
$:
|
|
280
|
+
$: namespaceUri
|
|
277
281
|
},
|
|
278
282
|
{
|
|
279
283
|
'@key': 'user',
|
|
@@ -397,10 +401,11 @@ s */
|
|
|
397
401
|
* @param {String} dataStore The data store name
|
|
398
402
|
* @param {String} wfsCapabilitiesUrl WFS capabilities URL
|
|
399
403
|
* @param {String} namespaceUrl URL of the GeoServer namespace
|
|
404
|
+
* @param {Boolean} [useHttpConnectionPooling=true] use HTTP connection pooling for WFS connection
|
|
400
405
|
*
|
|
401
406
|
* @returns {Boolean} If store could be created
|
|
402
407
|
*/
|
|
403
|
-
async createWfsStore (workspace, dataStore, wfsCapabilitiesUrl, namespaceUrl) {
|
|
408
|
+
async createWfsStore (workspace, dataStore, wfsCapabilitiesUrl, namespaceUrl, useHttpConnectionPooling) {
|
|
404
409
|
const body = {
|
|
405
410
|
dataStore: {
|
|
406
411
|
name: dataStore,
|
|
@@ -414,6 +419,10 @@ s */
|
|
|
414
419
|
{
|
|
415
420
|
'@key': 'namespace',
|
|
416
421
|
$: namespaceUrl
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
'@key': 'WFSDataStoreFactory:USE_HTTP_CONNECTION_POOLING',
|
|
425
|
+
$: useHttpConnectionPooling !== false ? 'true' : 'false'
|
|
417
426
|
}
|
|
418
427
|
]
|
|
419
428
|
}
|
package/src/layer.js
CHANGED
|
@@ -369,10 +369,11 @@ export default class LayerClient {
|
|
|
369
369
|
* @param {String} defaultValue The default time value, e.g. 'MINIMUM' or 'MAXIMUM' or 'NEAREST' or 'FIXED'
|
|
370
370
|
* @param {Boolean} [nearestMatchEnabled] Enable nearest match
|
|
371
371
|
* @param {Boolean} [rawNearestMatchEnabled] Enable raw nearest match
|
|
372
|
+
* @param {String} [acceptableInterval] Acceptable interval for nearest match, e.g.'PT30M'
|
|
372
373
|
*
|
|
373
374
|
* @returns If time dimension could be enabled
|
|
374
375
|
*/
|
|
375
|
-
async enableTimeCoverage (workspace, dataStore, name, presentation, resolution, defaultValue, nearestMatchEnabled, rawNearestMatchEnabled) {
|
|
376
|
+
async enableTimeCoverage (workspace, dataStore, name, presentation, resolution, defaultValue, nearestMatchEnabled, rawNearestMatchEnabled, acceptableInterval) {
|
|
376
377
|
try {
|
|
377
378
|
const body = {
|
|
378
379
|
coverage: {
|
|
@@ -389,7 +390,8 @@ export default class LayerClient {
|
|
|
389
390
|
strategy: defaultValue
|
|
390
391
|
},
|
|
391
392
|
nearestMatchEnabled: nearestMatchEnabled,
|
|
392
|
-
rawNearestMatchEnabled: rawNearestMatchEnabled
|
|
393
|
+
rawNearestMatchEnabled: rawNearestMatchEnabled,
|
|
394
|
+
acceptableInterval: acceptableInterval
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
]
|
package/src/namespace.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client for GeoServer namespace
|
|
5
|
+
*
|
|
6
|
+
* @module NamespaceClient
|
|
7
|
+
*/
|
|
8
|
+
export default class NamespaceClient {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a GeoServer REST NamespaceClient instance.
|
|
11
|
+
*
|
|
12
|
+
* @param {String} url The URL of the GeoServer REST API endpoint
|
|
13
|
+
* @param {String} user The user for the GeoServer REST API
|
|
14
|
+
* @param {String} password The password for the GeoServer REST API
|
|
15
|
+
*/
|
|
16
|
+
constructor (url, user, password) {
|
|
17
|
+
this.url = url.endsWith('/') ? url : url + '/';
|
|
18
|
+
this.user = user;
|
|
19
|
+
this.password = password;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns all namespaces.
|
|
24
|
+
*
|
|
25
|
+
* @returns {Object|Boolean} An object describing the namespace or 'false'
|
|
26
|
+
*/
|
|
27
|
+
async getAll () {
|
|
28
|
+
try {
|
|
29
|
+
const auth =
|
|
30
|
+
Buffer.from(this.user + ':' + this.password).toString('base64');
|
|
31
|
+
const response = await fetch(this.url + 'namespaces.json', {
|
|
32
|
+
credentials: 'include',
|
|
33
|
+
method: 'GET',
|
|
34
|
+
headers: {
|
|
35
|
+
Authorization: 'Basic ' + auth
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const json = await response.json();
|
|
39
|
+
return json;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new namespace.
|
|
47
|
+
*
|
|
48
|
+
* @param {String} prefix Prefix of the new namespace
|
|
49
|
+
* @param {String} uri Uri of the new namespace
|
|
50
|
+
*
|
|
51
|
+
* @returns {String|Boolean} The name of the created namespace or 'false'
|
|
52
|
+
*/
|
|
53
|
+
async create (prefix, uri) {
|
|
54
|
+
try {
|
|
55
|
+
const body = {
|
|
56
|
+
namespace: {
|
|
57
|
+
prefix: prefix,
|
|
58
|
+
uri: uri
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const auth =
|
|
63
|
+
Buffer.from(this.user + ':' + this.password).toString('base64');
|
|
64
|
+
|
|
65
|
+
const response = await fetch(this.url + 'namespaces', {
|
|
66
|
+
credentials: 'include',
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: 'Basic ' + auth,
|
|
70
|
+
'Content-Type': 'application/json'
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify(body)
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (response.status === 201) {
|
|
76
|
+
const responseText = await response.text();
|
|
77
|
+
return responseText;
|
|
78
|
+
} else {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns a namespace.
|
|
88
|
+
*
|
|
89
|
+
* @param {String} name Name of the namespace
|
|
90
|
+
* @returns {Object|Boolean} An object describing the namespace or 'false'
|
|
91
|
+
*/
|
|
92
|
+
async get (name) {
|
|
93
|
+
try {
|
|
94
|
+
const auth =
|
|
95
|
+
Buffer.from(this.user + ':' + this.password).toString('base64');
|
|
96
|
+
const response = await fetch(this.url + 'namespaces/' + name + '.json', {
|
|
97
|
+
credentials: 'include',
|
|
98
|
+
method: 'GET',
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: 'Basic ' + auth
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
if (response.status === 200) {
|
|
104
|
+
return await response.json();
|
|
105
|
+
} else {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Deletes a namespace.
|
|
115
|
+
*
|
|
116
|
+
* @param {String} name Name of the namespace to delete
|
|
117
|
+
*
|
|
118
|
+
* @returns {Boolean} If deletion was successful
|
|
119
|
+
*/
|
|
120
|
+
async delete (name) {
|
|
121
|
+
try {
|
|
122
|
+
const auth =
|
|
123
|
+
Buffer.from(this.user + ':' + this.password).toString('base64');
|
|
124
|
+
const response = await fetch(this.url + 'namespaces/' + name, {
|
|
125
|
+
credentials: 'include',
|
|
126
|
+
method: 'DELETE',
|
|
127
|
+
headers: {
|
|
128
|
+
Authorization: 'Basic ' + auth
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// TODO map other HTTP status
|
|
133
|
+
if (response.status === 200) {
|
|
134
|
+
return true;
|
|
135
|
+
} else {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
package/test/test.js
CHANGED
|
@@ -9,6 +9,10 @@ const pw = 'geoserver';
|
|
|
9
9
|
const grc = new GeoServerRestClient(url, user, pw);
|
|
10
10
|
|
|
11
11
|
const workSpace = 'my-workspace';
|
|
12
|
+
|
|
13
|
+
const nameSpace = 'my-namespace';
|
|
14
|
+
const nameSpaceUri = 'http://www.example.com';
|
|
15
|
+
|
|
12
16
|
const geoServerVersion = process.env.GEOSERVER_VERSION;
|
|
13
17
|
|
|
14
18
|
describe('Basic GeoServer', () => {
|
|
@@ -126,6 +130,40 @@ describe('Workspace', () => {
|
|
|
126
130
|
});
|
|
127
131
|
});
|
|
128
132
|
|
|
133
|
+
describe('Namespace', () => {
|
|
134
|
+
it('has no namespaces', async () => {
|
|
135
|
+
const gsNamespaces = await grc.namespaces.getAll();
|
|
136
|
+
expect(gsNamespaces.namespaces).to.equal('');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('creates one namespace', async () => {
|
|
140
|
+
const result = await grc.namespaces.create(nameSpace, nameSpaceUri);
|
|
141
|
+
expect(result).to.equal(nameSpace);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('has one namespace', async () => {
|
|
145
|
+
const gsNameSpaces = await grc.namespaces.getAll();
|
|
146
|
+
expect(gsNameSpaces.namespaces.namespace.length).to.equal(1);
|
|
147
|
+
expect(gsNameSpaces.namespaces.namespace[0].name).to.equal(nameSpace);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('query dedicated namespace', async () => {
|
|
151
|
+
const gsNameSpace = await grc.namespaces.get(nameSpace);
|
|
152
|
+
expect(gsNameSpace.namespace.prefix).to.equal(nameSpace);
|
|
153
|
+
expect(gsNameSpace.namespace.uri).to.equal(nameSpaceUri);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('delete namespace', async () => {
|
|
157
|
+
const result = await grc.namespaces.delete(nameSpace);
|
|
158
|
+
expect(result).to.be.true;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('has no namespace', async () => {
|
|
162
|
+
const gsNameSpaces = await grc.namespaces.getAll();
|
|
163
|
+
expect(gsNameSpaces.namespaces).to.equal('');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
129
167
|
describe('Datastore', () => {
|
|
130
168
|
let createdWorkSpace;
|
|
131
169
|
|
package/demo/dev.js
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import GeoServerRestClient from '../geoserver-rest-client.js';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { execSync } from 'child_process'
|
|
4
|
-
|
|
5
|
-
const geoserverUrl = 'http://localhost:8080/geoserver/rest/';
|
|
6
|
-
const geoserverDefaultUser = 'mgsm-admin';
|
|
7
|
-
const geoserverDefaultPw = 'VB?w10&0A2s8PU?BPM&Nn!8kl';
|
|
8
|
-
const role = 'ADMIN';
|
|
9
|
-
|
|
10
|
-
// TODO: read from secrets
|
|
11
|
-
const newGeoserverUser = 'mgsm-admin';
|
|
12
|
-
const newGeoserverPw = 'VB?w10&0A2s8PU?BPM&Nn!8kl';
|
|
13
|
-
|
|
14
|
-
const workspaceWorld = 'mgsm-world';
|
|
15
|
-
const workspaceGermany = 'mgsm-ger';
|
|
16
|
-
|
|
17
|
-
const gsDataDir = '/opt/geoserver_data/';
|
|
18
|
-
const countriesGpkgUrl = 'https://github.com/JakobMiksch/geodata/raw/main/vector-data.gpkg?raw=true';
|
|
19
|
-
const worldRasterUrl = 'https://github.com/JakobMiksch/geodata/raw/main/world.tiff';
|
|
20
|
-
const neGpkgPath = gsDataDir + 'natural_earth_vector.gpkg';
|
|
21
|
-
const neWorldRasterPath = gsDataDir + 'world.tif';
|
|
22
|
-
const worldRasterDs = 'world-raster';
|
|
23
|
-
const worldRasterLayer = 'ne_world';
|
|
24
|
-
const worldRasterTitle = 'World Image';
|
|
25
|
-
|
|
26
|
-
const postalCodesUrl = 'https://github.com/JakobMiksch/geodata/raw/main/postal_codes_germany.gpkg';
|
|
27
|
-
const postalCodesPath = gsDataDir + 'postal_codes.gpkg';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Main function
|
|
31
|
-
*/
|
|
32
|
-
async function initGeoserver () {
|
|
33
|
-
console.log('accessing geoserver');
|
|
34
|
-
const workspaceGermany = 'mgsm-ger';
|
|
35
|
-
const gpkgStore = 'postal-code-store';
|
|
36
|
-
const nativeLayerName = undefined;
|
|
37
|
-
const layerName = 'postal_codes_germany';
|
|
38
|
-
const layerTitle = 'Postal Codes Germany';
|
|
39
|
-
|
|
40
|
-
const layerCreated = await grc.layers.publishFeatureType(
|
|
41
|
-
workspaceGermany, gpkgStore, nativeLayerName, layerName, layerTitle,
|
|
42
|
-
'EPSG:4326', true, 'asdfasdfasdfasdf asdfasdfsadf asdfasdf afas asdf asdfas asdf asdfas asdf asdfas asfd asdfas as dfas dfasdf asdf asdfas asdfa sdfawesdf'
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
console.log(layerCreated);
|
|
46
|
-
|
|
47
|
-
// let result;
|
|
48
|
-
// result = await grc.layers.get('mgsm-ger:postal_codes_germany');
|
|
49
|
-
// console.log(result);
|
|
50
|
-
|
|
51
|
-
// result = await grc.layers.modifyAttribution('mgsm-ger:postal_codes_germany', 'Apfelbaum', 'www.zeit.de');
|
|
52
|
-
// console.log(result);
|
|
53
|
-
|
|
54
|
-
// result = await grc.layers.get('mgsm-ger:postal_codes_germany');
|
|
55
|
-
// console.log(result);
|
|
56
|
-
|
|
57
|
-
// await addContactInformation();
|
|
58
|
-
|
|
59
|
-
// await adaptSecurity();
|
|
60
|
-
|
|
61
|
-
// await createWorkspaces();
|
|
62
|
-
|
|
63
|
-
// await publishGlobalVectorData();
|
|
64
|
-
|
|
65
|
-
// await publishWorldRaster();
|
|
66
|
-
|
|
67
|
-
// await publishPostalCodes();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Adapts security settings for GeoServer
|
|
72
|
-
*/
|
|
73
|
-
async function adaptSecurity () {
|
|
74
|
-
const user = newGeoserverUser;
|
|
75
|
-
const userPw = newGeoserverPw;
|
|
76
|
-
|
|
77
|
-
if (!user || !userPw || user === '' || userPw === '') {
|
|
78
|
-
console.error('No valid user or user password given - EXIT.');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const userCreated = await grc.security.createUser(user, userPw);
|
|
82
|
-
if (userCreated) {
|
|
83
|
-
console.info('Successfully created user', user);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const roleAssigend = await grc.security.associateUserRole(user, role);
|
|
87
|
-
if (roleAssigend) {
|
|
88
|
-
console.info(`Successfully added role ${role} to user ${user}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// disable user
|
|
92
|
-
const adminDisabled = await grc.security.updateUser(geoserverDefaultUser, geoserverDefaultPw, false);
|
|
93
|
-
if (adminDisabled) {
|
|
94
|
-
console.info('Successfully disabled default "admin" user');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Adds basic contact information
|
|
100
|
-
*/
|
|
101
|
-
async function addContactInformation () {
|
|
102
|
-
const address = 'Schillerstraße 2a';
|
|
103
|
-
const city = 'Mutterstadt';
|
|
104
|
-
const country = 'Deutschland';
|
|
105
|
-
const state = undefined;
|
|
106
|
-
const postalCode = '67112';
|
|
107
|
-
const email = 'info@meggsimum.de';
|
|
108
|
-
const organization = 'meggsimum – Büro für Geoinformatik';
|
|
109
|
-
const contactPerson = 'Christian Mayer';
|
|
110
|
-
const phoneNumber = undefined;
|
|
111
|
-
|
|
112
|
-
const result = await grc.settings.updateContactInformation(address, city, country, postalCode, state, email, organization, contactPerson, phoneNumber);
|
|
113
|
-
if (result) {
|
|
114
|
-
console.log('Changed contact information.');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Creates the basic workspaces
|
|
120
|
-
*/
|
|
121
|
-
async function createWorkspaces () {
|
|
122
|
-
const wsWorldCreated = await grc.workspaces.create(workspaceWorld);
|
|
123
|
-
console.log('Created Workspace World', wsWorldCreated);
|
|
124
|
-
|
|
125
|
-
const wsGermanyCreated = await grc.workspaces.create(workspaceGermany);
|
|
126
|
-
console.log('Created Workspace Germany', wsGermanyCreated);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Publish postal codes from Germany
|
|
131
|
-
*/
|
|
132
|
-
async function publishPostalCodes () {
|
|
133
|
-
const gpkgStore = 'postal-code-store';
|
|
134
|
-
const nativeLayerName = undefined;
|
|
135
|
-
const layerName = 'postal_codes_germany';
|
|
136
|
-
const layerTitle = 'Postal Codes Germany';
|
|
137
|
-
|
|
138
|
-
const wsExists =
|
|
139
|
-
await grc.datastores.getDataStore(workspaceGermany, gpkgStore);
|
|
140
|
-
const lyrExists =
|
|
141
|
-
await grc.layers.get(`${workspaceGermany}:${layerName}`);
|
|
142
|
-
|
|
143
|
-
if (wsExists && lyrExists) {
|
|
144
|
-
console.info('Datastore and layer for postal codes already exist - SKIP!');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// TODO replace by native node call
|
|
149
|
-
execSync(`wget -N -O ${postalCodesPath} ${postalCodesUrl}`);
|
|
150
|
-
|
|
151
|
-
const postalStoreCreated = await grc.datastores.createGpkgStore(
|
|
152
|
-
workspaceGermany, gpkgStore, postalCodesPath
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
console.log('Successfully created postal code GPKG store', postalStoreCreated);
|
|
156
|
-
|
|
157
|
-
const layerCreated = await grc.layers.publishFeatureType(
|
|
158
|
-
workspaceGermany, gpkgStore, nativeLayerName, layerName, layerTitle,
|
|
159
|
-
'EPSG:4326', true
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
if (layerCreated) {
|
|
163
|
-
console.log(`Created vector layer for postal codes "${layerName}"`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Publishes the world vector data (currently countries, rivers, places)
|
|
169
|
-
*/
|
|
170
|
-
async function publishGlobalVectorData () {
|
|
171
|
-
const gpkgStore = 'natural-earth-gpkg-store';
|
|
172
|
-
const countriesLyr = 'ne_10m_admin_0_countries';
|
|
173
|
-
const riversLyr = 'ne_10m_rivers_lake_centerlines';
|
|
174
|
-
const placesLyr = 'ne_10m_populated_places';
|
|
175
|
-
|
|
176
|
-
const wsExists =
|
|
177
|
-
await grc.datastores.getDataStore(workspaceWorld, gpkgStore);
|
|
178
|
-
const lyr1Exists =
|
|
179
|
-
await grc.layers.get(`${workspaceWorld}:${countriesLyr}`);
|
|
180
|
-
const lyr2Exists =
|
|
181
|
-
await grc.layers.get(`${workspaceWorld}:${riversLyr}`);
|
|
182
|
-
const lyr3Exists =
|
|
183
|
-
await grc.layers.get(`${workspaceWorld}:${placesLyr}`);
|
|
184
|
-
|
|
185
|
-
if (wsExists && lyr1Exists && lyr2Exists && lyr3Exists) {
|
|
186
|
-
console.info('Datastore and layers for global vector data already exist - SKIP!');
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// TODO replace by native node call
|
|
191
|
-
execSync(`wget -N -O ${neGpkgPath} ${countriesGpkgUrl}`);
|
|
192
|
-
|
|
193
|
-
// NOTE: here the relative path within the GeoServer is required
|
|
194
|
-
const neStoreCreated = await grc.datastores.createGpkgStore(
|
|
195
|
-
workspaceWorld, gpkgStore, neGpkgPath
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
console.log('Successfully created NE GPKG store', neStoreCreated);
|
|
199
|
-
|
|
200
|
-
// publish vector layers
|
|
201
|
-
publishVectorLayerWithStyle(gpkgStore, countriesLyr, 'World Countries', 'countries');
|
|
202
|
-
publishVectorLayerWithStyle(gpkgStore, riversLyr, 'World Rivers', 'rivers');
|
|
203
|
-
publishVectorLayerWithStyle(gpkgStore, placesLyr, 'World Places', 'places');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Util for publishing a layer with a style
|
|
208
|
-
*/
|
|
209
|
-
async function publishVectorLayerWithStyle (store, layerName, layerTitle, styleName) {
|
|
210
|
-
const nativeLayerName = undefined;
|
|
211
|
-
|
|
212
|
-
const layerCreated = await grc.layers.publishFeatureType(
|
|
213
|
-
workspaceWorld, store, nativeLayerName, layerName, layerTitle,
|
|
214
|
-
'EPSG:4326', true
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
if (layerCreated) {
|
|
218
|
-
console.log(`Created vector layer "${layerName}"`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
const sldFilePath = styleName + '.sld';
|
|
223
|
-
const sldBody = fs.readFileSync(sldFilePath, 'utf8');
|
|
224
|
-
|
|
225
|
-
// publish style
|
|
226
|
-
const stylePublished = await grc.styles.publish(workspaceWorld, styleName, sldBody);
|
|
227
|
-
|
|
228
|
-
console.log('Published style: ', stylePublished);
|
|
229
|
-
|
|
230
|
-
const qualifiedName = `${workspaceWorld}:${layerName}`;
|
|
231
|
-
const styleAssigend = await grc.styles.assignStyleToLayer(qualifiedName, styleName,
|
|
232
|
-
workspaceWorld, true
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
console.log(`Style "${styleName}" assigned to layer "${qualifiedName}"`, styleAssigend);
|
|
236
|
-
} catch (e) {
|
|
237
|
-
console.error('Error:', e.stack);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Publishes the NE GeoTiff based World Raster
|
|
243
|
-
*/
|
|
244
|
-
async function publishWorldRaster () {
|
|
245
|
-
const wsExists =
|
|
246
|
-
await grc.datastores.getCoverageStore(workspaceWorld, worldRasterDs);
|
|
247
|
-
const lyrExists = await grc.layers.get(`${workspaceWorld}:${worldRasterLayer}`);
|
|
248
|
-
|
|
249
|
-
if (wsExists && lyrExists) {
|
|
250
|
-
console.info('Datastore and layer for world raster data already exist - SKIP!');
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// TODO replace by native node call
|
|
255
|
-
execSync(`wget -N -O ${neWorldRasterPath} ${worldRasterUrl}`);
|
|
256
|
-
|
|
257
|
-
console.log('Downloaded GeoTIFF file');
|
|
258
|
-
|
|
259
|
-
const geotiffCreated = await grc.datastores.createGeotiffFromFile(
|
|
260
|
-
workspaceWorld, worldRasterDs, worldRasterLayer,
|
|
261
|
-
worldRasterTitle, neWorldRasterPath
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
console.log('Created GeoTIFF world raster layer', geotiffCreated);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// check if we can connect to GeoServer REST API
|
|
268
|
-
const grc = new GeoServerRestClient(geoserverUrl, geoserverDefaultUser, geoserverDefaultPw);
|
|
269
|
-
grc.exists().then(gsExists => {
|
|
270
|
-
if (gsExists === true) {
|
|
271
|
-
initGeoserver();
|
|
272
|
-
} else {
|
|
273
|
-
console.error('Could not connect to GeoServer REST API - ABORT!');
|
|
274
|
-
}
|
|
275
|
-
});
|