Skip to content

Commit

Permalink
Merge pull request #545 from Esri/f/content-family-and-identifier
Browse files Browse the repository at this point in the history
Content family and org
  • Loading branch information
tomwayson authored Jun 15, 2021
2 parents 655cca6 + 02d08c6 commit c41b183
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 69 deletions.
6 changes: 4 additions & 2 deletions packages/common/src/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const document: string[] = [

const event: string[] = ["Hub Event"];

const feedback: string[] = ["Form"];
const feedback: string[] = ["Form", "Quick Capture Project"];

const initiative: string[] = ["Hub Initiative"];

Expand All @@ -64,13 +64,15 @@ const map: string[] = [
"Map Service Layer",
"Map Service",
"Scene Service",
"Scene Layer",
"Vector Tile Service",
"Web Map Service",
"Web Map Tile Service",
"Web Map",
"Web Scene",
"WFS",
"WMS"
"WMS",
"WMTS"
];

const other: string[] = [
Expand Down
54 changes: 53 additions & 1 deletion packages/common/src/content.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Copyright (c) 2019 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { IItem } from "@esri/arcgis-rest-portal";
import { collections } from "./collections";
import { categories } from "./categories";
import { includes } from "./utils";
Expand Down Expand Up @@ -138,3 +138,55 @@ export function getCollection(itemType: string = ""): string {
}
}
}

/**
* Case-insensitive check if the type is "Feature Service"
* @param {string} type - item's type
* @returns {boolean}
*/
export const isFeatureService = (type: string) => {
return type && type.toLowerCase() === "feature service";
};

/**
* parse layer id from a service URL
* @param {string} url
* @returns {string} layer id
*/
export const getLayerIdFromUrl = (url: string) => {
const endsWithNumberSegmentRegEx = /\/\d+$/;
const matched = url && url.match(endsWithNumberSegmentRegEx);
return matched && matched[0].slice(1);
};

/**
* return the layerId if we can tell that item is a single layer service
* @param {*} item from AGO
* @returns {string} layer id
*/
export const getItemLayerId = (item: IItem) => {
// try to parse it from the URL, but failing that we check for
// the Singlelayer typeKeyword, which I think is set when you create the item in AGO
// but have not verified that, nor that we should alway return '0' in that case
return (
getLayerIdFromUrl(item.url) ||
(isFeatureService(item.type) &&
includes(item.typeKeywords, "Singlelayer") &&
"0")
);
};

/**
* given an item, get the id to use w/ the Hub API
* @param item
* @returns Hub API id (hubId)
*/
export const getItemHubId = (item: IItem) => {
if (item.access !== "public") {
// the hub only indexes public items
return;
}
const id = item.id;
const layerId = getItemLayerId(item);
return layerId ? `${id}_${layerId}` : id;
};
47 changes: 45 additions & 2 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { IItem, IUser, IGroup, IGeometry } from "@esri/arcgis-rest-types";
import {
IItem,
IUser,
IGroup,
IGeometry,
IFeatureServiceDefinition,
ILayerDefinition
} from "@esri/arcgis-rest-types";
import { IPortal, ISearchResult } from "@esri/arcgis-rest-portal";
import { IUserRequestOptions } from "@esri/arcgis-rest-auth";

Expand Down Expand Up @@ -141,6 +148,7 @@ export type IRevertableTaskResult =

/**
* Types of Hub resources
* DEPRECATED: Use HubFamily instead
*/
export type HubType =
| "member"
Expand All @@ -155,6 +163,20 @@ export type HubType =
| "template"
| "organization";

export type HubFamily =
| "people"
| "team"
| "event"
| "dataset"
| "document"
| "feedback"
| "map"
| "app"
| "site"
| "initiative"
| "template"
| "content";

