geoserver-node-client 0.0.5 → 1.0.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/src/datastore.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import fetch from 'node-fetch';
2
2
  import fs from 'fs';
3
+ import { getGeoServerResponseText, GeoServerResponseError } from './util/geoserver.js';
4
+ import AboutClient from './about.js'
3
5
 
4
6
  /**
5
7
  * Client for GeoServer data stores
@@ -11,13 +13,11 @@ export default class DatastoreClient {
11
13
  * Creates a GeoServer REST DatastoreClient instance.
12
14
  *
13
15
  * @param {String} url The URL of the GeoServer REST API endpoint
14
- * @param {String} user The user for the GeoServer REST API
15
- * @param {String} password The password for the GeoServer REST API
16
+ * @param {String} auth The Basic Authentication string
16
17
  */
17
- constructor (url, user, password) {
18
- this.url = url.endsWith('/') ? url : url + '/';
19
- this.user = user;
20
- this.password = password;
18
+ constructor (url, auth) {
19
+ this.url = url;
20
+ this.auth = auth;
21
21
  }
22
22
 
23
23
  /**
@@ -25,7 +25,7 @@ export default class DatastoreClient {
25
25
  *
26
26
  * @param {String} workspace The workspace to get DataStores for
27
27
  *
28
- * @returns {Object|Boolean} An object containing store details or 'false'
28
+ * @returns {Object} An object containing store details
29
29
  */
30
30
  async getDataStores (workspace) {
31
31
  return this.getStores(workspace, 'datastores');
@@ -36,7 +36,7 @@ export default class DatastoreClient {
36
36
  *
37
37
  * @param {String} workspace The workspace to get CoverageStores for
38
38
  *
39
- * @returns {Object|Boolean} An object containing store details or 'false'
39
+ * @returns {Object} An object containing store details
40
40
  */
41
41
  async getCoverageStores (workspace) {
42
42
  return this.getStores(workspace, 'coveragestores');
@@ -47,7 +47,7 @@ export default class DatastoreClient {
47
47
  *
48
48
  * @param {String} workspace The workspace to get WmsStores for
49
49
  *
50
- * @returns {Object|Boolean} An object containing store details or 'false'
50
+ * @returns {Object} An object containing store details
51
51
  */
52
52
  async getWmsStores (workspace) {
53
53
  return this.getStores(workspace, 'wmsstores');
@@ -58,7 +58,7 @@ export default class DatastoreClient {
58
58
  *
59
59
  * @param {String} workspace The workspace to get WmtsStores for
60
60
  *
61
- * @returns {Object|Boolean} An object containing store details or 'false'
61
+ * @returns {Object} An object containing store details
62
62
  */
63
63
  async getWmtsStores (workspace) {
64
64
  return this.getStores(workspace, 'wmtsstores');
@@ -68,30 +68,26 @@ export default class DatastoreClient {
68
68
  * @private
69
69
  * Get information about various store types in a workspace.
70
70
  *
71
- * @param {String} workspace
72
- * @param {String} storeType
71
+ * @param {String} workspace The workspace name
72
+ * @param {String} storeType The type of store
73
73
  *
74
- * @returns {Object|Boolean} An object containing store details or 'false'
74
+ * @throws Error if request fails
75
+ *
76
+ * @returns {Object} An object containing store details or undefined if it cannot be found
75
77
  */
76
78
  async getStores (workspace, storeType) {
77
- try {
78
- const auth =
79
- Buffer.from(this.user + ':' + this.password).toString('base64');
80
- const response = await fetch(this.url + 'workspaces/' + workspace + '/' + storeType + '.json', {
81
- credentials: 'include',
82
- method: 'GET',
83
- headers: {
84
- Authorization: 'Basic ' + auth
85
- }
86
- });
87
- if (response.status === 200) {
88
- return await response.json();
79
+ const response = await fetch(this.url + 'workspaces/' + workspace + '/' + storeType + '.json', {
80
+ credentials: 'include',
81
+ method: 'GET',
82
+ headers: {
83
+ Authorization: this.auth
89
84
  }
90
- console.warn(await response.text());
91
- return false;
92
- } catch (error) {
93
- return false;
85
+ });
86
+ if (!response.ok) {
87
+ const geoServerResponse = await getGeoServerResponseText(response);
88
+ throw new GeoServerResponseError(null, geoServerResponse);
94
89
  }
90
+ return response.json();
95
91
  }
96
92
 
97
93
  /**
@@ -100,7 +96,7 @@ export default class DatastoreClient {
100
96
  * @param {String} workspace The workspace to search DataStore in
101
97
  * @param {String} dataStore DataStore name
102
98
  *
103
- * @returns {Object|Boolean} An object containing store details or 'false'
99
+ * @returns {Object} An object containing store details or undefined if it cannot be found
104
100
  */
105
101
  async getDataStore (workspace, dataStore) {
106
102
  return this.getStore(workspace, dataStore, 'datastores');
@@ -112,7 +108,7 @@ export default class DatastoreClient {
112
108
  * @param {String} workspace The workspace to search CoverageStore in
113
109
  * @param {String} covStore CoverageStore name
114
110
  *
115
- * @returns {Object|Boolean} An object containing store details or 'false'
111
+ * @returns {Object} An object containing store details or undefined if it cannot be found
116
112
  */
117
113
  async getCoverageStore (workspace, covStore) {
118
114
  return this.getStore(workspace, covStore, 'coveragestores');
@@ -124,7 +120,8 @@ export default class DatastoreClient {
124
120
  * @param {String} workspace The workspace to search WmsStore in
125
121
  * @param {String} wmsStore WmsStore name
126
122
  *
127
- * @returns {Object|Boolean} An object containing store details or 'false'
123
+ * @returns {Object} An object containing store details or undefined if it cannot be found
124
+ *
128
125
  */
129
126
  async getWmsStore (workspace, wmsStore) {
130
127
  return this.getStore(workspace, wmsStore, 'wmsstores');
@@ -136,44 +133,46 @@ export default class DatastoreClient {
136
133
  * @param {String} workspace The workspace to search WmtsStore in
137
134
  * @param {String} wmtsStore WmtsStore name
138
135
  *
139
- * @returns {Object|Boolean} An object containing store details or 'false'
140
- s */
136
+ * @returns {Object} An object containing store details or undefined if it cannot be found
137
+ */
141
138
  async getWmtsStore (workspace, wmtsStore) {
142
139
  return this.getStore(workspace, wmtsStore, 'wmtsstores');
143
140
  }
144
141
 
145
142
  /**
146
143
  * @private
147
- * @param {String} workspace
148
- * @param {String} storeName
149
- * @param {String} storeType
144
+ * Get GeoServer store by type
150
145
  *
151
- * @returns {Object|Boolean} An object containing store details or 'false'
146
+ * @param {String} workspace The name of the workspace
147
+ * @param {String} storeName The name of the store
148
+ * @param {String} storeType The type of the store
149
+ *
150
+ * @throws Error if request fails
151
+ *
152
+ * @returns {Object} An object containing store details or undefined if it cannot be found
152
153
  */
153
154
  async getStore (workspace, storeName, storeType) {
154
- try {
155
- const auth =
156
- Buffer.from(this.user + ':' + this.password).toString('base64');
157
- const url = this.url + 'workspaces/' + workspace + '/' + storeType + '/' + storeName + '.json';
158
- const response = await fetch(url, {
159
- credentials: 'include',
160
- method: 'GET',
161
- headers: {
162
- Authorization: 'Basic ' + auth
163
- }
164
- });
165
- if (response.status === 200) {
166
- return await response.json();
167
- } else if (response.status === 404) {
168
- console.warn('No ' + storeType + ' with name "' + storeName + '" found');
169
- return false;
155
+ const url = this.url + 'workspaces/' + workspace + '/' + storeType + '/' + storeName + '.json';
156
+ const response = await fetch(url, {
157
+ credentials: 'include',
158
+ method: 'GET',
159
+ headers: {
160
+ Authorization: this.auth
161
+ }
162
+ });
163
+
164
+ if (!response.ok) {
165
+ const grc = new AboutClient(this.url, this.auth);
166
+ if (await grc.exists()) {
167
+ // GeoServer exists, but requested item does not exist, we return empty
168
+ return;
170
169
  } else {
171
- console.warn(await response.text());
172
- return false;
170
+ // There was a general problem with GeoServer
171
+ const geoServerResponse = await getGeoServerResponseText(response);
172
+ throw new GeoServerResponseError(null, geoServerResponse);
173
173
  }
174
- } catch (error) {
175
- return false;
176
174
  }
175
+ return response.json();
177
176
  }
178
177
 
179
178
  /**
@@ -187,41 +186,36 @@ s */
187
186
  * @param {String} layerTitle The published title of the new layer
188
187
  * @param {String} filePath The path to the GeoTIFF file on the server
189
188
  *
190
- * @returns {String|Boolean} The successful response text or 'false'
189
+ * @throws Error if request fails
190
+ *
191
+ * @returns {String} The successful response text
191
192
  */
192
193
  async createGeotiffFromFile (workspace, coverageStore, layerName, layerTitle, filePath) {
193
- try {
194
- const lyrTitle = layerTitle || layerName;
195
- const stats = fs.statSync(filePath);
196
- const fileSizeInBytes = stats.size;
197
- const readStream = fs.createReadStream(filePath);
198
-
199
- const auth =
200
- Buffer.from(this.user + ':' + this.password).toString('base64');
201
- let url = this.url + 'workspaces/' + workspace + '/coveragestores/' +
194
+ const lyrTitle = layerTitle || layerName;
195
+ const stats = fs.statSync(filePath);
196
+ const fileSizeInBytes = stats.size;
197
+ const readStream = fs.createReadStream(filePath);
198
+
199
+ let url = this.url + 'workspaces/' + workspace + '/coveragestores/' +
202
200
  coverageStore + '/file.geotiff';
203
- url += '?filename=' + lyrTitle + '&coverageName=' + layerName;
204
- const response = await fetch(url, {
205
- credentials: 'include',
206
- method: 'PUT',
207
- headers: {
208
- Authorization: 'Basic ' + auth,
209
- 'Content-Type': 'image/tiff',
210
- 'Content-length': fileSizeInBytes
211
- },
212
- body: readStream
213
- });
201
+ url += '?filename=' + lyrTitle + '&coverageName=' + layerName;
202
+ const response = await fetch(url, {
203
+ credentials: 'include',
204
+ method: 'PUT',
205
+ headers: {
206
+ Authorization: this.auth,
207
+ 'Content-Type': 'image/tiff',
208
+ 'Content-length': fileSizeInBytes
209
+ },
210
+ body: readStream
211
+ });
214
212
 
215
- if (response.status === 201) {
216
- const responseText = await response.text();
217
- // TODO enforce JSON response or parse XML
218
- return responseText;
219
- } else {
220
- return false;
221
- }
222
- } catch (error) {
223
- return false;
213
+ if (!response.ok) {
214
+ const geoServerResponse = await getGeoServerResponseText(response);
215
+ throw new GeoServerResponseError(null, geoServerResponse);
224
216
  }
217
+ // TODO: enforce JSON response or parse XML
218
+ return response.text();
225
219
  }
226
220
 
227
221
  /**
@@ -238,7 +232,7 @@ s */
238
232
  * @param {String} pgDb The PostGIS DB name
239
233
  * @param {String} [exposePk] expose primary key, defaults to false
240
234
  *
241
- * @returns {Boolean} If the store could be created
235
+ * @throws Error if request fails
242
236
  */
243
237
  async createPostgisStore (workspace, namespaceUri, dataStore, pgHost, pgPort, pgUser, pgPassword, pgSchema, pgDb, exposePk) {
244
238
  const body = {
@@ -292,24 +286,21 @@ s */
292
286
  }
293
287
  };
294
288
 
295
- const auth =
296
- Buffer.from(this.user + ':' + this.password).toString('base64');
297
289
  const url = this.url + 'workspaces/' + workspace + '/datastores';
298
290
  const response = await fetch(url, {
299
291
  credentials: 'include',
300
292
  method: 'POST',
301
293
  headers: {
302
- Authorization: 'Basic ' + auth,
294
+ Authorization: this.auth,
303
295
  'Content-Type': 'application/json'
304
296
  },
305
297
  body: JSON.stringify(body)
306
298
  });
307
299
 
308
- if (response.status === 201) {
309
- return true;
310
- } else {
311
- console.warn(await response.text());
312
- return false;
300
+ // TODO: not tested yet
301
+ if (!response.ok) {
302
+ const geoServerResponse = await getGeoServerResponseText(response);
303
+ throw new GeoServerResponseError(null, geoServerResponse);
313
304
  }
314
305
  }
315
306
 
@@ -324,35 +315,32 @@ s */
324
315
  *
325
316
  * @param {String} workspace The WS to create the data store in
326
317
  * @param {String} dataStore The data store name
327
- * @param {String} zipArchivePath Aboslute path to zip archive with the 3 properties files
318
+ * @param {String} zipArchivePath Absolute path to zip archive with the 3 properties files
319
+ *
320
+ * @throws Error if request fails
328
321
  *
329
- * @returns {String|Boolen} The response text or 'false'
322
+ * @returns {String} The response text
330
323
  */
331
324
  async createImageMosaicStore (workspace, coverageStore, zipArchivePath) {
332
- try {
333
- const readStream = fs.createReadStream(zipArchivePath);
334
- const auth = Buffer.from(this.user + ':' + this.password).toString('base64');
335
- const url = this.url + 'workspaces/' + workspace + '/coveragestores/' + coverageStore + '/file.imagemosaic';
336
- const response = await fetch(url, {
337
- credentials: 'include',
338
- method: 'PUT',
339
- headers: {
340
- Authorization: 'Basic ' + auth,
341
- 'Content-Type': 'application/zip'
342
- },
343
- body: readStream
344
- });
325
+ const readStream = fs.createReadStream(zipArchivePath);
345
326
 
346
- if (response.status === 201) {
347
- return await response.text();
348
- } else {
349
- console.warn(await response.text());
350
- return false;
351
- }
352
- } catch (error) {
353
- console.log(error);
354
- return false;
327
+ const url = this.url + 'workspaces/' + workspace + '/coveragestores/' + coverageStore + '/file.imagemosaic';
328
+ const response = await fetch(url, {
329
+ credentials: 'include',
330
+ method: 'PUT',
331
+ headers: {
332
+ Authorization: this.auth,
333
+ 'Content-Type': 'application/zip'
334
+ },
335
+ body: readStream
336
+ });
337
+
338
+ if (!response.ok) {
339
+ const geoServerResponse = await getGeoServerResponseText(response);
340
+ throw new GeoServerResponseError(null, geoServerResponse);
355
341
  }
342
+
343
+ return response.text();
356
344
  };
357
345
 
358
346
  /**
@@ -362,7 +350,7 @@ s */
362
350
  * @param {String} dataStore The data store name
363
351
  * @param {String} wmsCapabilitiesUrl Base WMS capabilities URL
364
352
  *
365
- * @returns {Boolean} If store could be created
353
+ * @throws Error if request fails
366
354
  */
367
355
  async createWmsStore (workspace, dataStore, wmsCapabilitiesUrl) {
368
356
  const body = {
@@ -373,24 +361,20 @@ s */
373
361
  }
374
362
  };
375
363
 
376
- const auth =
377
- Buffer.from(this.user + ':' + this.password).toString('base64');
378
364
  const url = this.url + 'workspaces/' + workspace + '/wmsstores';
379
365
  const response = await fetch(url, {
380
366
  credentials: 'include',
381
367
  method: 'POST',
382
368
  headers: {
383
- Authorization: 'Basic ' + auth,
369
+ Authorization: this.auth,
384
370
  'Content-Type': 'application/json'
385
371
  },
386
372
  body: JSON.stringify(body)
387
373
  });
388
374
 
389
- if (response.status === 201) {
390
- return true;
391
- } else {
392
- console.warn(await response.text());
393
- return false;
375
+ if (!response.ok) {
376
+ const geoServerResponse = await getGeoServerResponseText(response);
377
+ throw new GeoServerResponseError(null, geoServerResponse);
394
378
  }
395
379
  }
396
380
 
@@ -403,7 +387,7 @@ s */
403
387
  * @param {String} namespaceUrl URL of the GeoServer namespace
404
388
  * @param {Boolean} [useHttpConnectionPooling=true] use HTTP connection pooling for WFS connection
405
389
  *
406
- * @returns {Boolean} If store could be created
390
+ * @throws Error if request fails
407
391
  */
408
392
  async createWfsStore (workspace, dataStore, wfsCapabilitiesUrl, namespaceUrl, useHttpConnectionPooling) {
409
393
  const body = {
@@ -429,24 +413,20 @@ s */
429
413
  }
430
414
  };
431
415
 
432
- const auth =
433
- Buffer.from(this.user + ':' + this.password).toString('base64');
434
416
  const url = this.url + 'workspaces/' + workspace + '/datastores';
435
417
  const response = await fetch(url, {
436
418
  credentials: 'include',
437
419
  method: 'POST',
438
420
  headers: {
439
- Authorization: 'Basic ' + auth,
421
+ Authorization: this.auth,
440
422
  'Content-Type': 'application/json'
441
423
  },
442
424
  body: JSON.stringify(body)
443
425
  });
444
426
 
445
- if (response.status === 201) {
446
- return true;
447
- } else {
448
- console.warn(await response.text());
449
- return false;
427
+ if (!response.ok) {
428
+ const geoServerResponse = await getGeoServerResponseText(response);
429
+ throw new GeoServerResponseError(null, geoServerResponse);
450
430
  }
451
431
  }
452
432
 
@@ -457,35 +437,25 @@ s */
457
437
  * @param {String} coverageStore Name of data store to delete
458
438
  * @param {String} recurse Flag to enable recursive deletion
459
439
  *
460
- * @returns {Boolean} If the datastore could be deleted
440
+ * @throws Error if request fails
461
441
  */
462
442
  async deleteDataStore (workspace, dataStore, recurse) {
463
- try {
464
- const auth =
465
- Buffer.from(this.user + ':' + this.password).toString('base64');
466
- let url = this.url + 'workspaces/' + workspace + '/datastores/' + dataStore;
467
- url += '?recurse=' + recurse;
468
-
469
- const response = await fetch(url, {
470
- credentials: 'include',
471
- method: 'DELETE',
472
- headers: {
473
- Authorization: 'Basic ' + auth
474
- }
475
- });
476
-
477
- if (response.status === 200) {
478
- return true;
479
- } else if (response.status === 401) {
480
- console.warn('Deletion failed. There might be dependant objects to ' +
481
- 'this store. Delete them first or call this with "recurse=false"');
482
- console.warn(response.text());
483
- return false;
484
- } else {
485
- return false;
443
+ let url = this.url + 'workspaces/' + workspace + '/datastores/' + dataStore;
444
+ url += '?recurse=' + recurse;
445
+
446
+ const response = await fetch(url, {
447
+ credentials: 'include',
448
+ method: 'DELETE',
449
+ headers: {
450
+ Authorization: this.auth
486
451
  }
487
- } catch (error) {
488
- return false;
452
+ });
453
+
454
+ if (!response.ok) {
455
+ // TODO: could not find status codes in the docs or via testing
456
+ // https://docs.geoserver.org/latest/en/api/#1.0.0/datastores.yaml
457
+ const geoServerResponse = await getGeoServerResponseText(response);
458
+ throw new GeoServerResponseError(null, geoServerResponse);
489
459
  }
490
460
  }
491
461
 
@@ -496,35 +466,30 @@ s */
496
466
  * @param {String} coverageStore Name of CoverageStore to delete
497
467
  * @param {String} recurse Flag to enable recursive deletion
498
468
  *
499
- * @returns {Boolean} If the datastore could be deleted
469
+ * @throws Error if request fails
500
470
  */
501
471
  async deleteCoverageStore (workspace, coverageStore, recurse) {
502
- try {
503
- const auth =
504
- Buffer.from(this.user + ':' + this.password).toString('base64');
505
- let url = this.url + 'workspaces/' + workspace + '/coveragestores/' + coverageStore;
506
- url += '?recurse=' + recurse;
507
-
508
- const response = await fetch(url, {
509
- credentials: 'include',
510
- method: 'DELETE',
511
- headers: {
512
- Authorization: 'Basic ' + auth
513
- }
514
- });
515
-
516
- if (response.status === 200) {
517
- return true;
518
- } else if (response.status === 401) {
519
- console.warn('Deletion failed. There might be dependant objects to ' +
520
- 'this store. Delete them first or call this with "recurse=false"');
521
- console.warn(response.text());
522
- return false;
523
- } else {
524
- return false;
472
+ let url = this.url + 'workspaces/' + workspace + '/coveragestores/' + coverageStore;
473
+ url += '?recurse=' + recurse;
474
+
475
+ const response = await fetch(url, {
476
+ credentials: 'include',
477
+ method: 'DELETE',
478
+ headers: {
479
+ Authorization: this.auth
480
+ }
481
+ });
482
+
483
+ // TODO: could not test it
484
+ if (!response.ok) {
485
+ const geoServerResponse = await getGeoServerResponseText(response);
486
+ switch (response.status) {
487
+ case 401:
488
+ throw new GeoServerResponseError('Deletion failed. There might be dependant objects to ' +
489
+ 'this store. Delete them first or call this with "recurse=false"', geoServerResponse);
490
+ default:
491
+ throw new GeoServerResponseError(null, geoServerResponse);
525
492
  }
526
- } catch (error) {
527
- return false;
528
493
  }
529
494
  }
530
495
 
@@ -535,7 +500,7 @@ s */
535
500
  * @param {String} dataStore The data store name
536
501
  * @param {String} gpkgPath Relative path to GeoPackage file within geoserver_data dir
537
502
  *
538
- * @returns {Boolean} If store could be created
503
+ * @throws Error if request fails
539
504
  */
540
505
  async createGpkgStore (workspace, dataStore, gpkgPath) {
541
506
  const body = {
@@ -557,24 +522,20 @@ s */
557
522
  }
558
523
  };
559
524
 
560
- const auth =
561
- Buffer.from(this.user + ':' + this.password).toString('base64');
562
525
  const url = this.url + 'workspaces/' + workspace + '/datastores';
563
526
  const response = await fetch(url, {
564
527
  credentials: 'include',
565
528
  method: 'POST',
566
529
  headers: {
567
- Authorization: 'Basic ' + auth,
530
+ Authorization: this.auth,
568
531
  'Content-Type': 'application/json'
569
532
  },
570
533
  body: JSON.stringify(body)
571
534
  });
572
535
 
573
- if (response.status === 201) {
574
- return true;
575
- } else {
576
- console.warn(await response.text());
577
- return false;
536
+ if (!response.ok) {
537
+ const geoServerResponse = await getGeoServerResponseText(response);
538
+ throw new GeoServerResponseError(null, geoServerResponse);
578
539
  }
579
540
  }
580
541
  }