Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functions for adding and updating text resources in an AGOL item #1509

Merged
merged 5 commits into from
Sep 27, 2024
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
95 changes: 95 additions & 0 deletions packages/common/src/resources/add-resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/** @license
* Copyright 2018 Esri
*
* 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.
*/

import { IItemResourceOptions, ArcGISAuthError, addItemResource, UserSession } from "../arcgisRestJS";

/**
* Adds a blob resource.
*
* @param blob Blob containing the resource to add
* @param itemId Id of the item to add the resource to
* @param folder A prefix string added to the filename in the storage; use null or undefined for no folder
* @param filename File name used to rename an existing file resource uploaded.
* File name must have the file resource extension.
* @param authentication Credentials for the request
*/
export function addBlobResource(
blob: any,
itemId: string,
folder: string,
filename: string,
authentication: UserSession,
): Promise<any> {
// Check that the filename has an extension because it is required by the addResources call
if (filename && filename.indexOf(".") < 0) {
return new Promise((resolve, reject) => {
reject(new ArcGISAuthError("Filename must have an extension indicating its type"));
});
}

const requestOptions: IItemResourceOptions = {
id: itemId,
resource: blob,
name: filename,
authentication: authentication,
params: {},
};
if (folder) {
requestOptions.params = {
resourcesPrefix: folder,
};
}
return addItemResource(requestOptions);
}

/**
* Adds a text resource.
*
* @param content Text to add as a resource
* @param itemId Id of the item to add the resource to
* @param folder A prefix string added to the filename in the storage; use null or undefined for no folder
* @param filename File name used to rename an existing file resource uploaded, or to be used together with
* text as file name for it. File name must have the file resource extension.
* @param authentication Credentials for the request
*/
export function addTextResource(
content: string,
itemId: string,
folder: string,
filename: string,
authentication: UserSession,
): Promise<any> {
// Check that the filename has an extension because it is required by the addResources call
if (filename && filename.indexOf(".") < 0) {
return new Promise((resolve, reject) => {
reject(new ArcGISAuthError("Filename must have an extension indicating its type"));
});
}

const requestOptions: IItemResourceOptions = {
id: itemId,
content,
name: filename,
authentication: authentication,
params: {},
};
if (folder) {
requestOptions.params = {
resourcesPrefix: folder,
};
}
return addItemResource(requestOptions);
}
3 changes: 2 additions & 1 deletion packages/common/src/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

export * from "./add-resource";
export * from "./addMetadataFromBlob";
export * from "./add-resource-from-blob";
export * from "./convert-item-resource-to-storage-resource";
export * from "./convert-storage-resource-to-item-resource";
export * from "./copyAssociatedFiles";
Expand All @@ -28,3 +28,4 @@ export * from "./get-blob";
export * from "./getItemResourcesFilesFromPaths";
export * from "./getItemResourcesPaths";
export * from "./solution-resource";
export * from "./update-resource";
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
* limitations under the License.
*/

import { ArcGISAuthError, addItemResource, UserSession } from "../arcgisRestJS";
import { IItemResourceOptions, ArcGISAuthError, updateItemResource, UserSession } from "../arcgisRestJS";

/**
* Add a resource from a blob
* Adds a text resource.
*
* @param blob
* @param itemId
* @param folder
* @param filename
* @param authentication
* @param content Text to add as a resource
* @param itemId Id of the item to add the resource to
* @param folder A prefix string added to the filename in the storage; use null or undefined for no folder
* @param filename File name used to rename an existing file resource uploaded, or to be used together with
* text as file name for it. File name must have the file resource extension.
* @param authentication Credentials for the request
*/
export function addResourceFromBlob(
blob: any,
export function updateTextResource(
content: string,
itemId: string,
folder: string,
filename: string,
Expand All @@ -38,17 +40,17 @@ export function addResourceFromBlob(
});
}

