Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions demos/deploySolution/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,16 @@ <h3>Deploy one or more Solutions</h3>
<br/><br/>

<div class="section-title">Options</div>
<div class="labeledItem">
<label for="useExistingChk">Use Existing Items:</label>
<input type="checkbox" id="useExistingChk">
</div>

<ul style="list-style:none">
<li>
<input type="checkbox" id="useExistingChk">
<label for="useExistingChk">Use existing items</label>
</li>
<li>
<input type="checkbox" id="dontCreateSolutionItem">
<label for="dontCreateSolutionItem">Don't include Solution item in deployment</label>
</li>
</ul>
<br /><br />

<div class="section-title">Custom Params</div>
Expand Down
24 changes: 18 additions & 6 deletions demos/deploySolution/src/deploy-solution-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function deploySolutionsInFolder(
destAuthentication: common.UserSession,
progressCallback: common.ISolutionProgressCallback,
enableItemReuse: boolean,
dontCreateSolutionItem: boolean,
customParams: any
): Promise<string> {
const query = new common.SearchQueryBuilder()
Expand All @@ -53,7 +54,8 @@ export function deploySolutionsInFolder(
} as ISolutionInfoCard;
}
);
return deployBatchOfSolutions(solutionsToDeploy, solutionsToDeploy.length, srcAuthentication, destAuthentication, progressCallback, enableItemReuse, customParams);
return deployBatchOfSolutions(solutionsToDeploy, solutionsToDeploy.length,
srcAuthentication, destAuthentication, progressCallback, enableItemReuse, dontCreateSolutionItem, customParams);
} else {
return Promise.resolve("<i>No solutions found in folder</i>");
}
Expand All @@ -67,6 +69,7 @@ function deployBatchOfSolutions(
destAuthentication: common.UserSession,
progressCallback: common.ISolutionProgressCallback,
enableItemReuse: boolean,
dontCreateSolutionItem: boolean,
customParams: any
): Promise<string> {
// Deploy the first item in the list
Expand All @@ -83,6 +86,7 @@ function deployBatchOfSolutions(
destAuthentication,
progressCallback,
enableItemReuse,
dontCreateSolutionItem,
customParams
);
}
Expand All @@ -94,7 +98,7 @@ function deployBatchOfSolutions(
let remainingDeployPromise = Promise.resolve("");
if (solutionsToDeploy.length > 0) {
remainingDeployPromise = deployBatchOfSolutions(solutionsToDeploy, totalNumberOfSolutions,
srcAuthentication, destAuthentication, progressCallback, enableItemReuse, customParams);
srcAuthentication, destAuthentication, progressCallback, enableItemReuse, dontCreateSolutionItem, customParams);
}
return remainingDeployPromise;
})
Expand All @@ -110,6 +114,7 @@ export function deploySolution(
destAuthentication: common.UserSession,
progressCallback: common.ISolutionProgressCallback,
enableItemReuse: boolean,
dontCreateSolutionItem: boolean,
customParams: any
): Promise<string> {
// Deploy a solution described by the supplied id
Expand All @@ -121,7 +126,8 @@ export function deploySolution(
enableItemReuse,
templateDictionary: isJsonStr(customParams) ? {
params: JSON.parse(customParams)
} : {}
} : {},
dontCreateSolutionItem
};
const itemUrlPrefix = destAuthentication.portal.replace("/sharing/rest", "");

Expand All @@ -138,6 +144,7 @@ export function deployAndDisplaySolution(
destAuthentication: common.UserSession,
progressCallback: common.ISolutionProgressCallback,
enableItemReuse: boolean,
dontCreateSolutionItem: boolean,
customParams: any
): Promise<string> {
// Deploy a solution described by the supplied id
Expand All @@ -150,12 +157,17 @@ export function deployAndDisplaySolution(
enableItemReuse,
templateDictionary: isJsonStr(customParams) ? {
params: JSON.parse(customParams)
} : {}
} : {},
dontCreateSolutionItem
};

