Skip to content

Commit

Permalink
fix(hub-sites): prevent surveys and their feature services from being…
Browse files Browse the repository at this point in the history
… shared to site teams during si

affects: @esri/hub-sites, @esri/hub-surveys

ISSUES CLOSED: [2325](https://devtopia.esri.com/dc/hub/issues/2325)
  • Loading branch information
rweber-esri committed Oct 14, 2021
1 parent 820900c commit 658ed28
Show file tree
Hide file tree
Showing 8 changed files with 19,149 additions and 32,937 deletions.
51,751 changes: 18,919 additions & 32,832 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion packages/sites/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"@esri/arcgis-rest-request": "^2.13.0 || 3",
"@esri/hub-common": "^8.2.0 || 9",
"@esri/hub-initiatives": "^8.0.0 || 9",
"@esri/hub-teams": "^8.0.0 || 9"
"@esri/hub-teams": "^8.0.0 || 9",
"@esri/hub-surveys": "^8.0.0 || 9"
},
"devDependencies": {
"@esri/arcgis-rest-auth": "^3.1.1",
Expand All @@ -28,6 +29,7 @@
"@esri/hub-initiatives": "^9.1.1",
"@esri/hub-teams": "^9.1.1",
"@esri/hub-types": "^6.10.0",
"@esri/hub-surveys": "^9.1.1",
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
Expand Down
48 changes: 48 additions & 0 deletions packages/sites/src/_get-sharing-eligible-models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { IModel, IHubRequestOptions } from "@esri/hub-common";
import { getSurveyModels } from "@esri/hub-surveys";

function _getIneligibleModelIds(
siteModel: IModel,
models: IModel[],
hubRequestOptions: IHubRequestOptions
) {
const fetchSurveyModels = (model: IModel) =>
getSurveyModels(model.item, hubRequestOptions).then(
({ form, featureService, fieldworker, stakeholder }) =>
[form, featureService, fieldworker, stakeholder].filter(Boolean)
);

const inEligibleModelCollectionPromises = models.reduce(
(acc, model) =>
model.item.type === "Form" ? [...acc, fetchSurveyModels(model)] : acc,
[Promise.resolve([siteModel])]
);

return Promise.all(inEligibleModelCollectionPromises).then(
(ineligibleModelCollections) =>
ineligibleModelCollections.reduce(
(acc, ineligibleModelCollection) => [
...acc,
...ineligibleModelCollection,
],
[]
)
);
}

export function _getSharingEligibleModels(
siteModel: IModel,
models: IModel[],
hubRequestOptions: IHubRequestOptions
) {
return _getIneligibleModelIds(siteModel, models, hubRequestOptions).then(
(ineligibleModels) =>
models.reduce(
(acc, model) =>
ineligibleModels.find(({ item: { id } }) => model.item.id === id)
? acc
: [...acc, model],
[]
)
);
}
42 changes: 25 additions & 17 deletions packages/sites/src/_share-items-to-site-groups.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IModel, IHubRequestOptions, failSafe } from "@esri/hub-common";
import { shareItemWithGroup, ISharingResponse } from "@esri/arcgis-rest-portal";
import { _getSecondPassSharingOptions } from "./_get-second-pass-sharing-options";
import { _getSharingEligibleModels } from "./_get-sharing-eligible-models";

