diff --git a/packages/downloads/src/download-status.ts b/packages/downloads/src/download-status.ts index 15a8ceb4184..aa6d587a1f7 100644 --- a/packages/downloads/src/download-status.ts +++ b/packages/downloads/src/download-status.ts @@ -3,6 +3,8 @@ export enum DownloadStatus { READY_UNKNOWN = "ready_unknown", STALE = "stale", NOT_READY = "not_ready", + LOCKED = "locked", + STALE_LOCKED = "stale_locked", DISABLED = "disabled", CREATING = "creating", UPDATING = "updating", @@ -16,6 +18,8 @@ export type DownloadStatuses = | "ready_unknown" | "stale" | "not_ready" + | "locked" + | "stale_locked" | "disabled" | "creating" | "updating" diff --git a/packages/downloads/src/portal/portal-request-download-metadata.ts b/packages/downloads/src/portal/portal-request-download-metadata.ts index 39160b97f8c..ab07d67dd90 100644 --- a/packages/downloads/src/portal/portal-request-download-metadata.ts +++ b/packages/downloads/src/portal/portal-request-download-metadata.ts @@ -11,6 +11,7 @@ import { urlBuilder, composeDownloadId } from "../utils"; import { DownloadTarget } from "../download-target"; import { DownloadStatus } from "../download-status"; import { isDownloadEnabled } from "./utils"; +import { isRecentlyUpdated } from "./utils"; enum ItemTypes { FeatureService = "Feature Service", @@ -164,6 +165,7 @@ function formatDownloadMetadata(params: any) { cachedDownload, serviceLastEditDate, authentication, + target, item, format } = params; @@ -175,9 +177,11 @@ function formatDownloadMetadata(params: any) { const { created, id } = cachedDownload || {}; + const recentlyUpdated = isRecentlyUpdated(target, serviceLastEditDate); const canDownload = isDownloadEnabled(item, format); + const status = canDownload - ? determineStatus(serviceLastEditDate, created) + ? determineStatus(serviceLastEditDate, created, recentlyUpdated) : DownloadStatus.DISABLED; if (!cachedDownload) { @@ -202,15 +206,19 @@ function formatDownloadMetadata(params: any) { }; } -function determineStatus(serviceLastEditDate: Date, exportCreatedDate: Date) { +function determineStatus( + serviceLastEditDate: Date, + exportCreatedDate: Date, + recentlyUpdated: boolean +) { if (!exportCreatedDate) { - return DownloadStatus.NOT_READY; + return recentlyUpdated ? DownloadStatus.LOCKED : DownloadStatus.NOT_READY; } if (!serviceLastEditDate) { return DownloadStatus.READY_UNKNOWN; } if (serviceLastEditDate > exportCreatedDate) { - return DownloadStatus.STALE; + return recentlyUpdated ? DownloadStatus.STALE_LOCKED : DownloadStatus.STALE; } return DownloadStatus.READY; } diff --git a/packages/downloads/src/portal/utils.ts b/packages/downloads/src/portal/utils.ts index b2bbd5f0ced..ed3312a9b5a 100644 --- a/packages/downloads/src/portal/utils.ts +++ b/packages/downloads/src/portal/utils.ts @@ -1,6 +1,23 @@ -import { IItem } from "@esri/arcgis-rest-types"; import { getProp } from "@esri/hub-common"; import { DownloadFormat } from "../download-format"; +import { DownloadTarget } from "../download-target"; + +const DOWNLOADS_LOCK_MS = 10 * 60 * 1000; +import { IItem } from "@esri/arcgis-rest-types"; + +/** + * @private + */ +export function isRecentlyUpdated( + target: DownloadTarget, + lastEditDate: number +): boolean { + return ( + target === "portal" && + lastEditDate && + new Date().getTime() - lastEditDate <= DOWNLOADS_LOCK_MS + ); +} /** * @private diff --git a/packages/downloads/test/portal/portal-request-download-metadata.test.ts b/packages/downloads/test/portal/portal-request-download-metadata.test.ts index 00a678d1daf..6fedec91647 100644 --- a/packages/downloads/test/portal/portal-request-download-metadata.test.ts +++ b/packages/downloads/test/portal/portal-request-download-metadata.test.ts @@ -455,7 +455,7 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("layer id, single-layer, has lastEditDate, not cached, item download disabled", async done => { + it("layer id, single-layer, has lastEditDate within 10 mins, not cached, targets hub", async done => { const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( @@ -463,12 +463,7 @@ describe("portalRequestDownloadMetadata", () => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - enabled: false - } - } + url: "http://feature-service.com/FeatureServer" }); }) ); @@ -506,7 +501,7 @@ describe("portalRequestDownloadMetadata", () => { downloadId: "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", lastEditDate: nineMinutesAgo.toISOString(), - status: "disabled" + status: "not_ready" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -548,7 +543,7 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("layer id, single-layer, has lastEditDate, not cached, format download disabled", async done => { + it("layer id, single-layer, has lastEditDate within 10 mins, not cached, targets portal", async done => { const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( @@ -556,17 +551,7 @@ describe("portalRequestDownloadMetadata", () => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - enabled: true, - formats: { - csv: { - enabled: false - } - } - } - } + url: "http://feature-service.com/FeatureServer" }); }) ); @@ -597,14 +582,15 @@ describe("portalRequestDownloadMetadata", () => { const result = await portalRequestDownloadMetadata({ datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", - authentication + authentication, + target: "portal" }); expect(result).toEqual({ downloadId: "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", lastEditDate: nineMinutesAgo.toISOString(), - status: "disabled" + status: "locked" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -646,7 +632,8 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, no layers, not cached", async done => { + it("layer id, single-layer, has lastEditDate within 10 mins, not cached, targets enterprise", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { @@ -657,35 +644,37 @@ describe("portalRequestDownloadMetadata", () => { }); }) ); - const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { - resolve({}); + resolve({ + layers: [{ id: 0 }] + }); }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( new Promise(resolve => { - resolve({}); + resolve({ + id: 0, + editingInfo: { lastEditDate: nineMinutesAgo.getTime() } + }); }) ); - const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( new Promise(resolve => { resolve({ results: [] }); }) ); - const result = await portalRequestDownloadMetadata({ - datasetId: "abcdef0123456789abcdef0123456789", + datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", - authentication + authentication, + target: "enterprise" }); expect(result).toEqual({ downloadId: - "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", - lastEditDate: undefined, + "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", + lastEditDate: nineMinutesAgo.toISOString(), status: "not_ready" }); @@ -703,14 +692,20 @@ describe("portalRequestDownloadMetadata", () => { authentication } ]); - expect(getLayerSpy.calls.count()).toEqual(0); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); expect(searchItemsSpy.calls.count()).toEqual(1); expect(searchItemsSpy.calls.argsFor(0)).toEqual([ { authentication, num: 1, q: - 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', sortField: "modified", sortOrder: "DESC" } @@ -722,14 +717,20 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, multi-layer, no lastEditDate, not cached", async done => { + it("layer id, single-layer, has recent lastEditDate, not cached, item download disabled", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer" + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + enabled: false + } + } }); }) ); @@ -737,17 +738,17 @@ describe("portalRequestDownloadMetadata", () => { const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { resolve({ - layers: [{ id: 0 }, { id: 1 }] + layers: [{ id: 0 }] }); }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( - new Promise(resolve => { - resolve({ id: 0 }); - }), + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( new Promise(resolve => { - resolve({ id: 1 }); + resolve({ + id: 0, + editingInfo: { lastEditDate: nineMinutesAgo.getTime() } + }); }) ); @@ -758,16 +759,16 @@ describe("portalRequestDownloadMetadata", () => { ); const result = await portalRequestDownloadMetadata({ - datasetId: "abcdef0123456789abcdef0123456789", + datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", authentication }); expect(result).toEqual({ downloadId: - "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", - lastEditDate: undefined, - status: "not_ready" + "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", + lastEditDate: nineMinutesAgo.toISOString(), + status: "disabled" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -784,26 +785,20 @@ describe("portalRequestDownloadMetadata", () => { authentication } ]); - expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.count()).toEqual(1); expect(getLayerSpy.calls.argsFor(0)).toEqual([ { url: "http://feature-service.com/FeatureServer/0", authentication } ]); - expect(getLayerSpy.calls.argsFor(1)).toEqual([ - { - url: "http://feature-service.com/FeatureServer/1", - authentication - } - ]); expect(searchItemsSpy.calls.count()).toEqual(1); expect(searchItemsSpy.calls.argsFor(0)).toEqual([ { authentication, num: 1, q: - 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', sortField: "modified", sortOrder: "DESC" } @@ -815,14 +810,25 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("layer id, multi-layer, no lastEditDate, not cached", async done => { + it("layer id, single-layer, has lastEditDate, not cached, format download disabled", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer" + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + enabled: true, + formats: { + csv: { + enabled: false + } + } + } + } }); }) ); @@ -830,17 +836,17 @@ describe("portalRequestDownloadMetadata", () => { const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { resolve({ - layers: [{ id: 0 }, { id: 1 }] + layers: [{ id: 0 }] }); }) ); - const getLayer = spyOn(featureLayer, "getLayer").and.returnValues( - new Promise(resolve => { - resolve({ id: 0 }); - }), + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( new Promise(resolve => { - resolve({ id: 1 }); + resolve({ + id: 0, + editingInfo: { lastEditDate: nineMinutesAgo.getTime() } + }); }) ); @@ -859,8 +865,8 @@ describe("portalRequestDownloadMetadata", () => { expect(result).toEqual({ downloadId: "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", - lastEditDate: undefined, - status: "not_ready" + lastEditDate: nineMinutesAgo.toISOString(), + status: "disabled" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -877,19 +883,13 @@ describe("portalRequestDownloadMetadata", () => { authentication } ]); - expect(getLayer.calls.count()).toEqual(2); - expect(getLayer.calls.argsFor(0)).toEqual([ + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ { url: "http://feature-service.com/FeatureServer/0", authentication } ]); - expect(getLayer.calls.argsFor(1)).toEqual([ - { - url: "http://feature-service.com/FeatureServer/1", - authentication - } - ]); expect(searchItemsSpy.calls.count()).toEqual(1); expect(searchItemsSpy.calls.argsFor(0)).toEqual([ { @@ -908,10 +908,7 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, multi-layer, has lastEditDate, not cached", async done => { - const twelveMinsAgo = new Date(new Date().getTime() - 12 * 60 * 1000); - const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); - + it("no layer id, no layers, not cached", async done => { try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { @@ -925,24 +922,13 @@ describe("portalRequestDownloadMetadata", () => { const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { - resolve({ - layers: [{ id: 0 }, { id: 1 }] - }); + resolve({}); }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( - new Promise(resolve => { - resolve({ - id: 0, - editingInfo: { lastEditDate: twelveMinsAgo.getTime() } - }); - }), + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( new Promise(resolve => { - resolve({ - id: 1, - editingInfo: { lastEditDate: elevenMinsAgo.getTime() } - }); + resolve({}); }) ); @@ -961,7 +947,7 @@ describe("portalRequestDownloadMetadata", () => { expect(result).toEqual({ downloadId: "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", - lastEditDate: elevenMinsAgo.toISOString(), + lastEditDate: undefined, status: "not_ready" }); @@ -979,26 +965,14 @@ describe("portalRequestDownloadMetadata", () => { authentication } ]); - expect(getLayerSpy.calls.count()).toEqual(2); - expect(getLayerSpy.calls.argsFor(0)).toEqual([ - { - url: "http://feature-service.com/FeatureServer/0", - authentication - } - ]); - expect(getLayerSpy.calls.argsFor(1)).toEqual([ - { - url: "http://feature-service.com/FeatureServer/1", - authentication - } - ]); + expect(getLayerSpy.calls.count()).toEqual(0); expect(searchItemsSpy.calls.count()).toEqual(1); expect(searchItemsSpy.calls.argsFor(0)).toEqual([ { authentication, num: 1, q: - 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', sortField: "modified", sortOrder: "DESC" } @@ -1010,22 +984,14 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, multi-layer, has lastEditDate, not cached, item download disabled", async done => { - const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); - const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); - + it("no layer id, multi-layer, no lastEditDate, not cached", async done => { try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - enabled: false - } - } + url: "http://feature-service.com/FeatureServer" }); }) ); @@ -1040,16 +1006,10 @@ describe("portalRequestDownloadMetadata", () => { const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( new Promise(resolve => { - resolve({ - id: 0, - editingInfo: { lastEditDate: elevenMinsAgo.getTime() } - }); + resolve({ id: 0 }); }), new Promise(resolve => { - resolve({ - id: 1, - editingInfo: { lastEditDate: nineMinsAgo.getTime() } - }); + resolve({ id: 1 }); }) ); @@ -1068,8 +1028,8 @@ describe("portalRequestDownloadMetadata", () => { expect(result).toEqual({ downloadId: "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", - lastEditDate: nineMinsAgo.toISOString(), - status: "disabled" + lastEditDate: undefined, + status: "not_ready" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -1117,26 +1077,14 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, multi-layer, has lastEditDate, not cached, format download disabled", async done => { - const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); - const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); - + it("layer id, multi-layer, no lastEditDate, not cached", async done => { try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - formats: { - csv: { - enabled: false - } - } - } - } + url: "http://feature-service.com/FeatureServer" }); }) ); @@ -1149,18 +1097,12 @@ describe("portalRequestDownloadMetadata", () => { }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + const getLayer = spyOn(featureLayer, "getLayer").and.returnValues( new Promise(resolve => { - resolve({ - id: 0, - editingInfo: { lastEditDate: elevenMinsAgo.getTime() } - }); + resolve({ id: 0 }); }), new Promise(resolve => { - resolve({ - id: 1, - editingInfo: { lastEditDate: nineMinsAgo.getTime() } - }); + resolve({ id: 1 }); }) ); @@ -1171,16 +1113,16 @@ describe("portalRequestDownloadMetadata", () => { ); const result = await portalRequestDownloadMetadata({ - datasetId: "abcdef0123456789abcdef0123456789", + datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", authentication }); expect(result).toEqual({ downloadId: - "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", - lastEditDate: nineMinsAgo.toISOString(), - status: "disabled" + "abcdef0123456789abcdef0123456789_0:CSV:undefined:undefined:undefined", + lastEditDate: undefined, + status: "not_ready" }); expect(getItemSpy.calls.count()).toEqual(1); @@ -1197,14 +1139,14 @@ describe("portalRequestDownloadMetadata", () => { authentication } ]); - expect(getLayerSpy.calls.count()).toEqual(2); - expect(getLayerSpy.calls.argsFor(0)).toEqual([ + expect(getLayer.calls.count()).toEqual(2); + expect(getLayer.calls.argsFor(0)).toEqual([ { url: "http://feature-service.com/FeatureServer/0", authentication } ]); - expect(getLayerSpy.calls.argsFor(1)).toEqual([ + expect(getLayer.calls.argsFor(1)).toEqual([ { url: "http://feature-service.com/FeatureServer/1", authentication @@ -1216,7 +1158,7 @@ describe("portalRequestDownloadMetadata", () => { authentication, num: 1, q: - 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', sortField: "modified", sortOrder: "DESC" } @@ -1228,7 +1170,10 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("no layer id, single-layer, no lastEditDate, cached, ready (unknown)", async done => { + it("no layer id, multi-layer, has lastEditDate, not cached", async done => { + const twelveMinsAgo = new Date(new Date().getTime() - 12 * 60 * 1000); + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { @@ -1243,14 +1188,1150 @@ describe("portalRequestDownloadMetadata", () => { const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { resolve({ - layers: [{ id: 0 }] + layers: [{ id: 0 }, { id: 1 }] }); }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( new Promise(resolve => { - resolve({ id: 0 }); + resolve({ + id: 0, + editingInfo: { lastEditDate: twelveMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication + }); + + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: elevenMinsAgo.toISOString(), + status: "not_ready" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, multi-layer, has lastEditDate within 10 mins, not cached, targets hub", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }, { id: 1 }] + }); + }) + ); + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: nineMinsAgo.getTime() } + }); + }) + ); + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication + }); + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: nineMinsAgo.toISOString(), + status: "not_ready" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, multi-layer, has lastEditDate within 10 mins, not cached, targets portal", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }, { id: 1 }] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: nineMinsAgo.getTime() } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication, + target: "portal" + }); + + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: nineMinsAgo.toISOString(), + status: "locked" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, multi-layer, has lastEditDate within 10 mins, not cached, targets enterprise", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }, { id: 1 }] + }); + }) + ); + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: nineMinsAgo.getTime() } + }); + }) + ); + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication, + target: "enterprise" + }); + + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: nineMinsAgo.toISOString(), + status: "not_ready" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, multi-layer, has lastEditDate, not cached, item download disabled", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + enabled: false + } + } + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }, { id: 1 }] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: nineMinsAgo.getTime() } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication + }); + + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: nineMinsAgo.toISOString(), + status: "disabled" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, multi-layer, has lastEditDate, not cached, format download disabled", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + formats: { + csv: { + enabled: false + } + } + } + } + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }, { id: 1 }] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValues( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { lastEditDate: elevenMinsAgo.getTime() } + }); + }), + new Promise(resolve => { + resolve({ + id: 1, + editingInfo: { lastEditDate: nineMinsAgo.getTime() } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ results: [] }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication + }); + + expect(result).toEqual({ + downloadId: + "abcdef0123456789abcdef0123456789:CSV:undefined:undefined:undefined", + lastEditDate: nineMinsAgo.toISOString(), + status: "disabled" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(2); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(getLayerSpy.calls.argsFor(1)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/1", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV Collection" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("no layer id, single-layer, no lastEditDate, cached, ready (unknown)", async done => { + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [{ id: 0 }] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ id: 0 }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: new Date(1594450876000).getTime() + } + ] + }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789", + format: "CSV", + authentication + }); + + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: undefined, + status: "ready_unknown", + contentLastModified: "2020-07-11T07:01:16.000Z", + lastModified: "2020-07-11T07:01:16.000Z", + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate, cached, ready", async done => { + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1593450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 + } + ] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: new Date(1584450876000).getTime() + } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: new Date(1594450876000).getTime() + } + ] + }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789_0", + format: "CSV", + authentication + }); + + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: new Date(1584450876000).toISOString(), + status: "ready", + contentLastModified: "2020-07-11T07:01:16.000Z", + lastModified: "2020-07-11T07:01:16.000Z", + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate within last 10 mins, cached, ready, targets hub", async done => { + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: nineMinsAgo.getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 + } + ] + }); + }) + ); + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: nineMinsAgo.getTime() + } + }); + }) + ); + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: nineMinsAgo.getTime() + } + ] + }); + }) + ); + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789_0", + format: "CSV", + authentication + }); + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: nineMinsAgo.toISOString(), + status: "ready", + contentLastModified: nineMinsAgo.toISOString(), + lastModified: nineMinsAgo.toISOString(), + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate within last 10 mins, cached, ready, targets portal", async done => { + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: nineMinsAgo.getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 + } + ] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: nineMinsAgo.getTime() + } + }); + }) + ); + + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: nineMinsAgo.getTime() + } + ] + }); + }) + ); + + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789_0", + format: "CSV", + authentication, + target: "portal" + }); + + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: nineMinsAgo.toISOString(), + status: "ready", + contentLastModified: nineMinsAgo.toISOString(), + lastModified: nineMinsAgo.toISOString(), + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate within last 10 mins, cached, ready, targets enterprise", async done => { + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: nineMinsAgo.getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 + } + ] + }); + }) + ); + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: nineMinsAgo.getTime() + } + }); + }) + ); + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: nineMinsAgo.getTime() + } + ] + }); + }) + ); + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789_0", + format: "CSV", + authentication, + target: "enterprise" + }); + + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: nineMinsAgo.toISOString(), + status: "ready", + contentLastModified: nineMinsAgo.toISOString(), + lastModified: nineMinsAgo.toISOString(), + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate, cached, ready, item download disabled", async done => { + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: nineMinsAgo.getTime(), + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + enabled: false + } + } + }); + }) + ); + + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 + } + ] + }); + }) + ); + + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: nineMinsAgo.getTime() + } + }); }) ); @@ -1260,7 +2341,7 @@ describe("portalRequestDownloadMetadata", () => { results: [ { id: "abcdef", - created: new Date(1594450876000).getTime() + created: nineMinsAgo.getTime() } ] }); @@ -1268,17 +2349,17 @@ describe("portalRequestDownloadMetadata", () => { ); const result = await portalRequestDownloadMetadata({ - datasetId: "abcdef0123456789abcdef0123456789", + datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", authentication }); expect(result).toEqual({ downloadId: "abcdef", - lastEditDate: undefined, - status: "ready_unknown", - contentLastModified: "2020-07-11T07:01:16.000Z", - lastModified: "2020-07-11T07:01:16.000Z", + lastEditDate: nineMinsAgo.toISOString(), + status: "disabled", + contentLastModified: nineMinsAgo.toISOString(), + lastModified: nineMinsAgo.toISOString(), downloadUrl: "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" }); @@ -1310,7 +2391,7 @@ describe("portalRequestDownloadMetadata", () => { authentication, num: 1, q: - 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:null,spatialRefId:undefined"', + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', sortField: "modified", sortOrder: "DESC" } @@ -1322,14 +2403,25 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("has lastEditDate, cached, ready", async done => { + it("has lastEditDate, cached, ready, format download disabled", async done => { + const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", - modified: new Date(1593450876).getTime(), - url: "http://feature-service.com/FeatureServer" + modified: nineMinsAgo.getTime(), + url: "http://feature-service.com/FeatureServer", + properties: { + downloadsConfig: { + formats: { + csv: { + enabled: false + } + } + } + } }); }) ); @@ -1351,7 +2443,7 @@ describe("portalRequestDownloadMetadata", () => { resolve({ id: 0, editingInfo: { - lastEditDate: new Date(1584450876000).getTime() + lastEditDate: nineMinsAgo.getTime() } }); }) @@ -1363,7 +2455,7 @@ describe("portalRequestDownloadMetadata", () => { results: [ { id: "abcdef", - created: new Date(1594450876000).getTime() + created: nineMinsAgo.getTime() } ] }); @@ -1378,10 +2470,10 @@ describe("portalRequestDownloadMetadata", () => { expect(result).toEqual({ downloadId: "abcdef", - lastEditDate: new Date(1584450876000).toISOString(), - status: "ready", - contentLastModified: "2020-07-11T07:01:16.000Z", - lastModified: "2020-07-11T07:01:16.000Z", + lastEditDate: nineMinsAgo.toISOString(), + status: "disabled", + contentLastModified: nineMinsAgo.toISOString(), + lastModified: nineMinsAgo.toISOString(), downloadUrl: "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" }); @@ -1425,21 +2517,15 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("has lastEditDate, cached, ready, item download disabled", async done => { - const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); - + it("has lastEditDate, cached, stale", async done => { + const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", - modified: nineMinsAgo.getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - enabled: false - } - } + modified: new Date(1573450876).getTime(), + url: "http://feature-service.com/FeatureServer" }); }) ); @@ -1461,7 +2547,7 @@ describe("portalRequestDownloadMetadata", () => { resolve({ id: 0, editingInfo: { - lastEditDate: nineMinsAgo.getTime() + lastEditDate: elevenMinsAgo.getTime() } }); }) @@ -1473,7 +2559,7 @@ describe("portalRequestDownloadMetadata", () => { results: [ { id: "abcdef", - created: nineMinsAgo.getTime() + created: new Date(1574450876000).getTime() } ] }); @@ -1488,10 +2574,10 @@ describe("portalRequestDownloadMetadata", () => { expect(result).toEqual({ downloadId: "abcdef", - lastEditDate: nineMinsAgo.toISOString(), - status: "disabled", - contentLastModified: nineMinsAgo.toISOString(), - lastModified: nineMinsAgo.toISOString(), + lastEditDate: elevenMinsAgo.toISOString(), + status: "stale", + contentLastModified: "2019-11-22T19:27:56.000Z", + lastModified: "2019-11-22T19:27:56.000Z", downloadUrl: "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" }); @@ -1535,28 +2621,116 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("has lastEditDate, cached, ready, format download disabled", async done => { - const nineMinsAgo = new Date(new Date().getTime() - 9 * 60 * 1000); - + it("has lastEditDate within last 10 minutes, cached, stale, targets hub", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { resolve({ type: "Feature Service", - modified: nineMinsAgo.getTime(), - url: "http://feature-service.com/FeatureServer", - properties: { - downloadsConfig: { - formats: { - csv: { - enabled: false - } - } + modified: new Date(1573450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); + const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( + new Promise(resolve => { + resolve({ + layers: [ + { + id: 0 } + ] + }); + }) + ); + const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( + new Promise(resolve => { + resolve({ + id: 0, + editingInfo: { + lastEditDate: nineMinutesAgo.getTime() } }); }) ); + const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( + new Promise(resolve => { + resolve({ + results: [ + { + id: "abcdef", + created: new Date(1574450876000).getTime() + } + ] + }); + }) + ); + const result = await portalRequestDownloadMetadata({ + datasetId: "abcdef0123456789abcdef0123456789_0", + format: "CSV", + authentication + }); + expect(result).toEqual({ + downloadId: "abcdef", + lastEditDate: nineMinutesAgo.toISOString(), + status: "stale", + contentLastModified: "2019-11-22T19:27:56.000Z", + lastModified: "2019-11-22T19:27:56.000Z", + downloadUrl: + "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" + }); + expect(getItemSpy.calls.count()).toEqual(1); + expect(getItemSpy.calls.argsFor(0)).toEqual([ + "abcdef0123456789abcdef0123456789", + { + authentication + } + ]); + expect(getServiceSpy.calls.count()).toEqual(1); + expect(getServiceSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer", + authentication + } + ]); + expect(getLayerSpy.calls.count()).toEqual(1); + expect(getLayerSpy.calls.argsFor(0)).toEqual([ + { + url: "http://feature-service.com/FeatureServer/0", + authentication + } + ]); + expect(searchItemsSpy.calls.count()).toEqual(1); + expect(searchItemsSpy.calls.argsFor(0)).toEqual([ + { + authentication, + num: 1, + q: + 'type:"CSV" AND typekeywords:"exportItem:abcdef0123456789abcdef0123456789,exportLayer:00,spatialRefId:undefined"', + sortField: "modified", + sortOrder: "DESC" + } + ]); + } catch (err) { + expect(err).toEqual(undefined); + } finally { + done(); + } + }); + + it("has lastEditDate within last 10 minutes, cached, stale, targets portal", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); + try { + const getItemSpy = spyOn(portal, "getItem").and.returnValue( + new Promise(resolve => { + resolve({ + type: "Feature Service", + modified: new Date(1573450876).getTime(), + url: "http://feature-service.com/FeatureServer" + }); + }) + ); const getServiceSpy = spyOn(featureLayer, "getService").and.returnValue( new Promise(resolve => { @@ -1575,7 +2749,7 @@ describe("portalRequestDownloadMetadata", () => { resolve({ id: 0, editingInfo: { - lastEditDate: nineMinsAgo.getTime() + lastEditDate: nineMinutesAgo.getTime() } }); }) @@ -1587,7 +2761,7 @@ describe("portalRequestDownloadMetadata", () => { results: [ { id: "abcdef", - created: nineMinsAgo.getTime() + created: new Date(1574450876000).getTime() } ] }); @@ -1597,15 +2771,16 @@ describe("portalRequestDownloadMetadata", () => { const result = await portalRequestDownloadMetadata({ datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", - authentication + authentication, + target: "portal" }); expect(result).toEqual({ downloadId: "abcdef", - lastEditDate: nineMinsAgo.toISOString(), - status: "disabled", - contentLastModified: nineMinsAgo.toISOString(), - lastModified: nineMinsAgo.toISOString(), + lastEditDate: nineMinutesAgo.toISOString(), + status: "stale_locked", + contentLastModified: "2019-11-22T19:27:56.000Z", + lastModified: "2019-11-22T19:27:56.000Z", downloadUrl: "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" }); @@ -1649,8 +2824,8 @@ describe("portalRequestDownloadMetadata", () => { } }); - it("has lastEditDate, cached, stale", async done => { - const elevenMinsAgo = new Date(new Date().getTime() - 11 * 60 * 1000); + it("has lastEditDate within last 10 minutes, cached, stale, targets enterprise", async done => { + const nineMinutesAgo = new Date(new Date().getTime() - 9 * 60 * 1000); try { const getItemSpy = spyOn(portal, "getItem").and.returnValue( new Promise(resolve => { @@ -1673,18 +2848,16 @@ describe("portalRequestDownloadMetadata", () => { }); }) ); - const getLayerSpy = spyOn(featureLayer, "getLayer").and.returnValue( new Promise(resolve => { resolve({ id: 0, editingInfo: { - lastEditDate: elevenMinsAgo.getTime() + lastEditDate: nineMinutesAgo.getTime() } }); }) ); - const searchItemsSpy = spyOn(portal, "searchItems").and.returnValue( new Promise(resolve => { resolve({ @@ -1697,23 +2870,22 @@ describe("portalRequestDownloadMetadata", () => { }); }) ); - const result = await portalRequestDownloadMetadata({ datasetId: "abcdef0123456789abcdef0123456789_0", format: "CSV", - authentication + authentication, + target: "enterprise" }); expect(result).toEqual({ downloadId: "abcdef", - lastEditDate: elevenMinsAgo.toISOString(), + lastEditDate: nineMinutesAgo.toISOString(), status: "stale", contentLastModified: "2019-11-22T19:27:56.000Z", lastModified: "2019-11-22T19:27:56.000Z", downloadUrl: "http://portal.com/sharing/rest/content/items/abcdef/data?token=123" }); - expect(getItemSpy.calls.count()).toEqual(1); expect(getItemSpy.calls.argsFor(0)).toEqual([ "abcdef0123456789abcdef0123456789", diff --git a/packages/downloads/test/portal/utils.test.ts b/packages/downloads/test/portal/utils.test.ts index 9444df85b85..5382878abb8 100644 --- a/packages/downloads/test/portal/utils.test.ts +++ b/packages/downloads/test/portal/utils.test.ts @@ -1,7 +1,45 @@ import { IItem } from "@esri/arcgis-rest-portal"; +import { DownloadTarget } from "../../src/download-target"; +import { isRecentlyUpdated } from "../../src/portal/utils"; import { DownloadFormats } from "../../src/download-format"; import { isDownloadEnabled } from "../../src/portal/utils"; +describe("isRecentlyUpdated", () => { + it('should return false if target is not "portal", even if data was recently updated', done => { + const targetOne: DownloadTarget = undefined; + const targetTwo: DownloadTarget = "hub"; + const targetThree: DownloadTarget = "enterprise"; + const lastEditDate: number = new Date().getTime(); + + expect(isRecentlyUpdated(targetOne, lastEditDate)).toBeFalsy(); + expect(isRecentlyUpdated(targetTwo, lastEditDate)).toBeFalsy(); + expect(isRecentlyUpdated(targetThree, lastEditDate)).toBeFalsy(); + done(); + }); + + it("should return false for any target if data was not recently updated", done => { + const targetOne: DownloadTarget = undefined; + const targetTwo: DownloadTarget = "hub"; + const targetThree: DownloadTarget = "enterprise"; + const targetFour: DownloadTarget = "portal"; + const lastEditDate: number = 1000; + + expect(isRecentlyUpdated(targetOne, lastEditDate)).toBeFalsy(); + expect(isRecentlyUpdated(targetTwo, lastEditDate)).toBeFalsy(); + expect(isRecentlyUpdated(targetThree, lastEditDate)).toBeFalsy(); + expect(isRecentlyUpdated(targetFour, lastEditDate)).toBeFalsy(); + done(); + }); + + it('should return true only if target is "portal" and if data was recently updated', done => { + const target: DownloadTarget = "portal"; + const lastEditDate: number = new Date().getTime(); + + expect(isRecentlyUpdated(target, lastEditDate)).toBeTruthy(); + done(); + }); +}); + describe("isDownloadEnabled", () => { it("true if item and format downloads are implicitly enabled", done => { const item: IItem = {