Source: src/style.js

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