/**
* Share all the other models to the Site's content and collaboration groups, if
Expand Down Expand Up @@ -35,26 +36,33 @@ export function shareItemsToSiteGroups(
solutionModels: IModel[],
hubRequestOptions: IHubRequestOptions
) {
const otherModels = solutionModels.filter(m => {
return m.item.id !== siteModel.item.id;
});
// Create Fail-safe version of share b/c this is not critical
const failSafeShare = failSafe(shareItemWithGroup, { success: true });

const groupsToShareTo = _getSecondPassSharingOptions(siteModel);
// share all items in the solution to the groups
return Promise.all(
otherModels.reduce((acc: Array<Promise<ISharingResponse>>, m: IModel) => {
const itemSharePromises = groupsToShareTo.map(g => {
const opts = {
id: m.item.id,
groupId: g.id,
confirmItemControl: g.confirmItemControl,
authentication: hubRequestOptions.authentication
};
return failSafeShare(opts) as Promise<ISharingResponse>;
});
return acc.concat(itemSharePromises);
}, [])
// share all items in the solution to the groups, excluding the the site, form
// and any form feature services
return _getSharingEligibleModels(
siteModel,
solutionModels,
hubRequestOptions
).then((eligibleModels) =>
Promise.all(
eligibleModels.reduce(
(acc: Array<Promise<ISharingResponse>>, m: IModel) => {
const itemSharePromises = groupsToShareTo.map((g) => {
const opts = {
id: m.item.id,
groupId: g.id,
confirmItemControl: g.confirmItemControl,
authentication: hubRequestOptions.authentication,
};
return failSafeShare(opts) as Promise<ISharingResponse>;
});
return acc.concat(itemSharePromises);
},
[]
)
)
);
}
54 changes: 54 additions & 0 deletions packages/sites/test/_get-sharing-eligible-models.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { IModel, IHubRequestOptions } from "@esri/hub-common";
import * as hubSurveys from "@esri/hub-surveys";
import { _getSharingEligibleModels } from "../src/_get-sharing-eligible-models";
import { getSurveyModels } from "@esri/hub-surveys";

describe("_getSharingEligibleModels", function () {
let models: IModel[];
let siteModel: IModel;
let hubRequestOptions: IHubRequestOptions;

beforeEach(() => {
siteModel = { item: { id: "site-id" } } as IModel;

models = [
siteModel,
{ item: { id: "123", type: "Web Map" } },
{ item: { id: "456", type: "Form" } },
{ item: { id: "789", type: "Feature Service" } },
{ item: { id: "101", type: "Feature Service" } },
{ item: { id: "121", type: "Feature Service" } },
] as IModel[];

hubRequestOptions = { authentication: {} } as IHubRequestOptions;
});

it("resolves an array of sharing eligible models", async function () {
const getSurveyModelsSpy = spyOn(
hubSurveys,
"getSurveyModels"
).and.returnValue(
Promise.resolve({
form: models[2],
featureService: models[3],
fieldworker: models[4],
stakeholder: models[5],
})
);
const results = await _getSharingEligibleModels(
siteModel,
models,
hubRequestOptions
);
expect(getSurveyModelsSpy.calls.count()).toBe(1);
expect(getSurveyModelsSpy).toHaveBeenCalledWith(
models[2].item,
hubRequestOptions
);
expect(results.length).toBe(1);
expect(results[0]).toEqual(models[1]);
});
});
90 changes: 26 additions & 64 deletions packages/sites/test/_share-items-to-site-groups.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { _shareItemsToSiteGroups, shareItemsToSiteGroups } from "../src";
import * as portalModule from "@esri/arcgis-rest-portal";
import { IModel, IHubRequestOptions } from "@esri/hub-common";
import * as sharingEligibleModelsModule from "../src/_get-sharing-eligible-models";

describe("_shareItemsToSiteGroups", () => {
it("shares items to groups", async () => {
it("shares items to groups using shareItemsToSiteGroups", async () => {
const shareSpy = spyOn(portalModule, "shareItemWithGroup").and.returnValue(
Promise.resolve({})
);
Expand All @@ -13,76 +14,37 @@ describe("_shareItemsToSiteGroups", () => {
id: "site-id",
properties: {
contentGroupId: "content-id",
collaborationGroupId: "collab-id"
}
}
collaborationGroupId: "collab-id",
},
},
} as IModel;

const hubRequestOptions = {
authentication: {},
} as IHubRequestOptions;

const toShare = [
siteModel,
{ item: { id: "foo" } },
{ item: { id: "bar" } }
{ item: { id: "bar" } },
] as IModel[];

await _shareItemsToSiteGroups(siteModel, toShare, {
authentication: {}
} as IHubRequestOptions);
const _getSharingEligibleModelsSpy = spyOn(
sharingEligibleModelsModule,
"_getSharingEligibleModels"
).and.returnValue(Promise.resolve(toShare.slice(1, 3)));

expect(shareSpy.calls.count()).toBe(
4,
"share called correct number of times"
);
expect(shareSpy).toHaveBeenCalledWith({
id: "foo",
groupId: "content-id",
confirmItemControl: false,
authentication: {}
});
expect(shareSpy).toHaveBeenCalledWith({
id: "bar",
groupId: "collab-id",
confirmItemControl: true,
authentication: {}
});
expect(shareSpy).toHaveBeenCalledWith({
id: "foo",
groupId: "content-id",
confirmItemControl: false,
authentication: {}
});
expect(shareSpy).toHaveBeenCalledWith({
id: "bar",
groupId: "collab-id",
confirmItemControl: true,
authentication: {}
});
});
await shareItemsToSiteGroups(siteModel, toShare, hubRequestOptions);

it("shares items to groups using shareItemsToSiteGroups", async () => {
const shareSpy = spyOn(portalModule, "shareItemWithGroup").and.returnValue(
Promise.resolve({})
expect(_getSharingEligibleModelsSpy.calls.count()).toBe(
1,
"calls _getSharingEligibleModelsSpy once"
);

const siteModel = {
item: {
id: "site-id",
properties: {
contentGroupId: "content-id",
collaborationGroupId: "collab-id"
}
}
} as IModel;

const toShare = [
expect(_getSharingEligibleModelsSpy).toHaveBeenCalledWith(
siteModel,
{ item: { id: "foo" } },
{ item: { id: "bar" } }
] as IModel[];

await shareItemsToSiteGroups(siteModel, toShare, {
authentication: {}
} as IHubRequestOptions);

toShare,
hubRequestOptions
);
expect(shareSpy.calls.count()).toBe(
4,
"share called correct number of times"
Expand All @@ -91,25 +53,25 @@ describe("_shareItemsToSiteGroups", () => {
id: "foo",
groupId: "content-id",
confirmItemControl: false,
authentication: {}
authentication: {},
});
expect(shareSpy).toHaveBeenCalledWith({
id: "bar",
groupId: "collab-id",
confirmItemControl: true,
authentication: {}
authentication: {},
});
expect(shareSpy).toHaveBeenCalledWith({
id: "foo",
groupId: "content-id",
confirmItemControl: false,
authentication: {}
authentication: {},
});
expect(shareSpy).toHaveBeenCalledWith({
id: "bar",
groupId: "collab-id",
confirmItemControl: true,
authentication: {}
authentication: {},
});
});
});
21 changes: 13 additions & 8 deletions packages/surveys/src/items/get-survey-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Apache-2.0 */