/**
* Visibility levels of a Hub resource
*/
Expand Down Expand Up @@ -255,8 +277,12 @@ export interface IEnrichmentErrorInfo {
export interface IHubContent extends IHubResource, IItem {
/**
* The content's ID for use with the Hub API
* For most content this will be the item's id
* For layers this will be `<itemId>_<layerId>`
* This will be undefined for private items and in enterprise
* because only public online items are included in the Hub API
*/
hubId: string;
hubId?: string;
// NOTE: we may want to elevate this to IHubResource if it's needed for other subtypes
/**
* Content visibility and access control, including groups
Expand All @@ -270,6 +296,9 @@ export interface IHubContent extends IHubResource, IItem {
groups?: IGroup[]; // TODO: item.sharing.groups via content/users/:username/items/:id
};

// TODO: make this required at next breaking release
family?: HubFamily;

// TODO: license: IHubLicense // [Future] item.licenseInfo

/**
Expand Down Expand Up @@ -319,11 +348,25 @@ export interface IHubContent extends IHubResource, IItem {
data?: {
[propName: string]: any;
};
// service types: Feature Service, Map Service
/** service information (currentVersion, capabilities, maxRecordCount etc) */
server?: Partial<IFeatureServiceDefinition>;
/** layer information (geometryType, fields, etc) for related layers in the service */
layers?: Array<Partial<ILayerDefinition>>;
// layer types: Feature Layers, Raster Layers
/** layer information (geometryType, fields, etc) */
layer?: Partial<ILayerDefinition>;
recordCount?: number;
// TODO: statistics?: ???
// NOTE: this is usually(? always?) returned by the item endpoint
// but it's not on IItem, possibly b/c it's not listed here:
// https://developers.arcgis.com/rest/users-groups-and-items/item.htm
/** The owner's organization id */
orgId?: string;
/**
* The owner's organization (portal) details (id, name, extent, etc)
*/
org?: Partial<IPortal>;
/** Whether the content is downloadable in the Hub app */
isDownloadable: boolean;
}
Expand Down
81 changes: 80 additions & 1 deletion packages/common/test/content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import {
getCollection,
getTypes,
getTypeCategories,
normalizeItemType
normalizeItemType,
isFeatureService,
getLayerIdFromUrl,
getItemLayerId,
getItemHubId
} from "../src/content";

describe("getCollection", () => {
Expand Down Expand Up @@ -108,3 +112,78 @@ describe("getTypeCategories", () => {
expect(getTypeCategories()).toEqual(["Other"]);
});
});

describe("isFeatureService", () => {
it("returns true when the type is Feature Service", () => {
expect(isFeatureService("Feature Service")).toBe(true);
expect(isFeatureService("feature service")).toBe(true);
});
it("returns false when the type is not Feature Service", () => {
expect(isFeatureService("Map Service")).toBe(false);
});
});

describe("getLayerIdFromUrl", () => {
it("returns layerId when present", () => {
expect(
getLayerIdFromUrl(
"https://services9.arcgis.com/BH6j7VrWdIXhhNYw/arcgis/rest/services/Befolkning_efter_k%C3%B6n/FeatureServer/0"
)
).toBe("0");
});
it("returns undefined when not present", () => {
expect(
getLayerIdFromUrl(
"https://services9.arcgis.com/BH6j7VrWdIXhhNYw/arcgis/rest/services/Befolkning_efter_k%C3%B6n/FeatureServer"
)
).toBe(null);
});
});

describe("getLayerIdFromItem", () => {
it("returns '0' when typeKeywords includes 'Singlelayer'", () => {
const item: any = {
type: "Feature Service",
url:
"https://services9.arcgis.com/BH6j7VrWdIXhhNYw/arcgis/rest/services/Befolkning_efter_k%C3%B6n/FeatureServer",
typeKeywords: [
"ArcGIS Server",
"Data",
"Feature Access",
"Feature Service",
"Metadata",
"Service",
"Singlelayer",
"Hosted Service"
]
};
expect(getItemLayerId(item)).toBe("0");
});
});

describe("getItemHubId", () => {
let layerItem: any;
beforeEach(() => {
layerItem = {
id: "4ef",
access: "shared",
type: "Feature Service",
url:
"https://services9.arcgis.com/BH6j7VrWdIXhhNYw/arcgis/rest/services/Befolkning_efter_k%C3%B6n/FeatureServer/42"
};
});
it("returns undefined when not public", () => {
expect(getItemHubId(layerItem)).toBeFalsy();
});
it("returns itemId_layerId when public layer", () => {
layerItem.access = "public";
expect(getItemHubId(layerItem)).toBe("4ef_42");
});
it("returns item id when public non-layer", () => {
const item: any = {
id: "3ec",
access: "public"
};
expect(getItemHubId(item)).toBe("3ec");
});
});
10 changes: 8 additions & 2 deletions packages/content/src/content.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { IHubContent, IModel } from "@esri/hub-common";
import { getItemHubId, IHubContent, IModel } from "@esri/hub-common";
import { IGetContentOptions, getContentFromHub } from "./hub";
import { getContentFromPortal, itemToContent } from "./portal";
import { enrichContent } from "./enrichments";
Expand Down Expand Up @@ -50,9 +50,15 @@ export function getContent(
if (typeof idOrModel === "string") {
getContentPromise = getContentById(idOrModel, options);
} else {
// no need to fetch item and maybe not data
// no need to fetch item
const { item, data } = idOrModel;
const content = itemToContent(item);
if (!options.isPortal) {
// try to get the id to use w/ the Hub API
// NOTE: this is only set if the item is public
content.hubId = getItemHubId(item);
}
// no need to fetch data if it was passed in
content.data = data;
// just fetch and add any missing or desired enrichments
getContentPromise = enrichContent(content, options);
Expand Down
3 changes: 2 additions & 1 deletion packages/content/src/enrichments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,14 @@ export const enrichContent = (
// fetch any missing or requested enrichments
return fetchEnrichments(content, requestOptions).then(
(enrichments: Partial<IHubContent>) => {
const serverErrors = content.errors || [];
// merge derived portal URLs & fetched enrichments into content
const merged = {
...content,
...portalUrls,
...enrichments,
// include any previous errors (if any)
errors: [...content.errors, ...enrichments.errors]
errors: [...serverErrors, ...enrichments.errors]
};
// return the content with enriched dates
return _enrichDates(merged);
Expand Down
Loading

0 comments on commit c41b183

Please sign in to comment.