const addRsrcOptions = {
const requestOptions: IItemResourceOptions = {
id: itemId,
resource: blob,
content,
name: filename,
authentication: authentication,
params: {},
};
if (folder) {
addRsrcOptions.params = {
requestOptions.params = {
resourcesPrefix: folder,
};
}
return addItemResource(addRsrcOptions);
return updateItemResource(requestOptions);
}
151 changes: 140 additions & 11 deletions packages/common/test/resourceHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import * as resourceHelpers from "../src/resourceHelpers";

import * as utils from "./mocks/utils";
const fetchMock = require("fetch-mock");
import * as addResourceFromBlobModule from "../src/resources/add-resource-from-blob";
import * as addResourceModule from "../src/resources/add-resource";
import * as updateResourceModule from "../src/resources/update-resource";

// ------------------------------------------------------------------------------------------------------------------ //

Expand All @@ -51,7 +52,7 @@ describe("Module `resourceHelpers`: common functions involving the management of
fetchMock.restore();
});

describe("addResourceFromBlob", () => {
describe("addBlobResource", () => {
it("has filename without folder", async () => {
const blob = utils.getSampleMetadataAsBlob();
const itemId = "itm1234567890";
Expand All @@ -62,8 +63,135 @@ describe("Module `resourceHelpers`: common functions involving the management of

fetchMock.post(updateUrl, expected);

const response: any = await addResourceFromBlobModule.addResourceFromBlob(
blob,
const response: any = await addResourceModule.addBlobResource(blob, itemId, folder, filename, MOCK_USER_SESSION);
expect(response).toEqual(expected);
const options: any = fetchMock.lastOptions(updateUrl);
const fetchBody = options.body;
expect(typeof fetchBody).toEqual("object");
const form = fetchBody as FormData;
expect(form.get("fileName")).toEqual(filename);
});

it("has a filename without an extension", async () => {
const blob = utils.getSampleMetadataAsBlob();
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename";
const expected = new ArcGISAuthError("Filename must have an extension indicating its type");

return addResourceModule.addBlobResource(blob, itemId, folder, filename, MOCK_USER_SESSION).then(
() => fail(),
(response: any) => {
expect(response).toEqual(expected);
return Promise.resolve();
},
);
});

it("has filename with folder", async () => {
const blob = utils.getSampleMetadataAsBlob();
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename.xml";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/addResources";
const expected = { success: true, id: itemId };

fetchMock.post(updateUrl, expected);

const response: any = await addResourceModule.addBlobResource(blob, itemId, folder, filename, MOCK_USER_SESSION);
expect(response).toEqual(expected);
const options: any = fetchMock.lastOptions(updateUrl);
const fetchBody = options.body;
expect(typeof fetchBody).toEqual("object");
const form = fetchBody as FormData;
expect(form.get("resourcesPrefix")).toEqual(folder);
expect(form.get("fileName")).toEqual(filename);
});
});

describe("addTextResource", () => {
it("has filename without folder", async () => {
const content = "abcdefghijklmnopqrstuvwxyz";
const itemId = "itm1234567890";
const folder = "";
const filename = "aFilename.text";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/addResources";
const expected = { success: true, id: itemId };

fetchMock.post(updateUrl, expected);

const response: any = await addResourceModule.addTextResource(
content,
itemId,
folder,
filename,
MOCK_USER_SESSION,
);
expect(response).toEqual(expected);
const options: any = fetchMock.lastOptions(updateUrl);
const fetchBody = options.body;
expect(typeof fetchBody).toEqual("string");
expect(fetchBody).toEqual(
"f=json&fileName=aFilename.text&text=abcdefghijklmnopqrstuvwxyz&access=inherit&token=fake-token",
);
});

it("has a filename without an extension", async () => {
const content = "";
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename";
const expected = new ArcGISAuthError("Filename must have an extension indicating its type");

return addResourceModule.addTextResource(content, itemId, folder, filename, MOCK_USER_SESSION).then(
() => fail(),
(response: any) => {
expect(response).toEqual(expected);
return Promise.resolve();
},
);
});

it("has filename with folder", async () => {
const content = JSON.stringify({ a: 1, b: 2, c: 3 });
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename.json";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/addResources";
const expected = { success: true, id: itemId };

fetchMock.post(updateUrl, expected);

const response: any = await addResourceModule.addTextResource(
content,
itemId,
folder,
filename,
MOCK_USER_SESSION,
);
expect(response).toEqual(expected);
const options: any = fetchMock.lastOptions(updateUrl);
const fetchBody = options.body;
expect(typeof fetchBody).toEqual("string");
expect(fetchBody).toEqual(
"f=json&fileName=aFilename.json&resourcesPrefix=aFolder&text=%7B%22a%22%3A1%2C%22b%22%3A2%2C%22c%22%3A3%7D&access=inherit&token=fake-token",
);
});
});

describe("updateResourceModule", () => {
it("has filename without folder", async () => {
const content = "abcdefghijklmnopqrstuvwxyz";
const itemId = "itm1234567890";
const folder = "";
const filename = "aFilename.text";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/updateResources";
const expected = { success: true, id: itemId };

fetchMock.post(updateUrl, expected);

const response: any = await updateResourceModule.updateTextResource(
content,
itemId,
folder,
filename,
Expand All @@ -74,17 +202,18 @@ describe("Module `resourceHelpers`: common functions involving the management of
const fetchBody = options.body;
expect(typeof fetchBody).toEqual("object");
const form = fetchBody as FormData;
expect(form.get("resourcesPrefix")).toBeNull();
expect(form.get("fileName")).toEqual(filename);
});

it("has a filename without an extension", async () => {
const blob = utils.getSampleMetadataAsBlob();
const content = "";
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename";
const expected = new ArcGISAuthError("Filename must have an extension indicating its type");

return addResourceFromBlobModule.addResourceFromBlob(blob, itemId, folder, filename, MOCK_USER_SESSION).then(
return updateResourceModule.updateTextResource(content, itemId, folder, filename, MOCK_USER_SESSION).then(
() => fail(),
(response: any) => {
expect(response).toEqual(expected);
Expand All @@ -94,17 +223,17 @@ describe("Module `resourceHelpers`: common functions involving the management of
});

it("has filename with folder", async () => {
const blob = utils.getSampleMetadataAsBlob();
const content = JSON.stringify({ a: 1, b: 2, c: 3 });
const itemId = "itm1234567890";
const folder = "aFolder";
const filename = "aFilename.xml";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/addResources";
const filename = "aFilename.json";
const updateUrl = utils.PORTAL_SUBSET.restUrl + "/content/users/casey/items/itm1234567890/updateResources";
const expected = { success: true, id: itemId };

fetchMock.post(updateUrl, expected);

const response: any = await addResourceFromBlobModule.addResourceFromBlob(
blob,
const response: any = await updateResourceModule.updateTextResource(
content,
itemId,
folder,
filename,
Expand Down
2 changes: 0 additions & 2 deletions packages/common/test/restHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3181,9 +3181,7 @@ describe("Module `restHelpers`: common REST utility functions shared across pack
const grp = templates.getGroupTemplatePart().item;
const additionalParams = { extra: "value" };

//const updateStub = sinon.stub(arcGISRestJS, "restUpdateGroup").resolves(utils.getSuccessResponse());
const updateStub = sinon.stub(arcGISRestJS, "restUpdateGroup").callsFake(() => {
console.log("fake updateStub");
return Promise.resolve(utils.getSuccessResponse());
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function createItemFromTemplate(
);

// Send the created qc.project.json file to the item
customProcDef = common.addResourceFromBlob(
customProcDef = common.addBlobResource(
qcProjectFile,
newItemTemplate.itemId,
"",
Expand Down
Loading