GetFeature.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // @require WFS_URL
  2. if (!WFS_URL) throw "Missing WFS_URL"
  3. var DBG = DBG || false
  4. var httpFetch = window.fetch
  5. // wfsFeature json format:
  6. /*
  7. {
  8. $fieldName:
  9. - string => field value
  10. - array of objects => ref features as objects (wfsFeature)
  11. - array of strings => ref features as xlink url
  12. }
  13. */
  14. function p5WFS_ParseFeatureFieldRecurse(tagNode) { // @returns object from xml element
  15. var item = {}
  16. var i = 0
  17. for (i = 0; i < tagNode.children.length; i++) {
  18. var fieldNode = tagNode.children[i]
  19. if ('p5:links' === fieldNode.tagName) {
  20. // <p5:links>
  21. // ​<p5:next p5:typeName=​"default_db__x3A__BI_audit_KW_requested_person:​BI_audit_KW_requested_person"
  22. // p5:backRefNS=​"default_db/​BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/​BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA"
  23. // p5:backRefPK=​"20"
  24. // p5:startIndex=​"10">
  25. // ​"https://.../wfs-data.php/default_db/?SERVICE=WFS&amp;VERSION=1.0.0&amp;TYPENAME=default_db__x3A__BI_audit_KW_requested_person:BI_audit_KW_requested_person&amp;REQUEST=GetFeature&amp;backRefNS=default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA&amp;backRefPK=20&amp;backRefField=default_db__x3A__BI_audit_KW_requested_person:BI_audit_KW_requested_person&amp;startIndex=10"
  26. // </p5:next>​</p5:links>​
  27. for (var j = 0; j < fieldNode.children.length; j++) {
  28. var p5LinkNode = fieldNode.children[j]
  29. if ('p5:next' === p5LinkNode.tagName) {
  30. var typeName = p5LinkNode.getAttribute('p5:typeName')
  31. item[ typeName ].push( {
  32. 'type': 'next',
  33. '@backRefNS': p5LinkNode.getAttribute('p5:backRefNS'),
  34. '@backRefPK': p5LinkNode.getAttribute('p5:backRefPK'),
  35. '@typeName': p5LinkNode.getAttribute('p5:typeName'),
  36. '@startIndex': p5LinkNode.getAttribute('p5:startIndex'),
  37. 'value': p5LinkNode.textContent
  38. } )
  39. } else {
  40. if (DBG) console.warn("Not implemented p5:links/" + p5LinkNode.tagName)
  41. }
  42. }
  43. }
  44. else if (!fieldNode.children.length) {
  45. if (fieldNode.textContent) item[ fieldNode.localName ] = fieldNode.textContent
  46. else {
  47. var xlink = fieldNode.getAttribute('xlink:href')
  48. if (xlink) {
  49. if (!(fieldNode.tagName in item)) item[ fieldNode.tagName ] = []
  50. item[ fieldNode.tagName ].push( xlink )
  51. }
  52. }
  53. }
  54. else {
  55. if (!(fieldNode.tagName in item)) item[ fieldNode.tagName ] = []
  56. item[ fieldNode.tagName ].push( p5WFS_ParseFeatureFieldRecurse(fieldNode) )
  57. }
  58. }
  59. return item
  60. }
  61. function p5WFS_ParseFeatureMember(featureMember) { // @returns object from xml element <gml:featureMemeber>
  62. if (!featureMember) return null
  63. if (!featureMember.children.length) return null
  64. var featureNode = featureMember.children[0]
  65. if (!featureNode) return null
  66. if (!featureNode.children.length) return null
  67. return p5WFS_ParseFeatureFieldRecurse(featureNode)
  68. }
  69. /**
  70. * @usage
  71. p5WFS_GetFeature('p5_default_db:CRM_PROCES', {
  72. 'featureID': 'CRM_PROCES.' + id,
  73. }).then(function (features) {
  74. // ...
  75. }).catch(function (e) {
  76. // ...
  77. })
  78. * @usage - expected one feature
  79. p5WFS_GetFeature('p5_default_db:CRM_PROCES', {
  80. 'featureID': 'CRM_PROCES.' + id,
  81. }).then(function (features) {
  82. if (!features) throw "Nie odnaleziono rekordu id = " + id
  83. if (!features.length) throw "Nie odnaleziono rekordu id = " + id
  84. if (1 !== features.length) throw "Bład: API zwróciło za dużo rekordów"
  85. return features[0]
  86. console.log('fetched features', features)
  87. }).then(function (feature) {
  88. // ...
  89. }).catch(function (e) {
  90. // ...
  91. })
  92. * @usage - POST
  93. p5WFS_GetFeature('p5_default_db:CRM_PROCES', {
  94. // 'Filter': '<ogc:PropertyIsEqualTo><ogc:PropertyName>' + "PARENT_ID" + '</ogc:PropertyName><ogc:Literal>' + parent_id + '</ogc:Literal></ogc:PropertyIsEqualTo>',
  95. 'sortBy': 'SORT_PRIO+A,ID',
  96. 'ogc:Filter': '<GetFeature xmlns="http://www.opengis.net/wfs/2.0"'+
  97. ' xmlns:p5_default_db="' + BASE_WFS_URL + '/default_db"' +
  98. ' xmlns:ogc="http://www.opengis.net/ogc"' +
  99. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
  100. ' service="WFS"' +
  101. ' version="2.0.2"' +
  102. ' xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd">' +
  103. '<ogc:Filter>' +
  104. '<ogc:PropertyIsEqualTo><ogc:PropertyName>' + "PARENT_ID" + '</ogc:PropertyName><ogc:Literal>' + parent_id + '</ogc:Literal></ogc:PropertyIsEqualTo>' +
  105. '</ogc:Filter>' +
  106. '</GetFeature>'
  107. }).then(function (features) {
  108. ...
  109. */
  110. function p5WFS_GetFeature(typeName, query) { // @returns Promise
  111. var postData = null
  112. var link = WFS_URL + '?SERVICE=WFS&VERSION=1.0.0' +
  113. '&REQUEST=GetFeature' +
  114. '&TYPENAME=' + typeName +
  115. '&SRSNAME=EPSG:3003'
  116. ;
  117. if ('WFS_URL' in query && query['WFS_URL'].length) {
  118. link = query['WFS_URL']
  119. link += ( (-1 !== query['WFS_URL'].indexOf('?')) ? '&' : '?' )
  120. link += 'SERVICE=WFS&VERSION=1.0.0'
  121. link += '&REQUEST=GetFeature'
  122. link += '&TYPENAME=' + typeName
  123. link += '&SRSNAME=EPSG:3003'
  124. }
  125. // '&Filter=<ogc:Filter><ogc:Or><ogc:PropertyIsEqualTo><ogc:PropertyName>ID</ogc:PropertyName><ogc:Literal>' + id + '</ogc:Literal></ogc:PropertyIsEqualTo></ogc:Or></ogc:Filter>'
  126. var query = query || {}
  127. if ('featureID' in query) link += '&featureID=' + query['featureID']
  128. if ('Filter' in query && 'string' === typeof query['Filter']) {
  129. var ogcFilter = ('<ogc:Filter>' !== query['Filter'].substr(0, '<ogc:Filter>'.length))
  130. ? '<ogc:Filter>' + query['Filter'] + '</ogc:Filter>'
  131. : query['Filter']
  132. ;
  133. link += '&Filter=' + ogcFilter
  134. }
  135. if ('ogc:Filter' in query && 'string' === typeof query['ogc:Filter']) {
  136. var headerOgcFilter = '<GetFeature xmlns="http://www.opengis.net/wfs/2.0"' +
  137. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +
  138. ' xmlns:ogc="http://www.opengis.net/ogc"' +
  139. ' xmlns:wfs="http://www.opengis.net/wfs"' +
  140. ' service="WFS"' +
  141. ' version="2.0.2"' + '>'
  142. if ('<GetFeature' === query['ogc:Filter'].substr(0, '<GetFeature'.length)) {
  143. postData = query['ogc:Filter']
  144. } else if ('<ogc:Filter>' === query['ogc:Filter'].substr(0, '<ogc:Filter>'.length)) {
  145. postData = headerOgcFilter + '<wfs:Query>' + query['ogc:Filter'] + '</wfs:Query>' + '</GetFeature>'
  146. } else if ('<wfs:Query>' === query['ogc:Filter'].substr(0, '<wfs:Query>'.length)) {
  147. postData = headerOgcFilter + query['ogc:Filter'] + '</GetFeature>'
  148. } else {
  149. return Promise.reject(new Error('Wrong syntax ogc:Filter'))
  150. }
  151. }
  152. if ('graphql:query' in query && 'string' === typeof query['graphql:query']) { // TODO: http://graphql.org/learn/queries/
  153. link += '&outputFormat=json'
  154. postData = query['graphql:query']
  155. }
  156. if ('sortBy' in query) link += '&sortBy=' + query['sortBy']
  157. if ('maxFeatures' in query) link += '&maxFeatures=' + query['maxFeatures']
  158. if ('count' in query) link += '&count=' + query['count']
  159. if ('startIndex' in query) link += '&startIndex=' + query['startIndex']
  160. if ('primaryKey' in query) link += '&primaryKey=' + query['primaryKey']
  161. if ('resolveDepth' in query && !('resolve' in query)) return Promise.reject(new Error('ResolveDepth set but missing resolve="all"'))
  162. if ('resolve' in query) link += '&resolve=' + query['resolve']
  163. if ('resolveDepth' in query) link += '&resolveDepth=' + query['resolveDepth']
  164. if ('xlink' in query) link += '&xlink=' + query['xlink'] // NOTE: replace '#' to '/'
  165. if ('root' in query) link += '&root=' + query['root'] // NOTE: require admin perms (admin level 0)
  166. if ('backRefNS' in query && 'backRefPK' in query && 'backRefField' in query) {
  167. link += '&backRefNS=' + query['backRefNS']
  168. link += '&backRefPK=' + query['backRefPK']
  169. link += '&backRefField=' + query['backRefField']
  170. }
  171. if ('p5:next' in query) link = query['p5:next'];
  172. var method = (postData) ? 'POST' : 'GET'
  173. var wfsPromise = httpFetch(link, Object.assign({
  174. method: method,
  175. credentials: 'include',
  176. }, ('POST' === method)
  177. ? {
  178. body: postData
  179. }
  180. : {}
  181. )).then(function (response) {
  182. return response.text()
  183. });
  184. return _p5WFS_GetFeaturePromise(wfsPromise);
  185. }
  186. function p5WFS_GetFeatureFromXml(url) {
  187. var wfsPromise = httpFetch(url, {
  188. method: 'GET',
  189. credentials: 'include',
  190. }).then(function (response) {
  191. return response.text()
  192. });
  193. return _p5WFS_GetFeaturePromise(wfsPromise);
  194. }
  195. function p5WFS_GetFeatureFromXmlString(xmlString) {
  196. var wfsPromise = new Promise(function (resolve, reject) {
  197. resolve(xmlString);
  198. });
  199. return _p5WFS_GetFeaturePromise(wfsPromise);
  200. }
  201. function _p5WFS_GetFeaturePromise(wfsPromise) {
  202. return wfsPromise.then(function (responseText) {
  203. return (new DOMParser()).parseFromString(responseText, "text/xml");
  204. }).then(function (xmlResponse) {
  205. if (!xmlResponse.children.length) throw "Missing response xml root element"
  206. return xmlResponse.children[0]
  207. }).then(function (xmlRoot) {
  208. if ('html' === xmlRoot.localName) {
  209. // console.log(xmlRoot) // html / body / parseerror / div
  210. throw "Parse xml error"
  211. }
  212. if ('FeatureCollection' !== xmlRoot.localName) {
  213. if ('ServiceExceptionReport' === xmlRoot.tagName) {
  214. if (xmlRoot.children[0] && xmlRoot.children[0].textContent) throw xmlRoot.children[0].textContent
  215. throw "WFS API Exception"
  216. }
  217. throw "Missing FeatureCollection as root element in wfs response"
  218. }
  219. var features = []
  220. for (var i = 0; i < xmlRoot.children.length; i++) {
  221. features.push(p5WFS_ParseFeatureMember(xmlRoot.children[i]))
  222. }
  223. return features.filter(function (item) { return !!item; })
  224. })
  225. }
  226. module.exports = {
  227. p5WFS_ParseFeatureMember: p5WFS_ParseFeatureMember,
  228. p5WFS_ParseFeatureFieldRecurse: p5WFS_ParseFeatureFieldRecurse,
  229. p5WFS_GetFeature: p5WFS_GetFeature,
  230. p5WFS_GetFeatureFromXml: p5WFS_GetFeatureFromXml,
  231. p5WFS_GetFeatureFromXmlString: p5WFS_GetFeatureFromXmlString,
  232. }