diff --git a/packages/common/src/featureServiceHelpers.ts b/packages/common/src/featureServiceHelpers.ts index 3c57e34d1..761d78572 100644 --- a/packages/common/src/featureServiceHelpers.ts +++ b/packages/common/src/featureServiceHelpers.ts @@ -219,7 +219,7 @@ export function deleteViewProps(layer: any) { * @param isView When true the current layer is a view and does not need to cache subtype details * @returns An updated instance of the fieldInfos */ -export function cacheFieldInfos(layer: any, fieldInfos: any, isView: boolean): any { +export function cacheFieldInfos(layer: any, fieldInfos: any, isView: boolean, isPortal: boolean): any { // cache the source fields as they are in the original source if (layer && layer.fields) { fieldInfos[layer.id] = { @@ -228,7 +228,7 @@ export function cacheFieldInfos(layer: any, fieldInfos: any, isView: boolean): a id: layer.id, }; /* istanbul ignore else */ - if (!isView) { + if (!isView && isPortal) { fieldInfos[layer.id].subtypes = layer.subtypes; fieldInfos[layer.id].subtypeField = layer.subtypeField; fieldInfos[layer.id].defaultSubtypeCode = layer.defaultSubtypeCode; @@ -252,7 +252,7 @@ export function cacheFieldInfos(layer: any, fieldInfos: any, isView: boolean): a }; /* istanbul ignore else */ - if (!isView) { + if (!isView && isPortal) { props["subtypes"] = true; props["subtypeField"] = true; props["defaultSubtypeCode"] = true; @@ -281,6 +281,31 @@ export function cacheContingentValues(id: string, fieldInfos: any, itemTemplate: return fieldInfos; } +/** + * Cache the stored contingent values so we can add them in subsequent addToDef calls + * + * @param layer The current layer to check indexes on + * @param fieldInfos The object that stores the cached field infos + * @returns An updated instance of the fieldInfos + */ +export function cacheIndexes(layer: any, fieldInfos: any): any { + /* istanbul ignore else */ + if (Array.isArray(layer.indexes)) { + const oidField = layer.objectIdField; + const guidField = layer.globalIdField; + fieldInfos[layer.id].indexes = layer.indexes.filter((i) => { + if ((i.isUnique && i.fields !== oidField && i.fields !== guidField) || i.indexType === "FullText") { + if (i.name) { + i.name = i.name.replaceAll(" ", ""); + } + return i; + } + }); + delete layer.indexes; + } + return fieldInfos; +} + /** * Helper function to cache a single property into the fieldInfos object * This property will be removed from the layer instance. @@ -825,7 +850,7 @@ export function addFeatureServiceLayersAndTables( updates .reduce((prev, update) => { return prev.then(() => { - return getRequest(update); + return getRequest(update, false, false, templateDictionary.isPortal); }); }, Promise.resolve(null)) .then( @@ -895,11 +920,17 @@ export function addFeatureServiceDefinition( let item = toAdd.item; const originalId = item.id; const isView = itemTemplate.properties.service.isView; - fieldInfos = cacheFieldInfos(item, fieldInfos, isView); + const isPortal = templateDictionary.isPortal; + fieldInfos = cacheFieldInfos(item, fieldInfos, isView, isPortal); // cache the values to be added in seperate addToDef calls fieldInfos = cacheContingentValues(item.id, fieldInfos, itemTemplate); + // cache specific field indexes when deploying to ArcGIS Enterprise portal + if (isPortal) { + fieldInfos = cacheIndexes(item, fieldInfos); + } + /* istanbul ignore else */ if (item.isView) { deleteViewProps(item); @@ -929,7 +960,7 @@ export function addFeatureServiceDefinition( } } /* istanbul ignore else */ - if (templateDictionary.isPortal) { + if (isPortal) { item = _updateForPortal(item, itemTemplate, templateDictionary); } diff --git a/packages/common/src/restHelpers.ts b/packages/common/src/restHelpers.ts index 75d158a5b..76558a127 100644 --- a/packages/common/src/restHelpers.ts +++ b/packages/common/src/restHelpers.ts @@ -956,7 +956,7 @@ export function getLayerUpdates(args: IPostProcessArgs, isPortal: boolean): IUpd }); /* istanbul ignore else */ - if (subtypeUpdates.length > 0) { + if (subtypeUpdates.length > 0 && isPortal) { subtypeUpdates.forEach((subtypeUpdate) => { updates.push( _getUpdate(adminUrl + subtypeUpdate.id, null, { subtypeField: subtypeUpdate.subtypeField }, args, "update"), @@ -1013,6 +1013,34 @@ export function getLayerUpdates(args: IPostProcessArgs, isPortal: boolean): IUpd }); } } + + // issue: https://devtopia.esri.com/WebGIS/solution-deployment-apps/issues/273 + // For portal only...add specific indexes with existing supplementary addToDefinition call if it exists + // or with a new addToDefinition call if one doesn't already exist + if (isPortal) { + Object.keys(args.objects).forEach((id) => { + const obj: any = Object.assign({}, args.objects[id]); + let update; + if (Array.isArray(obj.indexes) && obj.indexes.length > 0) { + const layerHasExistingAdd = updates.some((u) => { + if (u.url.indexOf(`${id}/addToDefinition`) > -1) { + update = u; + return true; + } + }); + if (layerHasExistingAdd) { + // append to existing addToDef + update.params.addToDefinition = { + ...update.params.addToDefinition, + indexes: obj.indexes, + }; + } else { + // create new addToDef + updates.push(_getUpdate(checkUrlPathTermination(adminUrl) + id, null, { indexes: obj.indexes }, args, "add")); + } + } + }); + } return updates.length === 1 ? [] : updates; } @@ -1101,7 +1129,12 @@ export function _sortRelationships(layers: any[], tables: any[], relUpdates: any * @private */ /* istanbul ignore else */ -export function getRequest(update: IUpdate, skipRetry: boolean = false, useAsync: boolean = false): Promise { +export function getRequest( + update: IUpdate, + skipRetry: boolean = false, + useAsync: boolean = false, + isPortal: boolean = false, +): Promise { return new Promise((resolve, reject) => { const options: IRequestOptions = { params: update.params, @@ -1124,7 +1157,7 @@ export function getRequest(update: IUpdate, skipRetry: boolean = false, useAsync }, (e: any) => { if (!skipRetry) { - getRequest(update, true, true).then( + getRequest(update, true, true, isPortal).then( () => resolve(), (e) => reject(e), ); diff --git a/packages/common/test/featureServiceHelpers.test.ts b/packages/common/test/featureServiceHelpers.test.ts index 0bac04b64..3a568f973 100644 --- a/packages/common/test/featureServiceHelpers.test.ts +++ b/packages/common/test/featureServiceHelpers.test.ts @@ -19,6 +19,7 @@ */ import { + cacheIndexes, getFeatureServiceRelatedRecords, templatize, deleteViewProps, @@ -556,7 +557,7 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service it("should not fail with undefined", () => { let fieldInfos: any = {}; const layer: any = undefined; - fieldInfos = cacheFieldInfos(layer, fieldInfos, true); + fieldInfos = cacheFieldInfos(layer, fieldInfos, true, false); expect(layer).toBeUndefined(); expect(fieldInfos).toEqual({}); }); @@ -564,7 +565,7 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service it("should not fail without key properties on the layer", () => { let fieldInfos: any = {}; const layer: any = {}; - fieldInfos = cacheFieldInfos(layer, fieldInfos, false); + fieldInfos = cacheFieldInfos(layer, fieldInfos, false, false); expect(layer).toEqual({}); expect(fieldInfos).toEqual({}); }); @@ -676,12 +677,56 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service }, }; - fieldInfos = cacheFieldInfos(layer, fieldInfos, false); + fieldInfos = cacheFieldInfos(layer, fieldInfos, false, true); expect(layer).toEqual(expectedLayer); expect(fieldInfos).toEqual(expectedFieldInfos); }); }); + describe("cacheIndexes", () => { + it("should cache specific indexes and remove them from the layer", () => { + const layer = { + id: "0", + objectIdField: "objectid", + globalIdField: "globalid", + indexes: [ + { + isUnique: true, + fields: "B", + indexType: "", + name: "B_Unique", + }, + { + isUnique: true, + fields: "objectid", + indexType: "", + name: "C_objectid", + }, + { + isUnique: false, + fields: "A", + indexType: "FullText", + name: "A _ FullText", + }, + ], + } as any; + + const id = "0"; + let fieldInfos: any = {}; + fieldInfos[id] = {}; + fieldInfos = cacheIndexes(layer, fieldInfos); + + const expectedLayer = { + id: "0", + objectIdField: "objectid", + globalIdField: "globalid", + }; + expect(layer).toEqual(expectedLayer); + expect(fieldInfos["0"].indexes.length).toEqual(2); + expect(fieldInfos["0"].indexes[1].name).toEqual("A_FullText"); + }); + }); + describe("cacheContingentValues", () => { it("should get contingent values from feature service properties", () => { const id = "0"; @@ -3607,7 +3652,7 @@ describe("Module `featureServiceHelpers`: utility functions for feature-service const layer1 = mockItems.getAGOLLayerOrTable(1, "ROW Permit Comment", "Table", [ mockItems.createAGOLRelationship(0, 1, "esriRelRoleDestination"), ]); - const fieldInfos = cacheFieldInfos(layer1, cacheFieldInfos(layer0, {}, false), false); + const fieldInfos = cacheFieldInfos(layer1, cacheFieldInfos(layer0, {}, false, false), false, false); Object.keys(fieldInfos).forEach((k) => { fieldInfos[k].sourceFields[1].visible = false; diff --git a/packages/common/test/restHelpers.test.ts b/packages/common/test/restHelpers.test.ts index 2ce47baf8..36a5ea2dd 100644 --- a/packages/common/test/restHelpers.test.ts +++ b/packages/common/test/restHelpers.test.ts @@ -1930,6 +1930,13 @@ describe("Module `restHelpers`: common REST utility functions shared across pack subtypeField: "SubtypeField", defaultSubtypeCode: "0", subtypes: [{ a: "A" }], + indexes: [{ name: "index" }], + }, + 1: { + b: "b", + type: "B", + id: 1, + indexes: [{ name: "index2" }], }, }; @@ -1940,7 +1947,7 @@ describe("Module `restHelpers`: common REST utility functions shared across pack authentication: MOCK_USER_SESSION, }; - const updates: any[] = restHelpers.getLayerUpdates(args, false); + const updates: any[] = restHelpers.getLayerUpdates(args, true); const _object: any = Object.assign({}, objects[0]); delete _object.type; @@ -1995,6 +2002,7 @@ describe("Module `restHelpers`: common REST utility functions shared across pack params: { addToDefinition: { subtypes: [{ a: "A" }], + indexes: [{ name: "index" }] }, }, args, @@ -2029,6 +2037,15 @@ describe("Module `restHelpers`: common REST utility functions shared across pack }, args, }, + { + url: adminUrl + "1/addToDefinition", + params: { + addToDefinition: { + indexes: [{ name: "index2" }] + }, + }, + args, + }, ]; expect(updates).toEqual(expected); });