From 5e196610d5fc92cc610024c54015b483bb1aa842 Mon Sep 17 00:00:00 2001 From: Vivian Zhang Date: Fri, 19 Jun 2020 17:46:48 -0400 Subject: [PATCH 1/2] Fn working, add tests Signed-off-by: Vivian Zhang --- .../src/items/does-item-exist-with-title.ts | 28 ++++++++++++ .../common/src/items/get-unique-item-title.ts | 39 +++++++++++++++++ packages/common/src/items/index.ts | 2 + .../test/items/does-group-exist.test.ts | 0 .../items/does-item-exist-with-title.test.ts | 43 +++++++++++++++++++ .../test/items/get-unique-item-title.test.ts | 41 ++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 packages/common/src/items/does-item-exist-with-title.ts create mode 100644 packages/common/src/items/get-unique-item-title.ts create mode 100644 packages/common/test/items/does-group-exist.test.ts create mode 100644 packages/common/test/items/does-item-exist-with-title.test.ts create mode 100644 packages/common/test/items/get-unique-item-title.test.ts diff --git a/packages/common/src/items/does-item-exist-with-title.ts b/packages/common/src/items/does-item-exist-with-title.ts new file mode 100644 index 00000000000..4caeaa095cb --- /dev/null +++ b/packages/common/src/items/does-item-exist-with-title.ts @@ -0,0 +1,28 @@ +import { searchItems } from '@esri/arcgis-rest-portal'; +import { IAuthenticationManager } from '@esri/arcgis-rest-request'; + +/** + * Check if a site/page exists with a specific name +*/ +export function doesItemExistWithTitle ( + itemTitle: string, + options: Record, + authMgr: IAuthenticationManager, +) { + // if options have multiple properties, put them into one string separated with 'AND' + let optionsQuery = Object.keys(options).map(key => { + return `${key}:${options[key]}`; + }).join(' AND '); + let opts = { + q: `title: "${itemTitle}"`, + authentication: authMgr + }; + if (optionsQuery.length) { + opts.q = `${opts.q} AND ${optionsQuery}`; + } + return searchItems(opts) + .then(searchResponse => searchResponse.results.length > 0) + .catch(e => { + throw Error(`Error in doesItemExistWithTitle ${e}`); + }); +} diff --git a/packages/common/src/items/get-unique-item-title.ts b/packages/common/src/items/get-unique-item-title.ts new file mode 100644 index 00000000000..f722859b23e --- /dev/null +++ b/packages/common/src/items/get-unique-item-title.ts @@ -0,0 +1,39 @@ +import { doesItemExistWithTitle } from "./does-item-exist-with-title"; +import { IAuthenticationManager } from '@esri/arcgis-rest-request'; + +/** + * Given a title, construct a site/page title that is unique + * if that title exists, this fn will add a number on the end, and increment until + * an available title is found + * @param {string} title site/page title to ensure if unique + * @param {object} options an object that can be passed in to the q, eg. typekeywords, type + * @param {object} authMgr auth info tells the function what url to use for the "root" of the API, + * if missing, it will search against PROD + * @param {number} step Number to increment. Defaults to 0 + */ + +export function getUniqueItemTitle ( + title: string, + options: Record, + authMgr: IAuthenticationManager, + step = 0 +): Promise { + let combinedName = title; + + if (step) { + combinedName = `${title} ${step}`; + } + + return doesItemExistWithTitle(combinedName, options, authMgr) + .then(result => { + if (result) { + step++; + return getUniqueItemTitle(title, options, authMgr, step); + } else { + return combinedName; + } + }) + .catch(e => { + throw Error(`Error in getUniqueItemTitle ${e}`); + }); +} \ No newline at end of file diff --git a/packages/common/src/items/index.ts b/packages/common/src/items/index.ts index 43d796d432c..fe9b5c195e4 100644 --- a/packages/common/src/items/index.ts +++ b/packages/common/src/items/index.ts @@ -12,3 +12,5 @@ export * from "./serialize-model"; export * from "./share-item-to-groups"; export * from "./unprotect-model"; export * from "./unshare-item-from-groups"; +export * from "./does-item-exist-with-title"; +export * from "./get-unique-item-title"; \ No newline at end of file diff --git a/packages/common/test/items/does-group-exist.test.ts b/packages/common/test/items/does-group-exist.test.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/common/test/items/does-item-exist-with-title.test.ts b/packages/common/test/items/does-item-exist-with-title.test.ts new file mode 100644 index 00000000000..6f7dba0f899 --- /dev/null +++ b/packages/common/test/items/does-item-exist-with-title.test.ts @@ -0,0 +1,43 @@ +import { doesItemExistWithTitle } from '../../src'; +import * as portalModule from "@esri/arcgis-rest-portal"; +import { IAuthenticationManager } from '@esri/arcgis-rest-request'; + +describe('doesItemExistWithTitle', () => { + const opts = { + typekeywords: 'typeX' + }; + + const authMgr = {} as IAuthenticationManager; + + it('should resolve true when item with same name exists', async () => { + const spy = spyOn(portalModule, "searchItems").and.returnValue( + Promise.resolve({ + results: [{}] + }) + ); + const res = await doesItemExistWithTitle("exists", opts, authMgr); + expect(res).toBeTruthy(); + }); + + it('should resolve false when item with same name DOES NOT exist', async () => { + const spy = spyOn(portalModule, "searchItems").and.returnValue( + Promise.resolve({ + results: [] // empty + }) + ); + const res = await doesItemExistWithTitle("not-exists", opts, authMgr); + expect(res).toBeFalsy(); + }); + + it('should reject if error', async () => { + spyOn(portalModule, "searchItems").and.returnValue + (Promise.reject()); + + try { + await doesItemExistWithTitle("not-exists", opts, authMgr); + fail("should reject"); + } catch (err) { + expect(err).toBeDefined(); + } + }); +}); \ No newline at end of file diff --git a/packages/common/test/items/get-unique-item-title.test.ts b/packages/common/test/items/get-unique-item-title.test.ts new file mode 100644 index 00000000000..daba0b25c9a --- /dev/null +++ b/packages/common/test/items/get-unique-item-title.test.ts @@ -0,0 +1,41 @@ +import { getUniqueItemTitle } from '../../src'; +import * as doesItemExistWithTitleModule from "../../src/items/does-item-exist-with-title"; +import { IAuthenticationManager } from '@esri/arcgis-rest-request'; + +describe('getUniqueItemTitle', () => { + const opts = { + typekeywords: 'typeX' + }; + it("generates a unique name", async function() { + const initialTitle = "foobar"; + spyOn(doesItemExistWithTitleModule, "doesItemExistWithTitle").and.callFake(function( + candidate: string + ) { + return Promise.resolve( + [`${initialTitle}`, `${initialTitle} 1`].indexOf(candidate) !== -1 + ); + }); + + const uniqueTitle = await getUniqueItemTitle( + initialTitle, + opts, + {} as IAuthenticationManager + ); + + expect(uniqueTitle).toBe("foobar 2"); + }); + + it("rejects if error", async function() { + const initialTitle = "foobar"; + spyOn(doesItemExistWithTitleModule, "doesItemExistWithTitle").and.returnValue( + Promise.reject("something") + ); + + try { + await getUniqueItemTitle(initialTitle, opts, {} as IAuthenticationManager); + fail("should reject"); + } catch (err) { + expect(err).toBeDefined(); + } + }); +}); \ No newline at end of file From dfc6cbfc60691f1e5027e2a6516d51a8391e7a2d Mon Sep 17 00:00:00 2001 From: Vivian Zhang Date: Fri, 19 Jun 2020 22:51:33 -0400 Subject: [PATCH 2/2] fix tests Signed-off-by: Vivian Zhang --- .../src/items/does-item-exist-with-title.ts | 7 +--- .../items/does-item-exist-with-title.test.ts | 37 ++++++++++++++++--- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/common/src/items/does-item-exist-with-title.ts b/packages/common/src/items/does-item-exist-with-title.ts index 4caeaa095cb..45dc498a496 100644 --- a/packages/common/src/items/does-item-exist-with-title.ts +++ b/packages/common/src/items/does-item-exist-with-title.ts @@ -11,15 +11,12 @@ export function doesItemExistWithTitle ( ) { // if options have multiple properties, put them into one string separated with 'AND' let optionsQuery = Object.keys(options).map(key => { - return `${key}:${options[key]}`; + return `${key}:"${options[key]}"`; }).join(' AND '); let opts = { - q: `title: "${itemTitle}"`, + q: `title:"${itemTitle}" AND ${optionsQuery}`, authentication: authMgr }; - if (optionsQuery.length) { - opts.q = `${opts.q} AND ${optionsQuery}`; - } return searchItems(opts) .then(searchResponse => searchResponse.results.length > 0) .catch(e => { diff --git a/packages/common/test/items/does-item-exist-with-title.test.ts b/packages/common/test/items/does-item-exist-with-title.test.ts index 6f7dba0f899..c33643cd1e5 100644 --- a/packages/common/test/items/does-item-exist-with-title.test.ts +++ b/packages/common/test/items/does-item-exist-with-title.test.ts @@ -3,30 +3,57 @@ import * as portalModule from "@esri/arcgis-rest-portal"; import { IAuthenticationManager } from '@esri/arcgis-rest-request'; describe('doesItemExistWithTitle', () => { - const opts = { - typekeywords: 'typeX' + let options = { + typekeywords: "foo" }; const authMgr = {} as IAuthenticationManager; it('should resolve true when item with same name exists', async () => { + const opts = { + q: `title:"exists"`, + authentication: authMgr + }; const spy = spyOn(portalModule, "searchItems").and.returnValue( Promise.resolve({ results: [{}] }) ); - const res = await doesItemExistWithTitle("exists", opts, authMgr); + const res = await doesItemExistWithTitle("exists", options, authMgr); expect(res).toBeTruthy(); + + const expectedSearchOpts = { + q: `title:"exists" AND typekeywords:"foo"`, + authentication: authMgr + }; + expect(spy.calls.argsFor(0)[0]).toEqual( + expectedSearchOpts, + "searchItems called with correct options" + ); }); + it('should resolve false when item with same name DOES NOT exist', async () => { + const opts = { + q: `title:"not-exists"`, + authentication: authMgr + }; const spy = spyOn(portalModule, "searchItems").and.returnValue( Promise.resolve({ results: [] // empty }) ); - const res = await doesItemExistWithTitle("not-exists", opts, authMgr); + const res = await doesItemExistWithTitle("not-exists", options, authMgr); expect(res).toBeFalsy(); + + const expectedSearchOpts = { + q: `title:"not-exists" AND typekeywords:"foo"`, + authentication: authMgr + }; + expect(spy.calls.argsFor(0)[0]).toEqual( + expectedSearchOpts, + "searchItems called with correct options" + ); }); it('should reject if error', async () => { @@ -34,7 +61,7 @@ describe('doesItemExistWithTitle', () => { (Promise.reject()); try { - await doesItemExistWithTitle("not-exists", opts, authMgr); + await doesItemExistWithTitle("not-exists", options, authMgr); fail("should reject"); } catch (err) { expect(err).toBeDefined();