Skip to content

Commit

Permalink
feat(getContent): add support for slugs as well as ids
Browse files Browse the repository at this point in the history
AFFECTS PACKAGES:
@esri/hub-content

ISSUES CLOSED: #320
  • Loading branch information
tomwayson committed Sep 1, 2020
1 parent 2882c68 commit c2e2623
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 50 deletions.
19 changes: 11 additions & 8 deletions packages/content/src/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,28 @@ import { request } from "@esri/arcgis-rest-request";
import { IHubContent, IHubRequestOptions } from "@esri/hub-common";
import { getContentFromHub } from "./hub";
import { getContentFromPortal } from "./portal";
import { isSlug } from "./slugs";

/**
* Fetch content using either the Hub API or the ArcGIS REST API
* @param id - content (item) id
* @param identifier Hub API slug ({orgKey}::{title-as-slug} or {title-as-slug})
* or record id ((itemId}_{layerId} or {itemId})
* @param requestOptions - request options that may include authentication
*/
export function getContent(
// TODO: this should take a slug as well
id: string,
identifier: string,
requestOptions?: IHubRequestOptions
): Promise<IHubContent> {
if (requestOptions && requestOptions.isPortal) {
return getContentFromPortal(id, requestOptions);
return getContentFromPortal(identifier, requestOptions);
} else {
return getContentFromHub(id, requestOptions).catch(() => {
// TODO: inspect error?
return getContentFromHub(identifier, requestOptions).catch(e => {
// dataset is not in index (i.e. might be a private item)
// try fetching from portal instead
return getContentFromPortal(id, requestOptions);
if (!isSlug(identifier)) {
// try fetching from portal instead
return getContentFromPortal(identifier, requestOptions);
}
return Promise.reject(e);
});
}
}
Expand Down
19 changes: 15 additions & 4 deletions packages/content/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
hubApiRequest
} from "@esri/hub-common";
import { itemToContent, withPortalUrls } from "./portal";
import { isSlug } from "./slugs";
import { cloneObject } from "@esri/hub-common";

/**
* Parse item ID and layer ID (if any) from dataset record ID
Expand Down Expand Up @@ -36,15 +38,24 @@ export type DatasetResource = ResourceObject<
/**
* Fetch a dataset resource with the given ID from the Hub API
*
* @param id Hub API ID
* @param requestOptions
* @param identifier Hub API slug ({orgKey}::{title-as-slug} or {title-as-slug})
* or record id ((itemId}_{layerId} or {itemId})
* @param requestOptions - request options that may include authentication
* @export
*/
export function getContentFromHub(
id: string,
identifier: string,
requestOptions?: IHubRequestOptions
): Promise<IHubContent> {
return hubApiRequest(`/datasets/${id}`, requestOptions).then(resp => {
let request;
if (isSlug(identifier)) {
const options = cloneObject(requestOptions);
options.params = { ...options.params, "filter[slug]": identifier };
request = hubApiRequest(`/datasets`, options);
} else {
request = hubApiRequest(`/datasets/${identifier}`, requestOptions);
}
return request.then((resp: any) => {
const dataset = resp && resp.data;
return dataset && withPortalUrls(datasetToContent(dataset), requestOptions);
});
Expand Down
94 changes: 57 additions & 37 deletions packages/content/test/content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,47 +22,67 @@ describe("get content", () => {
});
afterEach(fetchMock.restore);
describe("from hub", () => {
it("should call getContentFromHub", done => {
beforeEach(() => {
requestOpts.isPortal = false;
requestOpts.portalSelf.isPortal = false;
const id = "foo";
const getContentFromHubSpy = spyOn(
hubModule,
"getContentFromHub"
).and.returnValue(Promise.resolve({}));
getContent(id, requestOpts).then(() => {
expect(getContentFromHubSpy.calls.count()).toBe(1);
expect(getContentFromHubSpy.calls.argsFor(0)).toEqual([
id,
requestOpts
]);
done();
});
describe("with an id", () => {
const id = "7a153563b0c74f7eb2b3eae8a66f2fbb";
it("should call getContentFromHub", done => {
const getContentFromHubSpy = spyOn(
hubModule,
"getContentFromHub"
).and.returnValue(Promise.resolve({}));
getContent(id, requestOpts).then(() => {
expect(getContentFromHubSpy.calls.count()).toBe(1);
expect(getContentFromHubSpy.calls.argsFor(0)).toEqual([
id,
requestOpts
]);
done();
});
});
it("handles private items", done => {
const getContentFromHubSpy = spyOn(
hubModule,
"getContentFromHub"
).and.returnValue(Promise.reject({}));
const getContentFromPortalSpy = spyOn(
portalModule,
"getContentFromPortal"
).and.returnValue(Promise.resolve({}));
getContent(id, requestOpts).then(() => {
expect(getContentFromHubSpy.calls.count()).toBe(1);
expect(getContentFromHubSpy.calls.argsFor(0)).toEqual([
id,
requestOpts
]);
expect(getContentFromPortalSpy.calls.count()).toBe(1);
expect(getContentFromPortalSpy.calls.argsFor(0)).toEqual([
id,
requestOpts
]);
done();
});
});
});
it("handles private items", done => {
requestOpts.isPortal = false;
requestOpts.portalSelf.isPortal = false;
const id = "foo";
const getContentFromHubSpy = spyOn(
hubModule,
"getContentFromHub"
).and.returnValue(Promise.reject({}));
const getContentFromPortalSpy = spyOn(
portalModule,
"getContentFromPortal"
).and.returnValue(Promise.resolve({}));
getContent(id, requestOpts).then(() => {
expect(getContentFromHubSpy.calls.count()).toBe(1);
expect(getContentFromHubSpy.calls.argsFor(0)).toEqual([
id,
requestOpts
]);
expect(getContentFromPortalSpy.calls.count()).toBe(1);
expect(getContentFromPortalSpy.calls.argsFor(0)).toEqual([
"foo",
requestOpts
]);
done();
describe("with a slug", () => {
const slug = "foo";
it("rejects when not in the index", done => {
const err = new Error("test");
const getContentFromHubSpy = spyOn(
hubModule,
"getContentFromHub"
).and.returnValue(Promise.reject(err));
getContent(slug, requestOpts).catch(e => {
expect(getContentFromHubSpy.calls.count()).toBe(1);
expect(getContentFromHubSpy.calls.argsFor(0)).toEqual([
slug,
requestOpts
]);
expect(e).toEqual(err);
done();
});
});
});
});
Expand Down
21 changes: 20 additions & 1 deletion packages/content/test/hub.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe("hub", () => {
};
});
afterEach(fetchMock.restore);
it("should fetch a dataset record and return content", done => {
it("should fetch a dataset record by id and return content", done => {
fetchMock.once("*", featureLayerJson);
const dataset = featureLayerJson.data as DatasetResource;
const id = dataset.id;
Expand All @@ -173,5 +173,24 @@ describe("hub", () => {
done();
});
});
it("should fetch a dataset record by slug and return content", done => {
fetchMock.once("*", featureLayerJson);
const dataset = featureLayerJson.data as DatasetResource;
const slug = "Wigan::out-of-work-benefit-claims";
getContentFromHub(slug, requestOpts).then(content => {
// verify that we attempted to fetch from the portal API
const [url, opts] = fetchMock.calls()[0];
expect(url).toBe(
`https://some.url.com/api/v3/datasets?${encodeURIComponent(
"filter[slug]"
)}=${encodeURIComponent(slug)}`
);
expect(opts.method).toBe("GET");
validateContentFromDataset(content, dataset, "dataset");
// TODO: content type specific properties
// expect(content.recordCount).toBe(attributes.recordCount);
done();
});
});
});
});

0 comments on commit c2e2623

Please sign in to comment.