@nextgis/ngw-connector
Version:
A lightweight HTTP client optimized for use with NextGIS Web API
3 lines (2 loc) • 18.7 kB
JavaScript
"use strict";var e=require("@nextgis/utils"),t=require("@nextgis/cache"),r=require("events");const s=/\{ *([\w_-]+) *\}/g;function n(e,t){return e.replace(s,(e,r)=>{let s=t[r];if(void 0===s)throw new Error("No value provided for letiable "+e);return"function"==typeof s&&(s=s(t)),s})}var o="3.0.1";class i extends Error{constructor(e="AbortError"){super(e),this.name="AbortError"}}var a=Object.defineProperty,c=(e,t,r)=>((e,t,r)=>t in e?a(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);class u extends Error{constructor(e){super(),c(this,"name","NgwError"),c(this,"title"),c(this,"message"),c(this,"detail"),c(this,"exception"),c(this,"status_code"),c(this,"data"),c(this,"guru_meditation"),Object.assign(this,e),Object.setPrototypeOf(this,u.prototype)}}var l=Object.defineProperty,h=(e,t,r)=>((e,t,r)=>t in e?l(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);class d extends u{constructor(e){super(e),h(this,"name","InsufficientPermissionsError"),h(this,"exception","nextgisweb.core.exception.InsufficientPermissions"),Object.setPrototypeOf(this,d.prototype)}}var p=Object.defineProperty,f=(e,t,r)=>((e,t,r)=>t in e?p(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);let m=class e extends u{constructor(t){super(t),f(this,"message","There is no response from the server or problem connecting to server."),f(this,"title","Network error"),f(this,"detail","Check network connectivity and try again later."),Object.setPrototypeOf(this,e.prototype)}};function g(t){return!!e.isObject(t)&&(t.status_code&&t.exception&&t.title)}var y=Object.defineProperty,w=(e,t,r)=>((e,t,r)=>t in e?y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,t+"",r);class b extends Error{constructor(e){super(),w(this,"name","NetworkError"),Object.setPrototypeOf(this,b.prototype),this.message=`Unable to request ${e}.\n Possibly invalid NGW URL entered or CORS not configured to get request from ${location.origin}`}}var v=Object.defineProperty,O=(e,t,r)=>((e,t,r)=>t in e?v(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);class E extends u{constructor(e){super(e),O(this,"name","ResourceNotFoundError"),O(this,"exception","nextgisweb.resource.exception.ResourceNotFound"),Object.setPrototypeOf(this,E.prototype)}}var R=Object.freeze({__proto__:null,AbortError:i,InsufficientPermissionsError:d,NetworkError:b,NetworksResponseError:m,NgwError:u,ResourceNotFoundError:E,extractError:function(t){if(e.isObject(t))return t.name&&t.message&&t.title?{title:t.title,message:t.message,detail:t.detail||null,data:t.data&&t.data.data?t.data.data:null}:!t.exception||void 0!==t.status&&0!==t.status&&void 0!==t.data?{title:"string"==typeof t.title?t.title:"Unexpected error",message:"string"==typeof t.message?t.message:"Something went wrong."}:new m({title:t.title,status_code:t.status_code,exception:t.exception})},isError:g}),j=Object.defineProperty,x=(e,t,r)=>((e,t,r)=>t in e?j(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);class q extends Error{constructor(e){super(e||"Something went wrong."),x(this,"title"),this.name="BaseAPIError",this.title="Unknown API error",Error.captureStackTrace&&Error.captureStackTrace(this,q)}}class C extends q{constructor(e){super(e||"There is no response from the server or problem connecting to server."),x(this,"detail"),this.title="Network error",this.detail="Check network connectivity and try again later."}}class _ extends q{constructor(e){super(e||"Something went wrong."),this.title="Unexpected server response"}}class I extends q{constructor(e){super(e.message),x(this,"detail"),x(this,"data"),this.title=e.title||this.title,this.detail=e.detail||null,this.data=e}}class P extends Error{constructor(e,t={}){super(e||"Unexpected error while processing long-running request."),x(this,"title"),x(this,"data"),this.name="LunkwillError",this.title="Long-running request error",this.data=t,Error.captureStackTrace&&Error.captureStackTrace(this,P)}}class k extends P{constructor(e){super("Long-running request was cancelled.",e)}}class U extends P{constructor(e){super(void 0,e)}}async function T(e){const t=await async function(e){try{return e.json()}catch{throw new Error}}(e);let r=t.delay_ms;const s=void 0!==t.retry_ms?t.retry_ms:2e3,n=`/api/lunkwill/${t.id}/summary`,o=`/api/lunkwill/${t.id}/response`,i=e=>new Promise(t=>setTimeout(t,e));let a=!1,c=!1;for(;!c;){let e,t;await i(a?s:r),a=!1;try{e=await fetch(n,{credentials:"same-origin"}),t=await e.json()}catch{a=!0;continue}switch(t.status){case void 0:throw new P(void 0,t);case"ready":c=!0;break;case"cancelled":throw new k(t);case"failed":throw new U(t);case"spooled":case"processing":case"buffering":r=t.delay_ms;break;default:throw new P(void 0,t)}}return o}const $=new RegExp(["application/pdf","image/png","image/jpeg","image/tiff","text/csv"].join("|"));function A(e,t){let r="";return void 0!==t&&(r="?"+function(e){const t=[];for(const[r,s]of Object.entries(e))if("string"==typeof s||"number"==typeof s||"boolean"==typeof s)t.push(`${r}=${encodeURIComponent(s)}`);else if(Array.isArray(s))t.push(`${r}=${s.map(encodeURIComponent).join(",")}`);else for(const[e,n]of Object.entries(s)){const s=`${r}[${encodeURIComponent(e)}]`;if("string"==typeof n||"number"==typeof n||"boolean"==typeof n)t.push(`${s}=${encodeURIComponent(n)}`);else if(Array.isArray(n)){const e=n.map(encodeURIComponent);t.push(`${s}=${e.join(",")}`)}}return t.join("&")}(t)),e+r}async function S(t,r,s){var n,o;const{withCredentials:i,responseType:a,cacheProps:c,cacheName:u,lunkwill:l,cache:h,query:d,json:p,...f}={method:"GET",credentials:"same-origin",headers:{},...r};f.method=null==(n=f.method)?void 0:n.toUpperCase(),i&&(f.credentials="include");let m=!1;void 0!==l&&(l.toHeaders(f.headers||{}),m=!0);const g=!!f.lunkwillReturnUrl;if(delete f.lunkwillReturnUrl,void 0!==p){f.body=JSON.stringify(p);const e=f.headers||{};e["Content-Type"]="application/json",f.headers=e}const y=A(t,d),w=async()=>{let e;try{e=await fetch(y,f)}catch(n){if(f.signal&&f.signal.aborted)throw n;throw new C}if(m&&function(e){const t=e.headers.get("content-type");return null!=t&&t.includes("application/vnd.lunkwill.request-summary+json")}(e)){const t=await T(e);if(g)return t;e=await async function(e){try{return await window.fetch(e,{credentials:"same-origin"})}catch{throw new C}}(t)}const t=e.headers.get("content-type"),r=t&&(t.includes("application/json")||t.includes("application/vnd.lunkwill.request-summary+json"));let s;try{const n=t&&$.test(t);if("blob"===a||n)s=await e.blob();else{if(!r)throw new _;s=await e.json()}}catch(n){if("AbortError"===n.name||n instanceof _)throw n;throw new _}if(400<=e.status&&e.status<=599)throw new I(s);return s};if(s)if("GET"===(null==(o=f.method)?void 0:o.toUpperCase())){if(!1!==h){const t=c||{...e.objectRemoveEmpty({withCredentials:i,responseType:a})};return s.add(u||y,w,{props:t,expirationTime:h?void 0:500})}}else{["api/feature_layer/identify"].every(e=>!t.includes(e))&&s.clean()}return w()}function N(t,r,s,...n){const[o,...i]=s[t],a=n[0];let c;if(void 0===a)c=[];else if("object"==typeof a&&null!==a){if(n.length>1)throw new Error("Too many arguments for route(name, object)!");c=[];for(const[e,t]of Object.entries(a))c[i.indexOf(e)]=String(t)}else c=n.map(e=>String(e));return e.fixUrlStr(r+o.replace(/\{(\w+)\}/g,function(e,t){const r=parseInt(t),s=c[r];if(void 0===s){throw new Error(`Undefined parameter ${r} in "${o}".`)}return String(s)}))}function B(e){return"[object Object]"===Object.prototype.toString.call(e)}let F;{const e=require("url"),t=require("http"),r=require("https"),s=require("form-data"),n=s=>({"http:":t,"https:":r}[e.parse(s).protocol||"https:"]);F=(e,t,r={},o,i)=>{const{file:a,headers:c,method:u,data:l,responseType:h}=r;return new Promise((t,r)=>{const o=n(e);if(!o)throw new Error(`Given URL '${e}' is not correct`);{const n={headers:c||{},method:u},d="string"==typeof l?l:JSON.stringify(l);let p,f=a;if(a){const e={};if(B(a)&&"file"in a&&("filename"in a||"name"in a)){const{file:t,name:r,...s}=a;r&&!s.filename&&(s.filename=r),Object.assign(e,s),f=t}if(p=new s,p.append("file",f,e),l)for(const t in l)p.append(t,l[t]);Object.assign(n.headers,{...p.getHeaders()})}void 0!==d&&Object.assign(n.headers,{"content-type":"application/json","content-length":Buffer.byteLength(d)});const m=o.request(e,n,e=>{let s="";e.on("data",e=>{s+=e}),e.on("end",()=>{if(s)if("blob"===h)t(s);else{let n;try{n=JSON.parse(s),n&&n.status_code&&n.status_code&&r(n.message)}catch(e){r(e)}void 0!==n&&(g(n)?r("extractError(json)"):t(n))}r("no data")})});p&&p.pipe(m),m.on("error",e=>{r(e)}),d&&m.write(d),i(()=>{m.abort()}),m.end()}}).then(e=>(t&&t(e),e)).catch(e=>{if(!o)throw new Error(e);o(e)})}}const L=[];function G(e){L.push(e)}var H=Object.defineProperty,z=(e,t,r)=>((e,t,r)=>t in e?H(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);let D=0,Q=0;class J{constructor(s){this.options=s,z(this,"id",D++),z(this,"emitter",new r.EventEmitter),z(this,"user"),z(this,"cache"),z(this,"withCredentials"),z(this,"routeCache"),z(this,"client",`NextGIS-NGW-Connector/${o}`),z(this,"routeStr","/api/component/pyramid/route"),z(this,"activeRequests",{}),z(this,"requestTransform");const n=function(t){return L.find(r=>{if(r.options.baseUrl===t.baseUrl){if(!t.auth)return!0;if(r.options.auth)return e.objectDeepEqual(r.options.auth,t.auth)}})}(s);if(this.cache=new t({namespace:s.cacheId}),this.routeCache=new t({namespace:"routecache"}),n)return n;{const{route:e,requestTransform:t,withCredentials:r}=this.options;e&&(this.routeStr=e),t&&(this.requestTransform=t),void 0!==r&&(this.withCredentials=r),G(this)}}clearCache(){this.cache.clean()}setRequestTransform(e){this.requestTransform=e}setNgw(e){this.logout(),this.options.baseUrl=e,G(this)}async connect({signal:e}={}){const t=this.options.auth;if(t){const{login:e,password:r}=t;e&&r&&await this._login({login:e,password:r})}const r=`${this.routeStr}?client=${this.client}`;return this.routeCache.add(this.options.baseUrl||String(this.id),()=>this.makeQuery(r,null,{signal:e,cache:!1}))}login(e,t){return this.logout(),G(this),this._login(e,t)}logout(){this.abort(),function(e){const t=L.indexOf(e);-1!==t&&L.splice(t,1)}(this),this.options.auth=void 0,this.user=void 0,this.routeCache.clean(),this.clearCache(),this.emitter.emit("logout")}async getUserInfo(e,t){if(this.user&&this.user.id)return this.user;e&&(this.options.auth=e);const r={headers:this.getAuthorizationHeaders(e),cache:!0,...t};return this.makeQuery("/api/component/auth/current_user",{},r)}getAuthorizationHeaders(e){const t=this.makeClientId(e);return t?{Authorization:`Basic ${t}`}:{}}makeClientId(e){if(e=e||this.options.auth){const{login:t,password:r}=e;return Buffer.from(`${t}:${r}`).toString("base64")}}abort(){for(const e of Object.values(this.activeRequests))e.abort();this.activeRequests={}}getActiveApiRequests(){return{...this.activeRequests}}route(t,...r){return function(t,r,...s){const n={url:async e=>{var n;const o=await r.connect();return A(N(t,null!=(n=r.options.baseUrl)?n:"",o,...s),null==e?void 0:e.query)}},o=["get","post","put","delete","patch"];for(const a of o)n[a]=n=>{var o;const{headers:c,...u}=n||{};if(null==(o=null==n?void 0:n.signal)?void 0:o.aborted)throw new i;return r.connect().then(n=>{var o;return S(N(t,null!=(o=r.options.baseUrl)?o:"",n,...s),{headers:e.objectRemoveEmpty({...r.getAuthorizationHeaders(),...null!=c?c:{}}),...u,method:a},r.cache)})};return n}(t,this,...r)}async makeQuery(t,r,s={}){var o;if(!(t=(this.options.baseUrl?this.options.baseUrl:"")+t))throw new Error("Empty `url` not allowed");if(r){const{paramList:e,...s}=r;t=n(t,s)}t=encodeURI(e.fixUrlStr(t)),s={withCredentials:this.withCredentials,...s};const{cache:a,signal:c,method:u="GET",headers:l,cacheName:h,cacheProps:d,responseType:p,withCredentials:f}=s,m=new AbortController,g=m.signal;if(c){if(c.aborted)throw new i;c.addEventListener("abort",()=>{m.abort()})}s.signal=g;const y=async()=>{const e=Q++;this.activeRequests[e]=m;try{return this._loadData(t,s)}finally{this._cleanActiveRequest(e)}};if("GET"===u&&!1!==a){const s=d||{...e.objectRemoveEmpty({headers:l,withCredentials:f,responseType:p,baseUrl:this.options.baseUrl,userId:null==(o=this.user)?void 0:o.id}),params:r};return this.cache.add(h||t,y,{props:s,expirationTime:a?void 0:500})}return y()}_loadData(e,t){return t.responseType=t.responseType||"json",new Promise((r,s)=>{var n;if(this.user&&((t=t||{}).headers={...this.getAuthorizationHeaders(),...t.headers}),this.requestTransform){const[r,s]=this.requestTransform(e,t);e=r,t=s}let o;F(e,r,t,s,e=>{o=e}),null==(n=t.signal)||n.addEventListener("abort",()=>{void 0!==o&&o(),s(new i)})}).catch(e=>{if("AbortError"!==e.name){const t=this._handleHttpError(e);if(t)throw t}throw e})}async _login(e,t){try{const r=await this.getUserInfo(e,t);return this.user=r,this.emitter.emit("login",r),r}catch(r){throw this.emitter.emit("login:error",r),r}}_cleanActiveRequest(e){delete this.activeRequests[e]}_handleHttpError(e){if(e&&e instanceof u){if("nextgisweb.resource.exception.ResourceNotFound"===e.exception)throw new E(e);if("nextgisweb.core.exception.InsufficientPermissions"===e.exception)throw new d(e)}return e}}z(J,"errors",R);const M=["description"];function W(t,r=""){r=r?r+"__":"";const s={};for(const[n,o]of Object.entries(t))if(-1===M.indexOf(n))if(B(o))if("owner_user"===n){const e=W(o,n);Object.assign(s,e)}else"parent"===n&&"id"in o&&(s.parent_id=o.id);else e.defined(o)&&(s[r+n]=o);return s}var K=Object.defineProperty,V=(e,t,r)=>((e,t,r)=>t in e?K(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,"symbol"!=typeof t?t+"":t,r);class X{constructor({connector:e,cacheId:r}){V(this,"cache"),V(this,"connector"),this.connector=e,this.cache=new t({namespace:r})}getOne(e,t){const r={...t};return"string"==typeof e||"number"==typeof e||B(e),"string"==typeof e?this._fetchResourceBy({keyname:e},r):"number"==typeof e?this._fetchResourceById(e,r):B(e)?this._fetchResourceBy(e,r):Promise.resolve(void 0)}getOneOrFail(e,t){return this.getOne(e,t).then(e=>{if(e)return e;throw new E})}getId(e,t){return"number"==typeof e?Promise.resolve(e):"string"==typeof e||B(e)?this.getOne(e,t).then(e=>{if(e)return e.resource.id}):Promise.resolve(void 0)}getIdOrFail(e,t){return this.getId(e,t).then(e=>{if(void 0===e)throw new Error;return e})}getMany(e,t){return this._resourceCacheFilter(e).then(r=>{if(!r.length){const r={};return e.keyname?r.keyname=e.keyname:Object.assign(r,W(e)),this.connector.route("resource.search").get({...t,query:{serialization:"full",...r}}).then(e=>{if((null==t?void 0:t.cache)&&e)for(const t of e)this.cache.add("resource.item",Promise.resolve(t),{id:t.resource.id});return e})}return r})}getParent(e,t){return this.getOne(e,t).then(e=>{var r,s;return(null==(s=null==(r=null==e?void 0:e.resource)?void 0:r.parent)?void 0:s.id)?this.getOne(e.resource.parent.id,t):Promise.resolve(void 0)})}getChildrenOf(e,t){return this.getIdOrFail(e).then(e=>this._getChildrenOf(e,t))}update(e,t){return this.getId(e).then(e=>{if(void 0!==e)return this.connector.put("resource.item",{data:t},{id:e})})}delete(e){return this.getId(e).then(e=>{if(void 0!==e)return this.connector.delete("resource.item",null,{id:e}).then(()=>{this._cleanResourceItemCache(e)})})}async _getChildrenOf(e,t,r=[]){let s;s="string"==typeof e?await this.getId(e,t):"object"==typeof e?e.id:e;const n=await this.connector.route("resource.collection").get({...t,query:{parent:s}}),o=[];for(const i of n)(null==t?void 0:t.cache)&&this.cache.add("resource.item",Promise.resolve(i),{id:i.resource.id}),r.push(i),(null==t?void 0:t.recursive)&&i.resource.children&&o.push(this._getChildrenOf(i.resource.id,t,r));return o.length?Promise.all(o).then(()=>r):r}async _cleanResourceItemCache(e){var t;const r=this.cache.all(),s=[];for(const n of r){const r=null==(t=n.props)?void 0:t.id;if(["resource.item","resource"].includes(n.key)&&void 0!==r)if("number"==typeof r)r===e&&s.push(n);else{await this.getId(r)===e&&s.push(n)}}for(const n of s)this.cache.delete(n)}_fetchResourceById(e,t){return this.connector.route("resource.item",{id:e}).get(t)}_fetchResourceBy(e,t){return this.getMany(e,t).then(e=>e[0])}_resourceCacheFilter(t){return Promise.all(this.cache.matchAll("resource.item")).then(r=>(r.filter(r=>{if(r)return t.keyname&&r.resource.keyname?t.keyname===r.resource.keyname:e.defined(t.id)&&e.defined(r.resource.id)?t.id===r.resource.id:e.objectDeepEqual(t,r.resource)}),[]))}}var Y=Object.defineProperty,Z=(e,t,r)=>((e,t,r)=>t in e?Y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r)(e,t+"",r);module.exports=class extends J{constructor(e){super(e),Z(this,"resources"),this.resources=new X({connector:this,cacheId:e.cacheId})}static create(e){return new this(e)}clearCache(){super.clearCache(),this.resources.cache.clean()}apiRequest(t,r={},s={}){var o;r=null!=(o=s.params)?o:r;return async function(e){const{params:t,name:r,connector:s,requestOptions:o}=e,i=await s.connect();let a=i&&i[r];if(a){a=[...a];let e=a.shift();if(a.length){const r={};for(let e=0;e<a.length;e++){const s=a[e];if(r[e]=`{${s}}`,void 0===t[s])throw new Error(`\`${s}\` URL API argument is not specified`)}e&&(e=n(e,r))}if(t){const r=[],s=t.paramList;Array.isArray(s)&&s.forEach(([e,t])=>{r.push(`${e}=${t}`)});for(const e in t)-1===a.indexOf(e)&&r.push(`${e}=${t[e]}`);r.length&&(e=`${e}?${r.join("&")}`)}if(e)return s.makeQuery(e,t,{cacheName:r,...o});throw new Error("Request URL is not set")}}({name:t,params:e.objectRemoveEmpty(r),requestOptions:s,connector:this})}post(e,t,r){return(t=t||{}).method="POST",this.apiRequest(e,r,t)}get(e,t,r){return(t=t||{}).method="GET",this.apiRequest(e,r,t)}patch(e,t,r){return(t=t||{}).method="PATCH",this.apiRequest(e,r,t)}put(e,t,r){return(t=t||{}).method="PUT",this.apiRequest(e,r,t)}delete(e,t,r){return(t=t||{}).method="DELETE",this.apiRequest(e,r,t)}getResource(e,t){return this.resources.getOne(e,t)}getResourceOrFail(e,t){return this.resources.getOneOrFail(e,t)}getResourceBy(e){return this.resources.getOne(e)}getResourceByKeyname(e){return this.resources.getOne(e)}getResourceById(e){return this.resources.getOne(e)}getResourceId(e,t){return this.resources.getId(e,t)}getResourceIdOrFail(e,t){return this.resources.getIdOrFail(e,t)}getResourcesBy(e,t){return this.resources.getMany(e,t)}getResourceParent(e,t){return this.resources.getParent(e,t)}getResourceChildren(e,t){return this.resources.getChildrenOf(e,t)}updateResource(e,t){return this.resources.update(e,t)}deleteResource(e){return this.resources.delete(e)}};
//# sourceMappingURL=ngw-connector.cjs.prod.js.map