@camptocamp/ogc-client
Advanced tools
Comparing version 1.1.1-dev.a0aadb6 to 1.1.1-dev.ad6d9ab
import { EndpointError } from "../shared/errors.js"; | ||
import { getFetchOptions } from "../shared/http-utils.js"; | ||
import { sharedFetch } from "../shared/http-utils.js"; | ||
function fetchDocument(url) { | ||
const urlObj = new URL(url, window.location.toString()); | ||
urlObj.searchParams.set("f", "json"); | ||
const options = getFetchOptions(); | ||
const optionsHeaders = "headers" in options ? options.headers : {}; | ||
return fetch(urlObj.toString(), { | ||
...options, | ||
headers: { ...optionsHeaders, Accept: "application/json" } | ||
}).then((resp) => { | ||
return sharedFetch(urlObj.toString(), "GET", true).then((resp) => { | ||
if (!resp.ok) { | ||
throw new Error(`The document at ${urlObj} could not be fetched.`); | ||
} | ||
return resp.json().catch(() => { | ||
return resp.clone().json().catch((e) => { | ||
throw new Error( | ||
`The document at ${urlObj} does not appear to be valid JSON.` | ||
`The document at ${urlObj} does not appear to be valid JSON. Error was: ${e.message}` | ||
); | ||
@@ -19,0 +14,0 @@ }); |
@@ -21,3 +21,3 @@ import { FetchOptions } from './models.js'; | ||
*/ | ||
export declare function sharedFetch(url: string, method?: 'GET' | 'HEAD'): Promise<any>; | ||
export declare function sharedFetch(url: string, method?: 'GET' | 'HEAD', asJson?: boolean): Promise<any>; | ||
/** | ||
@@ -24,0 +24,0 @@ * Runs a GET HTTP request to the provided URL and resolves to the |
@@ -23,8 +23,17 @@ import { parseXmlString } from "./xml-utils.js"; | ||
} | ||
function sharedFetch(url, method = "GET") { | ||
const fetchKey = `${method}#${url}`; | ||
function sharedFetch(url, method = "GET", asJson) { | ||
let fetchKey = `${method}#${url}`; | ||
if (asJson) { | ||
fetchKey = `${method}#asJson#${url}`; | ||
} | ||
if (fetchPromises.has(fetchKey)) { | ||
return fetchPromises.get(fetchKey); | ||
} | ||
const promise = fetch(url, { ...getFetchOptions(), method }).catch((e) => e).then((resp) => { | ||
const options = { ...getFetchOptions() }; | ||
options.method = method; | ||
if (asJson) { | ||
options.headers = "headers" in options ? options.headers : {}; | ||
options.headers["Accept"] = "application/json"; | ||
} | ||
const promise = fetch(url, options).catch((e) => e).then((resp) => { | ||
fetchPromises.delete(fetchKey); | ||
@@ -31,0 +40,0 @@ return resp; |
@@ -69,2 +69,6 @@ import { BoundingBox, CrsCode, GenericEndpointInfo, MimeType } from '../shared/models.js'; | ||
/** | ||
* Returns true if the WFS service supports the startIndex parameter. | ||
*/ | ||
supportsStartIndex(): boolean; | ||
/** | ||
* Returns a URL that can be used to query features from this feature type. | ||
@@ -79,2 +83,3 @@ * @param featureType | ||
* @property [options.extentCrs] if unspecified, `extent` should be in the data native projection | ||
* @property [options.startIndex] if the service supports it, this will be the index of the first feature to return | ||
* @returns Returns null if endpoint is not ready | ||
@@ -89,4 +94,5 @@ */ | ||
extentCrs?: CrsCode; | ||
startIndex?: number; | ||
}): string; | ||
} | ||
//# sourceMappingURL=endpoint.d.ts.map |
@@ -204,2 +204,10 @@ import { | ||
/** | ||
* Returns true if the WFS service supports the startIndex parameter. | ||
*/ | ||
supportsStartIndex() { | ||
if (!this._version) | ||
return false; | ||
return this._version >= "2.0.0"; | ||
} | ||
/** | ||
* Returns a URL that can be used to query features from this feature type. | ||
@@ -214,2 +222,3 @@ * @param featureType | ||
* @property [options.extentCrs] if unspecified, `extent` should be in the data native projection | ||
* @property [options.startIndex] if the service supports it, this will be the index of the first feature to return | ||
* @returns Returns null if endpoint is not ready | ||
@@ -221,3 +230,11 @@ */ | ||
} | ||
const { maxFeatures, asJson, outputFormat, outputCrs, extent, extentCrs } = options || {}; | ||
const { | ||
maxFeatures, | ||
asJson, | ||
outputFormat, | ||
outputCrs, | ||
extent, | ||
extentCrs, | ||
startIndex | ||
} = options || {}; | ||
const internalFeatureType = this._getFeatureTypeByName(featureType); | ||
@@ -252,3 +269,4 @@ if (!internalFeatureType) { | ||
extent, | ||
extentCrs | ||
extentCrs, | ||
startIndex | ||
); | ||
@@ -255,0 +273,0 @@ } |
@@ -16,4 +16,5 @@ import { BoundingBox, CrsCode, MimeType } from '../shared/models.js'; | ||
* @param [extentCrs] if unspecified, `extent` should be in the data native projection | ||
* @param [startIndex] if the service supports it, this will be the index of the first feature to return | ||
*/ | ||
export declare function generateGetFeatureUrl(serviceUrl: string, version: WfsVersion, featureType: string, outputFormat?: MimeType, maxFeatures?: number, attributes?: string[], hitsOnly?: boolean, outputCrs?: CrsCode, extent?: BoundingBox, extentCrs?: CrsCode): string; | ||
export declare function generateGetFeatureUrl(serviceUrl: string, version: WfsVersion, featureType: string, outputFormat?: MimeType, maxFeatures?: number, attributes?: string[], hitsOnly?: boolean, outputCrs?: CrsCode, extent?: BoundingBox, extentCrs?: CrsCode, startIndex?: number): string; | ||
/** | ||
@@ -20,0 +21,0 @@ * Generates an URL for a DescribeFeatureType operation |
import { setQueryParams } from "../shared/http-utils.js"; | ||
function generateGetFeatureUrl(serviceUrl, version, featureType, outputFormat, maxFeatures, attributes, hitsOnly, outputCrs, extent, extentCrs) { | ||
function generateGetFeatureUrl(serviceUrl, version, featureType, outputFormat, maxFeatures, attributes, hitsOnly, outputCrs, extent, extentCrs, startIndex) { | ||
const typeParam = version === "2.0.0" ? "TYPENAMES" : "TYPENAME"; | ||
@@ -27,2 +27,5 @@ const countParam = version === "2.0.0" ? "COUNT" : "MAXFEATURES"; | ||
} | ||
if (startIndex) { | ||
newParams.STARTINDEX = startIndex.toString(10); | ||
} | ||
return setQueryParams(serviceUrl, newParams); | ||
@@ -29,0 +32,0 @@ } |
{ | ||
"name": "@camptocamp/ogc-client", | ||
"version": "1.1.1-dev.a0aadb6", | ||
"version": "1.1.1-dev.ad6d9ab", | ||
"description": "A pure JS library for interacting with geospatial services.", | ||
@@ -5,0 +5,0 @@ "main": "./dist/dist-node.js", |
import { OgcApiDocument, OgcApiDocumentLink } from './model.js'; | ||
import { EndpointError } from '../shared/errors.js'; | ||
import { getFetchOptions } from '../shared/http-utils.js'; | ||
import { sharedFetch } from '../shared/http-utils.js'; | ||
@@ -10,16 +10,14 @@ export function fetchDocument<T extends OgcApiDocument>( | ||
urlObj.searchParams.set('f', 'json'); | ||
const options = getFetchOptions(); | ||
const optionsHeaders = 'headers' in options ? options.headers : {}; | ||
return fetch(urlObj.toString(), { | ||
...options, | ||
headers: { ...optionsHeaders, Accept: 'application/json' }, | ||
}).then((resp) => { | ||
return sharedFetch(urlObj.toString(), 'GET', true).then((resp) => { | ||
if (!resp.ok) { | ||
throw new Error(`The document at ${urlObj} could not be fetched.`); | ||
} | ||
return resp.json().catch(() => { | ||
throw new Error( | ||
`The document at ${urlObj} does not appear to be valid JSON.` | ||
); | ||
}) as Promise<T>; | ||
return resp | ||
.clone() | ||
.json() | ||
.catch((e) => { | ||
throw new Error( | ||
`The document at ${urlObj} does not appear to be valid JSON. Error was: ${e.message}` | ||
); | ||
}) as Promise<T>; | ||
}); | ||
@@ -26,0 +24,0 @@ } |
@@ -37,3 +37,3 @@ import { EndpointError } from './errors.js'; | ||
originalFetch = global.fetch; // keep reference of native impl | ||
global.fetch = jest.fn((xmlString, opts) => { | ||
global.fetch = jest.fn().mockImplementation((xmlString, opts) => { | ||
const noCors = opts && opts.mode === 'no-cors'; | ||
@@ -49,2 +49,5 @@ const headers = { get: () => null }; | ||
headers, | ||
clone: function () { | ||
return this; | ||
}, | ||
}); | ||
@@ -56,2 +59,5 @@ case 'httpError': | ||
ok: false, | ||
clone: function () { | ||
return this; | ||
}, | ||
}); | ||
@@ -77,2 +83,5 @@ case 'corsError': | ||
Promise.resolve(Buffer.from(sampleXml, 'utf-8')), | ||
clone: function () { | ||
return this; | ||
}, | ||
}), | ||
@@ -414,2 +423,3 @@ 10 | ||
...sampleOptions, | ||
method: 'GET', | ||
headers: { | ||
@@ -416,0 +426,0 @@ ...sampleOptions.headers, |
@@ -46,10 +46,23 @@ import { parseXmlString } from './xml-utils.js'; | ||
*/ | ||
export function sharedFetch(url: string, method: 'GET' | 'HEAD' = 'GET') { | ||
const fetchKey = `${method}#${url}`; | ||
export function sharedFetch( | ||
url: string, | ||
method: 'GET' | 'HEAD' = 'GET', | ||
asJson?: boolean | ||
) { | ||
let fetchKey = `${method}#${url}`; | ||
if (asJson) { | ||
fetchKey = `${method}#asJson#${url}`; | ||
} | ||
if (fetchPromises.has(fetchKey)) { | ||
return fetchPromises.get(fetchKey); | ||
} | ||
const options: RequestInit = { ...getFetchOptions() }; | ||
options.method = method; | ||
if (asJson) { | ||
options.headers = 'headers' in options ? options.headers : {}; | ||
options.headers['Accept'] = 'application/json'; | ||
} | ||
// to avoid unhandled promise rejections this promise will never reject, | ||
// but only return errors as a normal value | ||
const promise = fetch(url, { ...getFetchOptions(), method }) | ||
const promise = fetch(url, options) | ||
.catch((e) => e) | ||
@@ -56,0 +69,0 @@ .then((resp) => { |
@@ -507,2 +507,9 @@ // @ts-expect-error ts-migrate(7016) | ||
}); | ||
describe('#supportsStartIndex', () => { | ||
it('returns true if the WFS version is 2.0.0 or higher', async () => { | ||
await endpoint.isReady(); | ||
expect(endpoint.supportsStartIndex()).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
@@ -239,2 +239,10 @@ import { | ||
/** | ||
* Returns true if the WFS service supports the startIndex parameter. | ||
*/ | ||
supportsStartIndex(): boolean { | ||
if (!this._version) return false; | ||
return this._version >= '2.0.0'; | ||
} | ||
/** | ||
* Returns a URL that can be used to query features from this feature type. | ||
@@ -249,2 +257,3 @@ * @param featureType | ||
* @property [options.extentCrs] if unspecified, `extent` should be in the data native projection | ||
* @property [options.startIndex] if the service supports it, this will be the index of the first feature to return | ||
* @returns Returns null if endpoint is not ready | ||
@@ -261,2 +270,3 @@ */ | ||
extentCrs?: CrsCode; | ||
startIndex?: number; | ||
} | ||
@@ -267,4 +277,11 @@ ) { | ||
} | ||
const { maxFeatures, asJson, outputFormat, outputCrs, extent, extentCrs } = | ||
options || {}; | ||
const { | ||
maxFeatures, | ||
asJson, | ||
outputFormat, | ||
outputCrs, | ||
extent, | ||
extentCrs, | ||
startIndex, | ||
} = options || {}; | ||
const internalFeatureType = this._getFeatureTypeByName(featureType); | ||
@@ -303,5 +320,6 @@ if (!internalFeatureType) { | ||
extent, | ||
extentCrs | ||
extentCrs, | ||
startIndex | ||
); | ||
} | ||
} |
@@ -102,2 +102,21 @@ import { | ||
}); | ||
it('generates a correct URL (v2.0.0, startIndex set)', () => { | ||
expect( | ||
generateGetFeatureUrl( | ||
'http://example.com/wfs', | ||
'2.0.0', | ||
'my:type', | ||
undefined, | ||
undefined, | ||
undefined, | ||
undefined, | ||
undefined, | ||
undefined, | ||
undefined, | ||
10 | ||
) | ||
).toBe( | ||
'http://example.com/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my%3Atype&STARTINDEX=10' | ||
); | ||
}); | ||
}); | ||
@@ -104,0 +123,0 @@ |
@@ -18,2 +18,3 @@ import { setQueryParams } from '../shared/http-utils.js'; | ||
* @param [extentCrs] if unspecified, `extent` should be in the data native projection | ||
* @param [startIndex] if the service supports it, this will be the index of the first feature to return | ||
*/ | ||
@@ -30,3 +31,4 @@ export function generateGetFeatureUrl( | ||
extent?: BoundingBox, | ||
extentCrs?: CrsCode | ||
extentCrs?: CrsCode, | ||
startIndex?: number | ||
) { | ||
@@ -56,2 +58,5 @@ const typeParam = version === '2.0.0' ? 'TYPENAMES' : 'TYPENAME'; | ||
} | ||
if (startIndex) { | ||
newParams.STARTINDEX = startIndex.toString(10); | ||
} | ||
@@ -58,0 +63,0 @@ return setQueryParams(serviceUrl, newParams); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
978989
15365
2