geoserver-node-client 0.0.7 → 1.2.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/style.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import fetch from 'node-fetch';
2
2
  import WorkspaceClient from './workspace.js';
3
+ import { getGeoServerResponseText, GeoServerResponseError } from './util/geoserver.js';
4
+ import AboutClient from './about.js'
3
5
 
4
6
  /**
5
7
  * Client for GeoServer styles
@@ -11,41 +13,34 @@ export default class StyleClient {
11
13
  * Creates a GeoServer REST StyleClient 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
  /**
24
24
  * Returns all default styles.
25
25
  *
26
- * @returns {Object|Boolean} An object with the default styles or 'false'
26
+ * @throws Error if request fails
27
+ *
28
+ * @returns {Object} An object with the default styles
27
29
  */
28
30
  async getDefaults () {
29
- try {
30
- const auth =
31
- Buffer.from(this.user + ':' + this.password).toString('base64');
32
- const response = await fetch(this.url + 'styles.json', {
33
- credentials: 'include',
34
- method: 'GET',
35
- headers: {
36
- Authorization: 'Basic ' + auth
37
- }
38
- });
39
-
40
- if (response.status === 200) {
41
- return await response.json();
42
- } else {
43
- console.warn(await response.text());
44
- return false;
31
+ const response = await fetch(this.url + 'styles.json', {
32
+ credentials: 'include',
33
+ method: 'GET',
34
+ headers: {
35
+ Authorization: this.auth
45
36
  }
46
- } catch (error) {
47
- return false;
37
+ });
38
+
39
+ if (!response.ok) {
40
+ const geoServerResponse = await getGeoServerResponseText(response);
41
+ throw new GeoServerResponseError(null, geoServerResponse);
48
42
  }
43
+ return response.json();
49
44
  }
50
45
 
51
46
  /**
@@ -53,76 +48,80 @@ export default class StyleClient {
53
48
  *
54
49
  * @param {String} workspace Workspace name to get styles for
55
50
  *
56
- * @returns {Object|Boolean} An object with all styles or 'false'
51
+ * @throws Error if request fails
52
+ *
53
+ * @returns {Object} An object with all styles
57
54
  */
58
55
  async getInWorkspace (workspace) {
59
- try {
60
- const auth =
61
- Buffer.from(this.user + ':' + this.password).toString('base64');
62
- const response = await fetch(this.url + 'workspaces/' + workspace + '/styles.json', {
63
- credentials: 'include',
64
- method: 'GET',
65
- headers: {
66
- Authorization: 'Basic ' + auth
67
- }
68
- });
69
-
70
- if (response.status === 200) {
71
- return await response.json();
72
- } else {
73
- console.warn(await response.text());
74
- return false;
56
+ const response = await fetch(this.url + 'workspaces/' + workspace + '/styles.json', {
57
+ credentials: 'include',
58
+ method: 'GET',
59
+ headers: {
60
+ Authorization: this.auth
75
61
  }
76
- } catch (error) {
77
- return false;
62
+ });
63
+
64
+ if (!response.ok) {
65
+ const geoServerResponse = await getGeoServerResponseText(response);
66
+ throw new GeoServerResponseError(null, geoServerResponse);
78
67
  }
68
+ return response.json();
79
69
  }
80
70
 
81
71
  /**
82
72
  * Returns all styles defined in workspaces.
83
73
  *
84
- * @returns {Object|Boolean} An object with all styles or 'false'
74
+ * @throws Error if request fails
75
+ *
76
+ * @returns {Object[]} An array with all style objects
85
77
  */
86
78
  async getAllWorkspaceStyles () {
87
- try {
88
- const allStyles = [];
89
- const ws = new WorkspaceClient(this.url, this.user, this.password);
90
- const allWs = await ws.getAll();
79
+ const allStyles = [];
80
+ const ws = new WorkspaceClient(this.url, this.auth);
81
+ const allWs = await ws.getAll();
82
+
83
+ if (!allWs ||
84
+ !allWs.workspaces ||
85
+ !allWs.workspaces.workspace ||
86
+ !Array.isArray(allWs.workspaces.workspace)) {
87
+ throw new GeoServerResponseError('Response of available workspaces is malformed');
88
+ }
91
89
 
92
- // go over all workspaces and query the styles for
93
- for (let i = 0; i < allWs.workspaces.workspace.length; i++) {
94
- const ws = allWs.workspaces.workspace[i];
95
- const wsStyles = await this.getInWorkspace(ws.name);
90
+ // go over all workspaces and query the styles for
91
+ for (let i = 0; i < allWs.workspaces.workspace.length; i++) {
92
+ const ws = allWs.workspaces.workspace[i];
93
+ const wsStyles = await this.getInWorkspace(ws.name);
96
94
 
97
- if (wsStyles.styles.style) {
98
- wsStyles.styles.style.forEach(wsStyle => {
99
- allStyles.push(wsStyle);
100
- });
101
- }
95
+ if (wsStyles.styles.style) {
96
+ wsStyles.styles.style.forEach(wsStyle => {
97
+ allStyles.push(wsStyle);
98
+ });
102
99
  }
103
-
104
- return allStyles;
105
- } catch (error) {
106
- return false;
107
100
  }
101
+
102
+ return allStyles;
108
103
  }
109
104
 
110
105
  /**
111
106
  * Returns all styles as combined object (default ones and those in
112
107
  * workspaces).
113
108
  *
114
- * @returns {Object|Boolean} An object with all styles or 'false'
109
+ * @returns {Object[]} An array with all style objects
115
110
  */
116
111
  async getAll () {
117
- try {
118
- const defaultStyles = await this.getDefaults();
119
- const wsStyles = await this.getAllWorkspaceStyles();
120
- const allStyles = defaultStyles.styles.style.concat(wsStyles);
121
-
122
- return allStyles;
123
- } catch (error) {
124
- return false;
112
+ const defaultStyles = await this.getDefaults();
113
+ const wsStyles = await this.getAllWorkspaceStyles();
114
+ if (
115
+ !defaultStyles ||
116
+ !defaultStyles.styles ||
117
+ !defaultStyles.styles.style ||
118
+ !Array.isArray(defaultStyles.styles.style)
119
+ ) {
120
+ throw new GeoServerResponseError('Response of default styles malformed')
125
121
  }
122
+ const allStyles = defaultStyles.styles.style.concat(wsStyles);
123
+
124
+ return allStyles;
126
125
  }
127
126
 
128
127
  /**
@@ -132,29 +131,22 @@ export default class StyleClient {
132
131
  * @param {String} name Name of the style
133
132
  * @param {String} sldBody SLD style (as XML text)
134
133
  *
135
- * @returns {Boolean} If the style could be published
134
+ * @throws Error if request fails
136
135
  */
137
136
  async publish (workspace, name, sldBody) {
138
- try {
139
- const auth = Buffer.from(this.user + ':' + this.password).toString('base64');
140
- const response = await fetch(this.url + 'workspaces/' + workspace + '/styles?name=' + name, {
141
- credentials: 'include',
142
- method: 'POST',
143
- headers: {
144
- Authorization: 'Basic ' + auth,
145
- 'Content-Type': 'application/vnd.ogc.sld+xml'
146
- },
147
- body: sldBody
148
- });
137
+ const response = await fetch(this.url + 'workspaces/' + workspace + '/styles?name=' + name, {
138
+ credentials: 'include',
139
+ method: 'POST',
140
+ headers: {
141
+ Authorization: this.auth,
142
+ 'Content-Type': 'application/vnd.ogc.sld+xml'
143
+ },
144
+ body: sldBody
145
+ });
149
146
 
150
- if (response.status === 201) {
151
- return true;
152
- } else {
153
- console.warn(await response.text());
154
- return false;
155
- }
156
- } catch (error) {
157
- return false;
147
+ if (!response.ok) {
148
+ const geoServerResponse = await getGeoServerResponseText(response);
149
+ throw new GeoServerResponseError(null, geoServerResponse);
158
150
  }
159
151
  }
160
152
 
@@ -165,8 +157,6 @@ export default class StyleClient {
165
157
  * @param {String} name The name of the style to delete
166
158
  * @param {Boolean} [recurse=false] If references to the specified style in existing layers should be deleted
167
159
  * @param {Boolean} [purge=false] Whether the underlying file containing the style should be deleted on disk
168
- *
169
- * @returns {Boolean} If the style could be deleted
170
160
  */
171
161
  async delete (workspace, name, recurse, purge) {
172
162
  let paramPurge = false;
@@ -179,117 +169,121 @@ export default class StyleClient {
179
169
  paramRecurse = true;
180
170
  }
181
171
 
182
- try {
183
- const auth = Buffer.from(this.user + ':' + this.password).toString('base64');
184
- let endpoint;
172
+ let endpoint;
185
173
 
186
- if (workspace) {
187
- // delete style inside workspace
188
- endpoint = this.url + 'workspaces/' + workspace + '/styles/' + name +
174
+ if (workspace) {
175
+ // delete style inside workspace
176
+ endpoint = this.url + 'workspaces/' + workspace + '/styles/' + name +
189
177
  '?' + 'purge=' + paramPurge + '&' + 'recurse=' + paramRecurse;
190
- } else {
191
- // delete style without workspace
192
- endpoint = this.url + 'styles/' + name +
178
+ } else {
179
+ // delete style without workspace
180
+ endpoint = this.url + 'styles/' + name +
193
181
  '?' + 'purge=' + paramPurge + '&' + 'recurse=' + paramRecurse;
194
- }
182
+ }
195
183
 
196
- const response = await fetch(endpoint, {
197
- credentials: 'include',
198
- method: 'DELETE',
199
- headers: {
200
- Authorization: 'Basic ' + auth
201
- }
202
- });
184
+ const response = await fetch(endpoint, {
185
+ credentials: 'include',
186
+ method: 'DELETE',
187
+ headers: {
188
+ Authorization: this.auth
189
+ }
190
+ });
203
191
 
204
- if (response.ok) {
205
- return true;
206
- } else if (response.status === 403) {
207
- console.warn('Deletion failed. There might be dependant objects to ' +
208
- 'this style. Delete them first or call this with "recurse=false"');
209
- console.warn(await response.text());
210
- return false;
211
- } else {
212
- console.warn(await response.text());
213
- return false;
192
+ if (!response.ok) {
193
+ const geoServerResponse = await getGeoServerResponseText(response);
194
+ switch (response.status) {
195
+ case 403:
196
+ throw new GeoServerResponseError(
197
+ 'Deletion failed. There might be dependant layers to this style. Delete them first or call this with "recurse=false"',
198
+ geoServerResponse
199
+ );
200
+ default:
201
+ throw new GeoServerResponseError(null, geoServerResponse);
214
202
  }
215
- } catch (error) {
216
- return false;
217
203
  }
218
204
  }
219
205
 
220
206
  /**
221
207
  * Assigns a style to a layer.
222
208
  *
223
- * @param {String} qualifiedName GeoServer layer name with workspace prefix
209
+ * @param {String} workspaceOfLayer The name of the layer's workspace, can be undefined
210
+ * @param {String} layerName The name of the layer to query
211
+ * @param {String} workspaceOfStyle The workspace of the style, can be undefined
224
212
  * @param {String} styleName The name of the style
225
- * @param {String} [workspaceStyle] The workspace of the style
226
213
  * @param {Boolean} [isDefaultStyle=true] If the style should be the default style of the layer
227
214
  *
228
- * @returns {Boolean} If the style could be assigned
215
+ * @throws Error if request fails
229
216
  */
230
- async assignStyleToLayer (qualifiedName, styleName, workspaceStyle, isDefaultStyle) {
231
- try {
232
- const auth = Buffer.from(this.user + ':' + this.password).toString('base64');
217
+ async assignStyleToLayer (workspaceOfLayer, layerName, workspaceOfStyle, styleName, isDefaultStyle) {
218
+ let qualifiedName;
219
+ if (workspaceOfLayer) {
220
+ qualifiedName = `${workspaceOfLayer}:${layerName}`;
221
+ } else {
222
+ qualifiedName = layerName;
223
+ }
224
+ const styleBody = await this.getStyleInformation(workspaceOfStyle, styleName);
233
225
 
234
- const styleBody = await this.getStyleInformation(styleName, workspaceStyle);
226
+ let url;
227
+ // we set the style as defaultStyle, unless user explicitly provides 'false'
228
+ if (isDefaultStyle !== false) {
229
+ url = this.url + 'layers/' + qualifiedName + '/styles?default=true';
230
+ } else {
231
+ url = this.url + 'layers/' + qualifiedName + '/styles';
232
+ }
235
233
 
236
- const response = await fetch(this.url + 'layers/' + qualifiedName + '/styles?default=' + isDefaultStyle, {
237
- credentials: 'include',
238
- method: 'POST',
239
- headers: {
240
- Authorization: 'Basic ' + auth,
241
- 'Content-Type': 'application/json'
242
- },
243
- body: JSON.stringify(styleBody)
244
- });
234
+ const response = await fetch(url, {
235
+ credentials: 'include',
236
+ method: 'POST',
237
+ headers: {
238
+ Authorization: this.auth,
239
+ 'Content-Type': 'application/json'
240
+ },
241
+ body: JSON.stringify(styleBody)
242
+ });
245
243
 
246
- if (response.status === 201) {
247
- return true;
248
- } else {
249
- console.warn(await response.text());
250
- return false;
251
- }
252
- } catch (error) {
253
- return false;
244
+ if (!response.ok) {
245
+ const geoServerResponse = await getGeoServerResponseText(response);
246
+ throw new GeoServerResponseError(null, geoServerResponse);
254
247
  }
255
248
  }
256
249
 
257
250
  /**
258
251
  * Get information about a style.
259
252
  *
253
+ * @param {String} workspace The name of the workspace, can be undefined
260
254
  * @param {String} styleName The name of the style
261
- * @param {String} [workspace] The name of the workspace
262
255
  *
263
- * @returns {Object|Boolean} An object about the style or 'false'
256
+ * @throws Error if request fails
257
+ *
258
+ * @returns {Object} An object about the style or undefined if it cannot be found
264
259
  */
265
- async getStyleInformation (styleName, workspace) {
266
- try {
267
- const auth =
268
- Buffer.from(this.user + ':' + this.password).toString('base64');
260
+ async getStyleInformation (workspace, styleName) {
261
+ let url;
262
+ if (workspace) {
263
+ url = this.url + 'workspaces/' + workspace + '/styles/' + styleName + '.json';
264
+ } else {
265
+ url = this.url + 'styles/' + styleName + '.json';
266
+ }
269
267
 
270
- let url;
271
- if (workspace) {
272
- url = this.url + 'workspaces/' + workspace + '/styles/' + styleName + '.json';
273
- } else {
274
- url = this.url + 'styles/' + styleName + '.json';
268
+ const response = await fetch(url, {
269
+ credentials: 'include',
270
+ method: 'GET',
271
+ headers: {
272
+ Authorization: this.auth
275
273
  }
274
+ });
276
275
 
277
- const response = await fetch(url, {
278
- credentials: 'include',
279
- method: 'GET',
280
- headers: {
281
- Authorization: 'Basic ' + auth
282
- }
283
- });
284
-
285
- if (response.status === 200) {
286
- return await response.json();
276
+ if (!response.ok) {
277
+ const grc = new AboutClient(this.url, this.auth);
278
+ if (await grc.exists()) {
279
+ // GeoServer exists, but requested item does not exist, we return empty
280
+ return;
287
281
  } else {
288
- console.warn(await response.text());
289
- return false;
282
+ // There was a general problem with GeoServer
283
+ const geoServerResponse = await getGeoServerResponseText(response);
284
+ throw new GeoServerResponseError(null, geoServerResponse);
290
285
  }
291
- } catch (error) {
292
- return false;
293
286
  }
287
+ return response.json();
294
288
  }
295
289
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Utility functions and classes
3
+ */
4
+
5
+ /**
6
+ * Return the GeoServer response text if available.
7
+ *
8
+ * @param {Response} response The response of the GeoServer
9
+ *
10
+ * @returns {String} The response text if available
11
+ */
12
+ async function getGeoServerResponseText (response) {
13
+ try {
14
+ return response.text()
15
+ } catch (e) {
16
+ // return nothing
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Generic GeoServer error
22
+ */
23
+ class GeoServerResponseError extends Error {
24
+ /**
25
+ * @param {String} [message=GeoServer Response Error] The error message
26
+ * @param {String} [geoServerOutput] The error output from GeoServer (useful for debugging)
27
+ */
28
+ constructor (message, geoServerOutput) {
29
+ super(message)
30
+ this.name = 'GeoServerResponseError';
31
+ this.message = message || 'GeoServer Response Error'
32
+
33
+ // custom property as explained here: https://xjamundx.medium.com/custom-javascript-errors-in-es6-aa891b173f87
34
+ this.geoServerOutput = geoServerOutput;
35
+ }
36
+ }
37
+
38
+ export {
39
+ getGeoServerResponseText,
40
+ GeoServerResponseError
41
+ }