return deployer.deploySolution(templateSolutionId, destAuthentication, options)
.then((deployedSolution: any) => {
return getFormattedItemInfo.getFormattedItemInfo(deployedSolution, destAuthentication);
.then((createdSolutionId: string) => {
if (dontCreateSolutionItem) {
return "Deployed solution";
} else {
return getFormattedItemInfo.getFormattedItemInfo(createdSolutionId, destAuthentication);
}
});
}

Expand Down
10 changes: 8 additions & 2 deletions demos/deploySolution/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function deploySolution(
srcCreds: common.UserSession,
destCreds: common.UserSession,
useExisting: boolean,
dontCreateSolutionItem: boolean,
customParams: any
): void {
const startTime = Date.now();
Expand All @@ -87,6 +88,7 @@ function deploySolution(
destCreds,
progressFcn,
useExisting,
dontCreateSolutionItem,
customParams
);
} else if (folderId.length > 0) {
Expand All @@ -96,6 +98,7 @@ function deploySolution(
destCreds,
progressFcn,
useExisting,
dontCreateSolutionItem,
customParams
);
}
Expand Down Expand Up @@ -129,6 +132,9 @@ function go(
// Use Existing
const useExisting = htmlUtil.getHTMLChecked("useExistingChk");

// Create Solution item in destination organization
const dontCreateSolutionItem = htmlUtil.getHTMLChecked("dontCreateSolutionItem");

// Custom Params
const customParams = htmlUtil.getHTMLValue("customParams");

Expand All @@ -154,7 +160,7 @@ function go(
portal: destPortal,
clientId: htmlUtil.getHTMLValue("clientId")
});
deploySolution(solutionId, folderId, srcCreds, destCreds, useExisting, customParams);
deploySolution(solutionId, folderId, srcCreds, destCreds, useExisting, dontCreateSolutionItem, customParams);
} else {
let redirect_uri = window.location.origin + window.location.pathname;
const iLastSlash = redirect_uri.lastIndexOf("/");
Expand All @@ -169,7 +175,7 @@ function go(
// Upon a successful login, update the session with the new session.
session = newSession;
updateSessionInfo(session);
deploySolution(solutionId, folderId, srcCreds, session, useExisting, customParams);
deploySolution(solutionId, folderId, srcCreds, session, useExisting, dontCreateSolutionItem, customParams);
}).catch(function (error) {
console.log(error);
});
Expand Down
18 changes: 12 additions & 6 deletions packages/common/src/deleteHelpers/deleteSolutionContents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export function deleteSolutionContents(
if (solutionSummary.items.length > 0) {
// Save a copy of the Solution item ids for the deleteSolutionFolder call because removeItems
// destroys the solutionSummary.items list
solutionIds = solutionSummary.items.map((item) => item.id).concat([solutionItemId]);
if (solutionItemId) {
solutionIds = solutionSummary.items.map((item) => item.id).concat([solutionItemId]);
}

const hubSiteItemIds: string[] = solutionSummary.items
.filter((item: any) => item.type === "Hub Site Application")
Expand Down Expand Up @@ -113,12 +115,16 @@ export function deleteSolutionContents(
});
})
.then(() => {
// If there were no failed deletes, it's OK to delete Solution item
if (solutionFailureSummary.items.length === 0) {
return deleteSolutionItem.deleteSolutionItem(solutionItemId, authentication);
if (solutionItemId) {
// If there were no failed deletes, it's OK to delete Solution item
if (solutionFailureSummary.items.length === 0) {
return deleteSolutionItem.deleteSolutionItem(solutionItemId, authentication);
} else {
// Not all items were deleted, so don't delete solution
return Promise.resolve({ success: false, itemId: solutionItemId });
}
} else {
// Not all items were deleted, so don't delete solution
return Promise.resolve({ success: false, itemId: solutionItemId });
return Promise.resolve({ success: true, itemId: "" });
}
})
.then((solutionItemDeleteStatus: IStatusResponse) => {
Expand Down
9 changes: 5 additions & 4 deletions packages/common/src/featureServiceHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
deleteProp,
deleteProps,
fail,
generateGUID,
getProp,
setCreateProp,
setProp,
Expand Down Expand Up @@ -449,13 +450,13 @@ export function getLayerSettings(layerInfos: any, url: string, itemId: string, e
* Set the names and titles for all feature services.
*
* This function will ensure that we have unique feature service names.
* The feature service name will have the solution item id appended.
* The feature service name will have a generated GUID appended.
*
* @param templates A collection of AGO item templates.
* @param solutionItemId The item id for the deployed solution item.
* @returns An updated collection of AGO templates with unique feature service names.
*/
export function setNamesAndTitles(templates: IItemTemplate[], solutionItemId: string): IItemTemplate[] {
export function setNamesAndTitles(templates: IItemTemplate[]): IItemTemplate[] {
const guid: string = generateGUID();
const names: string[] = [];
return templates.map((t) => {
/* istanbul ignore else */
Expand All @@ -473,7 +474,7 @@ export function setNamesAndTitles(templates: IItemTemplate[], solutionItemId: st

// The name length limit is 98
// Limit the baseName to 50 characters before the _<guid>
const name: string = baseName.substring(0, 50) + "_" + solutionItemId;
const name: string = baseName.substring(0, 50) + "_" + guid;

// If the name + GUID already exists then append "_occurrenceCount"
t.item.name = names.indexOf(name) === -1 ? name : `${name}_${names.filter((n) => n === name).length}`;
Expand Down
31 changes: 31 additions & 0 deletions packages/common/src/generalHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,37 @@ export function generateEmptyCreationResponse(itemType: string, id = ""): ICreat
};
}

/**
* Generates a version 4 2-bit variant GUID.
*
* @returns A GUID
* @see {@link https://en.wikipedia.org/wiki/Universally_unique_identifier}, section "Version 4 (random)"
*/
export function generateGUID(): string {
/** @license
* Copyright 2013 Steve Fenton
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*/
try {
return crypto.randomUUID().replace(/-/g, "");
} catch {
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
// eslint-disable-next-line no-bitwise
const r = (Math.random() * 16) | 0;
// eslint-disable-next-line no-bitwise
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
}

/**
* Returns a regular expression matching a global search for a 32-character AGO-style id.
*
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,11 @@ export interface IDeploySolutionOptions {
* Version of storage read from Solution item. DO NOT USE--it is overwritten by function deploySolutionFromTemplate
*/
storageVersion?: number;

/**
* Determines if the solution item should be created during deployment; default: false
*/
dontCreateSolutionItem?: boolean;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions packages/common/test/arcgisRestJS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,14 @@ describe("Module arcgisRestJS", () => {
await arcgisRestJS.unprotectGroup(requestOptions);
expect(unprotectGroupSpy.called);
});

it("tests binding function unprotectItem", async () => {
const requestOptions: arcgisRestJS.IUserItemOptions = {
id: "0",
authentication: MOCK_USER_SESSION,
};
const unprotectItemSpy = sinon.stub(arcgisRestPortal, "unprotectItem").resolves();
await arcgisRestJS.unprotectItem(requestOptions);
expect(unprotectItemSpy.called);
});
});
27 changes: 18 additions & 9 deletions packages/common/test/featureServiceHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import {
IPopupInfos,
} from "../src/featureServiceHelpers";

import * as generalHelpers from "../../common/src/generalHelpers";
import * as restHelpers from "../../common/src/restHelpers";

import * as arcGISRestJS from "../src/arcgisRestJS";
Expand Down Expand Up @@ -1683,13 +1684,15 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service
t.item.title = "TheName";
const _templates: IItemTemplate[] = [t];

spyOn(generalHelpers, "generateGUID").and.returnValue("212dbc19b03943008fdfaf8d6adca00e");

const expectedTemplate: IItemTemplate = templates.getItemTemplateSkeleton();
expectedTemplate.item.type = "Feature Service";
expectedTemplate.item.name = `TheName_${itemId}`;
expectedTemplate.item.name = `TheName_212dbc19b03943008fdfaf8d6adca00e`;
expectedTemplate.item.title = "TheName";
const expected: IItemTemplate[] = [expectedTemplate];

const actual: IItemTemplate[] = setNamesAndTitles(_templates, itemId);
const actual: IItemTemplate[] = setNamesAndTitles(_templates);
expect(actual).toEqual(expected);
});

Expand All @@ -1704,17 +1707,19 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service
t2.item.title = undefined;
const _templates: IItemTemplate[] = [t, t2];

spyOn(generalHelpers, "generateGUID").and.returnValue("212dbc19b03943008fdfaf8d6adca00e");

const expectedTemplate: IItemTemplate = templates.getItemTemplateSkeleton();
expectedTemplate.item.type = "Feature Service";
expectedTemplate.item.name = `TheName_${itemId}`;
expectedTemplate.item.name = `TheName_212dbc19b03943008fdfaf8d6adca00e`;
expectedTemplate.item.title = "TheName_99ac87b220fd45038fc92ba10843886d";
const expectedTemplate2: IItemTemplate = templates.getItemTemplateSkeleton();
expectedTemplate2.item.type = "Feature Service";
expectedTemplate2.item.name = `TheName_${itemId}_1`;
expectedTemplate2.item.name = `TheName_212dbc19b03943008fdfaf8d6adca00e_1`;
expectedTemplate2.item.title = "TheName_88ac87b220fd45038fc92ba10843886d";
const expected: IItemTemplate[] = [expectedTemplate, expectedTemplate2];

const actual: IItemTemplate[] = setNamesAndTitles(_templates, itemId);
const actual: IItemTemplate[] = setNamesAndTitles(_templates);
expect(actual).toEqual(expected);
});

Expand All @@ -1725,13 +1730,15 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service
t.item.title = "TheName";
const _templates: IItemTemplate[] = [t];

spyOn(generalHelpers, "generateGUID").and.returnValue("212dbc19b03943008fdfaf8d6adca00e");

const expectedTemplate: IItemTemplate = templates.getItemTemplateSkeleton();
expectedTemplate.item.type = "Feature Service";
expectedTemplate.item.name = `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_${itemId}`;
expectedTemplate.item.name = `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_212dbc19b03943008fdfaf8d6adca00e`;
expectedTemplate.item.title = "TheName";
const expected: IItemTemplate[] = [expectedTemplate];

const actual: IItemTemplate[] = setNamesAndTitles(_templates, itemId);
const actual: IItemTemplate[] = setNamesAndTitles(_templates);
expect(actual).toEqual(expected);
});

Expand All @@ -1742,13 +1749,15 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service
t.item.title = "TheName";
const _templates: IItemTemplate[] = [t];

spyOn(generalHelpers, "generateGUID").and.returnValue("212dbc19b03943008fdfaf8d6adca00e");

const expectedTemplate: IItemTemplate = templates.getItemTemplateSkeleton();
expectedTemplate.item.type = "Feature Service";
expectedTemplate.item.name = `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_${itemId}`;
expectedTemplate.item.name = `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_212dbc19b03943008fdfaf8d6adca00e`;
expectedTemplate.item.title = "TheName";
const expected: IItemTemplate[] = [expectedTemplate];

const actual: IItemTemplate[] = setNamesAndTitles(_templates, itemId);
const actual: IItemTemplate[] = setNamesAndTitles(_templates);
expect(actual).toEqual(expected);
});
});
Expand Down
Loading