Skip to content

Commit

Permalink
Merge pull request #40 from camptocamp/ogc-api-url-fix
Browse files Browse the repository at this point in the history
OGC API: add a json bulk download link, fix issue with query params for root path lookup
  • Loading branch information
cmoinier authored Apr 30, 2024
2 parents 25992d5 + 40b8cca commit 593181f
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 10 deletions.
24 changes: 24 additions & 0 deletions src/ogc-api/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ describe('OgcApiEndpoint', () => {
'application/vnd.ogc.fg+json;compatibility=geojson',
],
bulkDownloadLinks: {},
jsonDownloadLink: null,
extent: {
spatial: {
bbox: [
Expand Down Expand Up @@ -280,6 +281,7 @@ describe('OgcApiEndpoint', () => {
'text/html',
],
bulkDownloadLinks: {},
jsonDownloadLink: null,
keywords: ['netherlands', 'open data', 'georegister'],
extent: {
spatial: {
Expand Down Expand Up @@ -358,6 +360,7 @@ describe('OgcApiEndpoint', () => {
'text/html',
],
bulkDownloadLinks: {},
jsonDownloadLink: null,
extent: {
spatial: {
bbox: [
Expand Down Expand Up @@ -1783,6 +1786,8 @@ The document at http://local/nonexisting?f=json could not be fetched.`
'text/csv;charset=UTF-8':
'https://my.server.org/sample-data-2/collections/aires-covoiturage/items?f=csv&limit=-1',
},
jsonDownloadLink:
'https://my.server.org/sample-data-2/collections/aires-covoiturage/items?f=geojson&limit=-1',
id: 'aires-covoiturage',
itemType: 'feature',
queryables: [],
Expand All @@ -1805,4 +1810,23 @@ The document at http://local/nonexisting?f=json could not be fetched.`
});
});
});

describe('url with trailing ?', () => {
beforeEach(() => {
endpoint = new OgcApiEndpoint(
'http://local/sample-data/collections/airports/items?'
);
});
describe('#info', () => {
it('returns endpoint info', async () => {
await expect(endpoint.info).resolves.toEqual({
title: 'OS Open Zoomstack',
description:
'OS Open Zoomstack is a comprehensive vector basemap showing coverage of Great Britain at a national level, right down to street-level detail.',
attribution:
'Contains OS data © Crown copyright and database right 2021.',
});
});
});
});
});
15 changes: 8 additions & 7 deletions src/ogc-api/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import {
} from './link-utils.js';
import { EndpointError } from '../shared/errors.js';
import { BoundingBox, CrsCode, MimeType } from '../shared/models.js';
import { isMimeTypeJson, isMimeTypeJsonFg } from '../shared/mime-type.js';
import {
isMimeTypeGeoJson,
isMimeTypeJson,
isMimeTypeJsonFg,
} from '../shared/mime-type.js';

/**
* Represents an OGC API endpoint advertising various collections and services.
Expand Down Expand Up @@ -313,13 +317,10 @@ ${e.message}`);
);
let url: URL;
if (options.asJson) {
// try json-fg
linkWithFormat = itemLinks.find((link) =>
isMimeTypeJsonFg(link.type)
);
// try geojson
// try json-fg, geojson and json
linkWithFormat =
linkWithFormat ??
itemLinks.find((link) => isMimeTypeJsonFg(link.type)) ||
itemLinks.find((link) => isMimeTypeGeoJson(link.type)) ||
itemLinks.find((link) => isMimeTypeJson(link.type));
}
if (options?.outputFormat && !linkWithFormat) {
Expand Down
14 changes: 14 additions & 0 deletions src/ogc-api/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {
} from './model.js';
import { assertHasLinks } from './link-utils.js';
import { EndpointError } from '../shared/errors.js';
import {
isMimeTypeGeoJson,
isMimeTypeJson,
isMimeTypeJsonFg,
} from '../shared/mime-type.js';

export function parseEndpointInfo(rootDoc: OgcApiDocument): OgcApiEndpointInfo {
try {
Expand Down Expand Up @@ -108,9 +113,18 @@ export function parseBaseCollectionInfo(
acc[link.type] = link.href;
return acc;
}, {});
const mimeTypes = Object.keys(bulkDownloadLinks);
const jsonMimeType =
mimeTypes.find(isMimeTypeJsonFg) ||
mimeTypes.find(isMimeTypeGeoJson) ||
mimeTypes.find(isMimeTypeJson);
const jsonDownloadLink = jsonMimeType
? bulkDownloadLinks[jsonMimeType]
: null;
return {
itemFormats: itemFormats,
bulkDownloadLinks,
jsonDownloadLink,
...props,
} as OgcApiCollectionInfo;
}
Expand Down
8 changes: 8 additions & 0 deletions src/ogc-api/link-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@ describe('link utils', () => {
'http://example.com/foo'
);
});
it('should keep query params', () => {
expect(getParentPath('http://example.com/foo/bar?aa=bb')).toBe(
'http://example.com/foo?aa=bb'
);
expect(getParentPath('http://example.com/foo/bar?')).toBe(
'http://example.com/foo?'
);
});
});
});
4 changes: 3 additions & 1 deletion src/ogc-api/link-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export function fetchRoot(url: string): Promise<OgcApiDocument> {
}
// if there is a collections array, we expect the parent path to end with slash
if ('collections' in doc) {
parentUrl = `${parentUrl}/`;
const urlObj = new URL(parentUrl);
urlObj.pathname = `${urlObj.pathname}/`;
parentUrl = urlObj.toString();
}
return fetchRoot(parentUrl);
}
Expand Down
2 changes: 2 additions & 0 deletions src/ogc-api/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface CollectionParameter {
* @property itemFormats These mime types are available through the `/items` endpoint;
* use the `getCollectionItemsUrl` function to generate a URL using one of those formats
* @property bulkDownloadLinks Map between formats and bulk download links (no filtering, pagination etc.)
* @property jsonDownloadLink Link to the first bulk download link using JSON-FG or GeoJSON; null if no link found
* @property crs
* @property storageCrs
* @property itemCount
Expand All @@ -54,6 +55,7 @@ export interface OgcApiCollectionInfo {
itemType: 'feature' | 'record';
itemFormats: MimeType[];
bulkDownloadLinks: Record<string, MimeType>;
jsonDownloadLink: string;
crs: CrsCode[];
storageCrs?: CrsCode;
itemCount: number;
Expand Down
16 changes: 14 additions & 2 deletions src/shared/mime-type.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { isMimeTypeJson, isMimeTypeJsonFg } from './mime-type.js';
import {
isMimeTypeGeoJson,
isMimeTypeJson,
isMimeTypeJsonFg,
} from './mime-type.js';

describe('mime type utils', () => {
it('isMimeTypeGeoJson', () => {
it('isMimeTypeJson', () => {
expect(isMimeTypeJson('application/geo+json')).toBe(true);
expect(isMimeTypeJson('application/vnd.geo+json')).toBe(true);
expect(isMimeTypeJson('geo+json')).toBe(true);
expect(isMimeTypeJson('geojson')).toBe(true);
expect(isMimeTypeJson('application/json')).toBe(true);
expect(isMimeTypeJson('json')).toBe(true);
});
it('isMimeTypeGeoJson', () => {
expect(isMimeTypeGeoJson('application/geo+json')).toBe(true);
expect(isMimeTypeGeoJson('application/vnd.geo+json')).toBe(true);
expect(isMimeTypeGeoJson('geo+json')).toBe(true);
expect(isMimeTypeGeoJson('geojson')).toBe(true);
expect(isMimeTypeGeoJson('application/json')).toBe(false);
expect(isMimeTypeGeoJson('json')).toBe(false);
});
it('isMimeTypeJsonFg', () => {
expect(isMimeTypeJsonFg('application/vnd.ogc.fg+json')).toBe(true);
expect(isMimeTypeJsonFg('fg+json')).toBe(true);
Expand Down
4 changes: 4 additions & 0 deletions src/shared/mime-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ export function isMimeTypeJson(mimeType: string): boolean {
return mimeType.toLowerCase().indexOf('json') > -1;
}

export function isMimeTypeGeoJson(mimeType: string): boolean {
return /geo.?json/.test(mimeType);
}

export function isMimeTypeJsonFg(mimeType: string): boolean {
return /json.?fg|fg.?json/.test(mimeType);
}

0 comments on commit 593181f

Please sign in to comment.