diff --git a/packages/common/src/collections.ts b/packages/common/src/collections.ts index dddf6b5c358..6d979390b35 100644 --- a/packages/common/src/collections.ts +++ b/packages/common/src/collections.ts @@ -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"]; @@ -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[] = [ diff --git a/packages/common/src/content.ts b/packages/common/src/content.ts index 91f510d17cc..f0af6ffba60 100644 --- a/packages/common/src/content.ts +++ b/packages/common/src/content.ts @@ -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"; @@ -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; +}; diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index a46c76cc160..4b86307d89a 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -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"; @@ -141,6 +148,7 @@ export type IRevertableTaskResult = /** * Types of Hub resources + * DEPRECATED: Use HubFamily instead */ export type HubType = | "member" @@ -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 */ @@ -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 `_` + * 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 @@ -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 /** @@ -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; + /** layer information (geometryType, fields, etc) for related layers in the service */ + layers?: Array>; + // layer types: Feature Layers, Raster Layers + /** layer information (geometryType, fields, etc) */ + layer?: Partial; + 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; /** Whether the content is downloadable in the Hub app */ isDownloadable: boolean; } diff --git a/packages/common/test/content.test.ts b/packages/common/test/content.test.ts index 1b8805001c2..0741f715bb1 100644 --- a/packages/common/test/content.test.ts +++ b/packages/common/test/content.test.ts @@ -3,7 +3,11 @@ import { getCollection, getTypes, getTypeCategories, - normalizeItemType + normalizeItemType, + isFeatureService, + getLayerIdFromUrl, + getItemLayerId, + getItemHubId } from "../src/content"; describe("getCollection", () => { @@ -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"); + }); +}); diff --git a/packages/content/src/content.ts b/packages/content/src/content.ts index a0eb2f1f29f..315a55161d3 100644 --- a/packages/content/src/content.ts +++ b/packages/content/src/content.ts @@ -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"; @@ -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); diff --git a/packages/content/src/enrichments.ts b/packages/content/src/enrichments.ts index 949745f9227..70bc1f02a23 100644 --- a/packages/content/src/enrichments.ts +++ b/packages/content/src/enrichments.ts @@ -428,13 +428,14 @@ export const enrichContent = ( // fetch any missing or requested enrichments return fetchEnrichments(content, requestOptions).then( (enrichments: Partial) => { + 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); diff --git a/packages/content/src/hub.ts b/packages/content/src/hub.ts index d6985c39a2d..8956bbbd398 100644 --- a/packages/content/src/hub.ts +++ b/packages/content/src/hub.ts @@ -130,50 +130,75 @@ export function datasetToContent(dataset: DatasetResource): IHubContent { boundary, extent, metadata, + modified, modifiedProvenance, slug, searchDescription, groupIds, structuredLicense, - layer, + // map and feature server enrichments server, - // dataset enrichments - isProxied - // recordCount - // TODO: fields, geometryType, layer?, server?, as needed + layers, + // NOTE: the Hub API also returns the following server properties + // but we should be able to get them from the above server object + // currentVersion, capabilities, tileInfo, serviceSpatialReference + // maxRecordCount, supportedQueryFormats, etc + // feature and raster layer enrichments + layer, + recordCount, + statistics, + // NOTE: the Hub API also returns the following layer properties + // but we should be able to get them from the above layer object + // supportedQueryFormats, supportsAdvancedQueries, advancedQueryCapabilities, useStandardizedQueries + // geometryType, objectIdField, displayField, fields, + // org properties? + orgId, + orgName, + organization, + orgExtent } = attributes; + // NOTE: we could throw or return if there are errors + // to prevent type errors trying to read properties below content.errors = errors; + // common enrichments content.boundary = boundary; - // setting this to null signals to enrichMetadata to skip this - content.metadata = metadata || null; - content.slug = slug; - content.groupIds = groupIds; - content.structuredLicense = structuredLicense; - content.layer = layer; - content.server = server; - content.isProxied = isProxied; if (!item.extent.length && extent && extent.coordinates) { // we fall back to the extent derived by the API // which prefers layer or service extents and ultimately // falls back to the org's extent content.extent = extent.coordinates; } + // setting this to null signals to enrichMetadata to skip this + content.metadata = metadata || null; + if (content.modified !== modified) { + // capture the enriched modified date + // NOTE: the item modified date is still available on content.item.modified + content.modified = modified; + content.updatedDate = new Date(modified); + content.updatedDateSource = modifiedProvenance; + } + content.slug = slug; if (searchDescription) { // overwrite default summary (from snippet) w/ search description content.summary = searchDescription; } - if (modifiedProvenance) { - // overwrite default updated source - content.updatedDateSource = modifiedProvenance; + content.groupIds = groupIds; + content.structuredLicense = structuredLicense; + // server enrichments + content.server = server; + content.layers = layers; + // layer enrichments + content.layer = layer; + content.recordCount = recordCount; + content.statistics = statistics; + // org enrichments + if (orgId) { + content.org = { + id: orgId, + name: orgName || organization, + extent: orgExtent + }; } - // type-specific enrichments - // TODO: should this be based on existence of attributes instead of hubType? - // TODO: if the latter, should we return a different subtype of IHubContent for this? - // if (content.hubType === "dataset") { - // content.recordCount = recordCount; - // // TODO: fields, geometryType, etc - // } - // TODO: any remaining enrichments return content; } @@ -205,6 +230,7 @@ export function datasetToItem(dataset: DatasetResource): IItem { owner, orgId, created, + // the Hub API returns item.modified in attributes.itemModified (below) modified, // NOTE: we use attributes.name to store the title or the service/layer name // but in Portal name is only used for file types to store the file name (read only) @@ -216,8 +242,8 @@ export function datasetToItem(dataset: DatasetResource): IItem { snippet, tags, thumbnail, - // the Hub API returns item.extent in attributes.itemExtent - itemExtent, + // the Hub API returns item.extent in attributes.itemExtent (below) + // extent, categories, contentStatus, // the Hub API doesn't currently return spatialReference @@ -253,9 +279,11 @@ export function datasetToItem(dataset: DatasetResource): IItem { numViews, itemControl, scoreCompleteness, - // additional attributes we'll need as fallbacks - createdAt, - updatedAt, + // additional attributes we'll need + // to derive the above values when missing + itemExtent, + itemModified, + modifiedProvenance, serviceSpatialReference } = attributes; @@ -269,8 +297,11 @@ export function datasetToItem(dataset: DatasetResource): IItem { id: itemId, owner: owner as string, orgId, - created: (created || createdAt) as number, - modified: (modified || updatedAt) as number, + created: created as number, + // for feature layers, modified will usually come from the layer so + // we prefer itemModified, but fall back to modified if it came from the item + modified: (itemModified || + (modifiedProvenance === "item.modified" && modified)) as number, title: (title || name) as string, type, typeKeywords, @@ -280,7 +311,7 @@ export function datasetToItem(dataset: DatasetResource): IItem { thumbnail, extent: itemExtent || - /* istanbul ignore next: I _think_ the API returns [] by default, but I'm not _sure_ */ [], + /* istanbul ignore next: API should always return itemExtent, but we default to [] just in case */ [], categories, contentStatus, spatialReference: spatialReference || serviceSpatialReference, diff --git a/packages/content/src/portal.ts b/packages/content/src/portal.ts index 476bc32b7d9..8239261c4e2 100644 --- a/packages/content/src/portal.ts +++ b/packages/content/src/portal.ts @@ -4,6 +4,7 @@ import { IItem, getItem } from "@esri/arcgis-rest-portal"; import { HubType, + HubFamily, IHubContent, IHubGeography, IHubRequestOptions, @@ -68,6 +69,7 @@ export function itemToContent(item: IItem): IHubContent { const createdDate = new Date(item.created); const createdDateSource = "item.created"; const properties = item.properties; + const normalizedType = normalizeItemType(item); const content = Object.assign({}, item, { // no server errors when fetching the item directly errors: [], @@ -78,11 +80,9 @@ export function itemToContent(item: IItem): IHubContent { // presumably there to use as the default file name when downloading // we don't store item.name in the Hub API and we use name for title name: item.title, - // TODO: should we alway be setting hubId here - // or only when we know the item exists in the index - hubId: item.id, + family: getFamily(normalizedType), hubType: getItemHubType(item), - normalizedType: normalizeItemType(item), + normalizedType, categories: parseItemCategories(item.categories), itemCategories: item.categories, // can we strip HTML from description, and do we need to trim it to a X chars? @@ -128,6 +128,47 @@ export function getItemHubType(itemOrType: IItem | string): HubType { return getCollection(itemType) as HubType; } +function collectionToFamily(collection: string): string { + const overrides: any = { + other: "content", + solution: "template" + }; + return overrides[collection] || collection; +} + +/** + * return the Hub family given an item's type + * @param type item type + * @returns Hub family + */ +export function getFamily(type: string) { + let family; + // override default behavior for the rows that are highlighted in yellow here: + // https://esriis.sharepoint.com/:x:/r/sites/ArcGISHub/_layouts/15/Doc.aspx?sourcedoc=%7BADA1C9DC-4F6C-4DE4-92C6-693EF9571CFA%7D&file=Hub%20Routes.xlsx&nav=MTBfe0VENEREQzI4LUZFMDctNEI0Ri04NjcyLThCQUE2MTA0MEZGRn1fezIwMTIwMEJFLTA4MEQtNEExRC05QzA4LTE5MTAzOUQwMEE1RH0&action=default&mobileredirect=true&cid=df1c874b-c367-4cea-bc13-7bebfad3f2ac + switch ((type || "").toLowerCase()) { + case "image service": + family = "dataset"; + break; + case "feature service": + case "raster layer": + // TODO: check if feature service has > 1 layer first? + family = "map"; + break; + case "microsoft excel": + family = "document"; + break; + case "cad drawing": + case "feature collection template": + case "report template": + family = "content"; + break; + default: + // by default derive from collection + family = collectionToFamily(getCollection(type)); + } + return family as HubFamily; +} + /** * Splits item category strings at slashes and discards the "Categories" keyword * diff --git a/packages/content/test/content.test.ts b/packages/content/test/content.test.ts index 82736f22965..9abc549d17d 100644 --- a/packages/content/test/content.test.ts +++ b/packages/content/test/content.test.ts @@ -44,7 +44,8 @@ describe("get content", () => { } } as IModel; - const ro = {} as IHubRequestOptions; + // setting isPortal here for hubId check below + const ro = { isPortal: true } as IHubRequestOptions; const content = await getContent(modelWithItem, ro); @@ -52,6 +53,9 @@ describe("get content", () => { expect(getContentFromHubSpy).not.toHaveBeenCalled(); expect(getContentFromPortalSpy).not.toHaveBeenCalled(); + // should not have set the hubId + expect(content.hubId).toBeFalsy("don't set hubId in portal"); + // should still load data expect(getDataSpy).toHaveBeenCalledWith(modelWithItem.item.id, ro); expect(content.data.from).toBe("api", "fetched data from API"); diff --git a/packages/content/test/enrichments.test.ts b/packages/content/test/enrichments.test.ts index c5a4c1feb81..2dcee3076d0 100644 --- a/packages/content/test/enrichments.test.ts +++ b/packages/content/test/enrichments.test.ts @@ -38,7 +38,6 @@ describe("enrichContent", () => { const getItemGroupsSpy = spyOn(arcgisRestPortal, "getItemGroups"); const getContentMetadataSpy = spyOn(metadataModule, "getContentMetadata"); const content = { - errors: [], id: "3ae", // signature for a Hub created web map would trigger getUser type: "Web Map", diff --git a/packages/content/test/hub.test.ts b/packages/content/test/hub.test.ts index 03acd62dc61..e04a844ed04 100644 --- a/packages/content/test/hub.test.ts +++ b/packages/content/test/hub.test.ts @@ -119,35 +119,72 @@ describe("hub", () => { const item = datasetToItem(dataset); expect(item.snippet).toBe(dataset.attributes.snippet); }); - it("falls back to createdAt/updatedAt when no created/modified", () => { - const dataset = cloneObject(documentsJson.data) as DatasetResource; + it("handles when no itemModified", () => { + // NOTE: I expect that the API always returns itemModified + // so I don't know if this ever happens + const dataset = cloneObject(featureLayerJson.data) as DatasetResource; const attributes = dataset.attributes; - attributes.createdAt = attributes.created; - attributes.updatedAt = attributes.modified; - delete attributes.created; - delete attributes.modified; - const item = datasetToItem(dataset); - expect(item.created).toBe(attributes.createdAt); - expect(item.modified).toBe(attributes.updatedAt); + attributes.modified = 1623232000295; + delete attributes.itemModified; + let item = datasetToItem(dataset); + expect(item.modified).toBe( + attributes.modified, + "returns modified when provenance is item" + ); + attributes.modifiedProvenance = "layer.editingInfo.lastEditDate"; + item = datasetToItem(dataset); + expect(item.modified).toBeFalsy( + "is undefined when provenance is layer.editingInfo" + ); }); // NOTE: other use cases are covered by getContent() tests }); describe("dataset to content", () => { + it("has a reference to the item", () => { + const dataset = cloneObject(documentsJson.data) as DatasetResource; + const content = datasetToContent(dataset); + expect(content.item).toEqual(datasetToItem(dataset)); + }); + it("has enriched updatedDate", () => { + const dataset = cloneObject(featureLayerJson.data) as DatasetResource; + const attributes = dataset.attributes; + // simulate API returning date the layer was last modified + // instead of the date the item was last modified + attributes.modified = 1623232000295; + attributes.modifiedProvenance = "layer.editingInfo.lastEditDate"; + const content = datasetToContent(dataset); + expect(content.modified).toBe(attributes.modified); + expect(content.updatedDate).toEqual(new Date(attributes.modified)); + expect(content.updatedDateSource).toBe(attributes.modifiedProvenance); + }); + it("has org", () => { + const dataset = cloneObject(featureLayerJson.data) as DatasetResource; + const { + orgId: id, + orgExtent: extent, + orgName: name, + organization + } = dataset.attributes; + let content = datasetToContent(dataset); + expect(content.org).toEqual({ id, extent, name }); + delete dataset.attributes.orgName; + content = datasetToContent(dataset); + expect(content.org).toEqual( + { id, extent, name: organization }, + "name falls back to organization" + ); + }); it("only uses enrichment attributes when they exist", () => { const dataset = cloneObject(documentsJson.data) as DatasetResource; + // NOTE: I don't necessarily expect the API to return w/o these + // but our code depends on them, this test is mostly here for coverage delete dataset.attributes.searchDescription; - delete dataset.attributes.modifiedProvenance; - dataset.attributes.isProxied = false; + delete dataset.attributes.errors; const content = datasetToContent(dataset); expect(content.summary).toBe(dataset.attributes.snippet); - expect(content.updatedDateSource).toBe("item.modified"); expect(content.extent).toEqual([]); - expect(content.isProxied).toBe(false); - }); - it("has a reference to the item", () => { - const dataset = cloneObject(documentsJson.data) as DatasetResource; - const content = datasetToContent(dataset); - expect(content.item).toEqual(datasetToItem(dataset)); + // NOTE: the document JSON does not have org attributes + expect(content.org).toBeUndefined(); }); // NOTE: other use cases are covered by getContent() tests }); diff --git a/packages/content/test/portal.test.ts b/packages/content/test/portal.test.ts index df2b669edfa..2a2496fd6da 100644 --- a/packages/content/test/portal.test.ts +++ b/packages/content/test/portal.test.ts @@ -8,8 +8,9 @@ import { getContentFromPortal, itemToContent, parseItemCategories, - getItemHubType -} from "../src/index"; + getItemHubType, + getFamily +} from "../src/portal"; import * as metadataModule from "../src/metadata"; import * as itemJson from "./mocks/items/map-service.json"; import { mockUserSession } from "./test-helpers/fake-user-session"; @@ -25,8 +26,11 @@ function validateContentFromPortal(content: IHubContent, item: IItem) { }); // name should be title expect(content.name).toBe(item.title); + // should not set hubId when in portal + expect(content.hubId).toBeUndefined(); // should include derived properties - expect(content.hubId).toBe(item.id); + expect(content.family).toBe("map"); + // DEPRECATED: remove hubType check expect(content.hubType).toBe("map"); expect(content.summary).toBe(item.snippet); expect(content.publisher).toEqual({ @@ -104,6 +108,29 @@ describe("item to content", () => { // NOTE: other use cases (including when a portal is passed) // are covered by getContentFromPortal() tests }); +describe("get item family", () => { + it("returns dataset for image service", () => { + expect(getFamily("Image Service")).toBe("dataset"); + }); + it("returns map for feature service and raster layer", () => { + expect(getFamily("Feature Service")).toBe("map"); + expect(getFamily("Raster Layer")).toBe("map"); + }); + it("returns document for excel", () => { + expect(getFamily("Microsoft Excel")).toBe("document"); + }); + it("returns template for solution", () => { + expect(getFamily("Solution")).toBe("template"); + }); + it("returns content for other specific types", () => { + expect(getFamily("CAD Drawing")).toBe("content"); + expect(getFamily("Feature Collection Template")).toBe("content"); + expect(getFamily("Report Template")).toBe("content"); + }); + it("returns content for collection other", () => { + expect(getFamily("360 VR Experience")).toBe("content"); + }); +}); describe("get item hub type", () => { it("normalizes item", () => { expect( diff --git a/packages/search/test/ago/encode-ago-query.test.ts b/packages/search/test/ago/encode-ago-query.test.ts index 0838aac67f8..5f4d2059b51 100644 --- a/packages/search/test/ago/encode-ago-query.test.ts +++ b/packages/search/test/ago/encode-ago-query.test.ts @@ -46,7 +46,7 @@ describe("encodeAgoQuery test", () => { const actual = encodeAgoQuery(params); const expected = { q: - '-type:"code attachment" AND crime AND ((group:"1ef")) AND (type:"Image Collection" OR type:"Image Service" OR type:"Map Service Layer" OR type:"Map Service" OR type:"Scene Service" OR type:"Vector Tile Service" OR type:"Web Map Service" OR type:"Web Map Tile Service" OR type:"Web Map" OR type:"Web Scene" OR type:"WFS" OR type:"WMS") AND (tags:"test")', + '-type:"code attachment" AND crime AND ((group:"1ef")) AND (type:"Image Collection" OR type:"Image Service" OR type:"Map Service Layer" OR type:"Map Service" OR type:"Scene Service" OR type:"Scene Layer" OR type:"Vector Tile Service" OR type:"Web Map Service" OR type:"Web Map Tile Service" OR type:"Web Map" OR type:"Web Scene" OR type:"WFS" OR type:"WMS" OR type:"WMTS") AND (tags:"test")', start: 1, num: 10, sortField: "title",