import { IRequestOptions } from "@esri/arcgis-rest-request";
import { getItem } from "@esri/arcgis-rest-portal";
import { getItem, IItem } from "@esri/arcgis-rest-portal";
import { IModel, IGetSurveyModelsResponse } from "@esri/hub-common";
import { getInputFeatureServiceModel } from "./get-input-feature-service-model";
import { getSourceFeatureServiceModelFromFieldworker } from "./get-source-feature-service-model-from-fieldworker";
Expand All @@ -16,18 +16,23 @@ import { isFieldworkerView } from "../utils/is-fieldworker-view";
* @returns {Promise<IGetSurveyModelsResponse>}
*/
export const getSurveyModels = (
formId: string,
formItemOrId: string | IItem,
requestOptions: IRequestOptions
): Promise<IGetSurveyModelsResponse> => {
let fieldworker: IModel;
let stakeholder: IModel;

return getItem(formId, requestOptions).then(item => {
const getForm = () =>
typeof formItemOrId === "string"
? getItem(formItemOrId, requestOptions)
: Promise.resolve(formItemOrId);

return getForm().then((form) => {
const promises: Array<Promise<IModel>> = [
// the primary input will be the fieldworker (if it exists), otherwise
// the source feature service.
getInputFeatureServiceModel(formId, requestOptions),
getStakeholderModel(formId, requestOptions)
getInputFeatureServiceModel(form.id, requestOptions),
getStakeholderModel(form.id, requestOptions),
];

return Promise.all(promises)
Expand All @@ -49,12 +54,12 @@ export const getSurveyModels = (
return featureServiceOrFieldworkerModelResult;
}
})
.then(featureService => {
.then((featureService) => {
return {
form: { item },
form: { item: form },
featureService,
fieldworker,
stakeholder
stakeholder,
};
});
});
Expand Down
Loading

0 comments on commit 658ed28

Please sign in to comment.