@esri/solution-common
Version:
Provides general helper functions for @esri/solution.js.
382 lines • 18.9 kB
JavaScript
;
/** @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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateItemResourceFile = exports.removeItemResourceFile = exports.getThumbnailFromStorageItem = exports.isSupportedFileType = exports.generateThumbnailStorageFilename = exports.generateStorageFilePaths = exports.generateSourceThumbnailUrl = exports.generateSourceResourceUrl = exports.generateSourceMetadataUrl = exports.generateSourceThumbnailPath = exports.generateSourceFilePaths = exports.generateMetadataStorageFilename = exports.copyFilesToStorageItem = exports.copyFilesFromStorageItem = exports.convertBlobToSupportableResource = exports.addThumbnailFromBlob = void 0;
/**
* Provides common functions involving the management of item and group resources.
*
* @module resourceHelpers
*
* How it works
*
* An item may have resources that are listed as a set of filenames and/or folder/filename combinations. It may have
* a thumbnail, listed in its item info as a filename or folder/filename combination. It may have metadata, which is
* not listed, but has a distinct URL. A group may have a thumbnail, but not the others.
*
* For storing these files in a common storage item, a new folder and filename combination is created for each. The
* filename is kept as-is. The folder consists of the source item's id and an optional suffix. For thumbnails, the
* suffix is "_info_thumbnail"; for metadata, the suffix is "_info_metadata"; for resources, the suffix is "_" plus
* the resource's folder's name; if the resource doesn't have a folder, there is no suffix.
*
* Note that "thumbnail" is included in an item's thumbnail property like a folder (e.g., "thumbnail/thumbnail.png"),
* and the item's thumbnail is accessed using a path such as "%lt;itemId>/info/thumbnail/thumbnail.png". Groups,
* on the other hand, have a property with a simple name (e.g., "thumbnail.png") and it is accessed using a path
* such as "%lt;groupId>/info/thumbnail.png".
*
* For copying these files from the common storage item to another item, one converts the unique names back into the
* original names (or the special cases for thumbnails and metadata).
*
* Routines are provided to
* 1. create full URLs to resources, thumbnails, and metadata.
* 2. create a folder and filename combination that uniquely identifies these files for
* storing them in a single, shared storage item
* 3. copy a set of resources, thumbnails, and metadata for an item to a storage item
* 4. copy a file by URL to an item using specified folder and filename
* 5. undo the unique folder and filename into the original folder and filename
*/
const interfaces_1 = require("./interfaces");
const arcgisRestJS_1 = require("./arcgisRestJS");
const generalHelpers_1 = require("./generalHelpers");
const convert_item_resource_to_storage_resource_1 = require("./resources/convert-item-resource-to-storage-resource");
const convert_storage_resource_to_item_resource_1 = require("./resources/convert-storage-resource-to-item-resource");
const restHelpersGet_1 = require("./restHelpersGet");
const copyAssociatedFiles_1 = require("./resources/copyAssociatedFiles");
// ------------------------------------------------------------------------------------------------------------------ //
function addThumbnailFromBlob(blob, itemId, authentication, isGroup = false) {
const updateOptions = {
params: {
// Pass image in directly because item object is serialized, which discards a blob
thumbnail: blob,
},
authentication: authentication,
};
updateOptions[isGroup ? "group" : "item"] = {
id: itemId,
};
return isGroup ? (0, arcgisRestJS_1.restUpdateGroup)(updateOptions) : (0, arcgisRestJS_1.restUpdateItem)(updateOptions);
}
exports.addThumbnailFromBlob = addThumbnailFromBlob;
function convertBlobToSupportableResource(blob, filename = "") {
const originalFilename = blob.name || filename;
let filenameToUse = originalFilename;
if (filenameToUse && !isSupportedFileType(filenameToUse)) {
filenameToUse = filenameToUse + ".zip";
}
return {
blob: new File([blob], filenameToUse, { type: blob.type }),
filename: originalFilename,
mimeType: blob.type,
};
}
exports.convertBlobToSupportableResource = convertBlobToSupportableResource;
/**
* Copies the files described by a list of full URLs and folder/filename combinations for
* the resources and metadata of an item or group to an item.
*
* @param storageAuthentication Credentials for the request to the storage
* @param filePaths List of item files' URLs and folder/filenames for storing the files
* @param sourceItemId Id of item supplying resource/metadata
* @param destinationFolderId Id of folder
* @param destinationItemId Id of item to receive copy of resource/metadata
* @param destinationAuthentication Credentials for the request to the destination
* @param template Description of item that will receive files
* @param templateDictionary Hash of facts: org URL, adlib replacements, deferreds for dependencies
* @returns A promise which resolves to a boolean indicating if the copies were successful
*/
function copyFilesFromStorageItem(storageAuthentication, filePaths, sourceItemId, destinationFolderId, destinationItemId, destinationAuthentication, template = {}, templateDictionary = {}) {
// TODO: This is only used in deployer, so move there
// changed to allow the template to be passed in
// because Hub templates need to swap out the templateId
// in the resource filename
const mimeTypes = template.properties || null;
// remove the template.itemId from the fileName in the filePaths
/* istanbul ignore else */
if (template.itemId) {
filePaths = filePaths.map((fp) => {
/* istanbul ignore else */
if (fp.filename.indexOf(template.itemId) === 0 && fp.folder === "") {
fp.filename = fp.filename.replace(`${template.itemId}-`, "");
}
return fp;
});
}
return new Promise((resolve, reject) => {
const fileInfos = filePaths.map((path) => {
return {
folder: path.type === interfaces_1.EFileType.Data ? destinationFolderId : path.folder,
filename: path.filename,
type: path.type,
mimeType: mimeTypes ? mimeTypes[path.filename] : "",
url: path.url,
};
});
void (0, copyAssociatedFiles_1.copyAssociatedFilesByType)(fileInfos, storageAuthentication, sourceItemId, destinationItemId, destinationAuthentication, template, templateDictionary).then((results) => {
const allOK = results
// Filter out metadata
.filter((result) => result.filename !== "metadata.xml")
// Extract success
.map((result) => result.fetchedFromSource && result.copiedToDestination)
// Boil it down to a single result
.reduce((success, currentValue) => success && currentValue, true);
if (allOK) {
resolve(true);
}
else {
reject();
}
});
});
}
exports.copyFilesFromStorageItem = copyFilesFromStorageItem;
/**
* Copies the files for storing the resources, metadata, and thumbnail of an item or group to a storage item
* with a specified path.
*
* @param files List of item files and paths for storing the files
* @param storageItemId Id of item to receive copy of resource/metadata
* @param storageAuthentication Credentials for the request to the storage
* @returns A promise which resolves to a list of the filenames under which the resource/metadata are stored
*/
function copyFilesToStorageItem(files, storageItemId, storageAuthentication) {
return new Promise((resolve) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
void (0, copyAssociatedFiles_1.copyFilesAsResources)(files, storageItemId, storageAuthentication).then((results) => {
resolve(results
// Filter out failures
.filter((result) => result.fetchedFromSource && result.copiedToDestination)
// Return folder and filename in storage item's resources
.map((result) => result.folder + "/" + result.filename));
});
});
}
exports.copyFilesToStorageItem = copyFilesToStorageItem;
/**
* Generates a folder and filename for storing a copy of an item's metadata in a storage item.
*
* @param itemId Id of item
* @returns Folder and filename for storage; folder is the itemID suffixed with "_info_metadata"
* @see convertStorageResourceToItemResource
*/
function generateMetadataStorageFilename(itemId) {
return {
folder: itemId + "_info_metadata",
filename: "metadata.xml",
};
}
exports.generateMetadataStorageFilename = generateMetadataStorageFilename;
/**
* Generates a list of full URLs and storage folder/filename combinations for storing the resources, metadata,
* and thumbnail of an item.
*
* @param portalSharingUrl Server/sharing
* @param itemId Id of item
* @param thumbnailUrlPart Partial path to the thumbnail held in an item's JSON
* @param resourceFilenames List of resource filenames for an item, e.g., ["file1", "myFolder/file2"]
* @param isGroup Boolean to indicate if the files are associated with a group or item
* @param storageVersion Version of the Solution template
* @returns List of item files' URLs and folder/filenames for storing the files
*/
function generateSourceFilePaths(portalSharingUrl, itemId, thumbnailUrlPart, resourceFilenames, isGroup = false, storageVersion = 0) {
const filePaths = resourceFilenames.map((resourceFilename) => {
return {
itemId,
url: generateSourceResourceUrl(portalSharingUrl, itemId, resourceFilename),
...(0, convert_item_resource_to_storage_resource_1.convertItemResourceToStorageResource)(itemId, resourceFilename, storageVersion),
};
});
filePaths.push({
itemId,
url: generateSourceMetadataUrl(portalSharingUrl, itemId, isGroup),
...generateMetadataStorageFilename(itemId),
});
/* istanbul ignore else */
if (thumbnailUrlPart) {
filePaths.push(generateSourceThumbnailPath(portalSharingUrl, itemId, thumbnailUrlPart, isGroup));
}
return filePaths;
}
exports.generateSourceFilePaths = generateSourceFilePaths;
/**
* Generates the full URL and storage folder/filename for storing an item's thumbnail.
*
* @param portalSharingUrl Server/sharing
* @param itemId Id of item
* @param thumbnailUrlPart Partial path to the thumbnail held in an item's JSON
* @param isGroup Boolean to indicate if the files are associated with a group or item
* @returns URL and folder/filename for storing the thumbnail
*/
function generateSourceThumbnailPath(portalSharingUrl, itemId, thumbnailUrlPart, isGroup = false) {
return {
itemId,
url: (0, generalHelpers_1.appendQueryParam)(generateSourceThumbnailUrl(portalSharingUrl, itemId, thumbnailUrlPart, isGroup), "w=400"),
...generateThumbnailStorageFilename(itemId, thumbnailUrlPart),
};
}
exports.generateSourceThumbnailPath = generateSourceThumbnailPath;
/**
* Generates the URL for reading an item's metadata.
*
* @param sourcePortalSharingUrl Server/sharing
* @param itemId Id of item
* @param isGroup Boolean to indicate if the files are associated with a group or item
* @returns URL string
*/
function generateSourceMetadataUrl(sourcePortalSharingUrl, itemId, isGroup = false) {
return ((0, generalHelpers_1.checkUrlPathTermination)(sourcePortalSharingUrl) +
(isGroup ? "community/groups/" : "content/items/") +
itemId +
"/info/metadata/metadata.xml");
}
exports.generateSourceMetadataUrl = generateSourceMetadataUrl;
/**
* Generates the URL for reading an item's resource given the filename of the resource.
*
* @param sourcePortalSharingUrl Server/sharing
* @param itemId Id of item
* @param sourceResourceFilename Either filename or folder/filename to resource
* @returns URL string
*/
function generateSourceResourceUrl(sourcePortalSharingUrl, itemId, sourceResourceFilename) {
// Escape resource name, but not "/" because AGO needs the path separator
const resourceName = encodeURIComponent(sourceResourceFilename).replace(/%2F/gi, "/");
return (0, generalHelpers_1.checkUrlPathTermination)(sourcePortalSharingUrl) + "content/items/" + itemId + "/resources/" + resourceName;
}
exports.generateSourceResourceUrl = generateSourceResourceUrl;
/**
* Generates the URL for reading an item's thumbnail.
*
* @param sourcePortalSharingUrl Server/sharing
* @param itemId Id of item
* @param thumbnailUrlPart Partial path to the thumbnail held in an item's JSON
* @param isGroup Boolean to indicate if the files are associated with a group or item
* @returns URL string
*/
function generateSourceThumbnailUrl(sourcePortalSharingUrl, itemId, thumbnailUrlPart, isGroup = false) {
return ((0, generalHelpers_1.checkUrlPathTermination)(sourcePortalSharingUrl) +
(isGroup ? "community/groups/" : "content/items/") +
itemId +
"/info/" +
thumbnailUrlPart);
}
exports.generateSourceThumbnailUrl = generateSourceThumbnailUrl;
/**
* Generates a list of full URLs and folder/filename combinations used to store the resources, metadata,
* and thumbnail of an item.
*
* @param portalSharingUrl Server/sharing
* @param storageItemId Id of storage item
* @param resourceFilenames List of resource filenames for an item, e.g., ["file1", "myFolder/file2"]
* @param storageVersion Version of the Solution template
* @returns List of item files' URLs and folder/filenames for storing the files
*/
function generateStorageFilePaths(portalSharingUrl, storageItemId, resourceFilenames = [], storageVersion = 0) {
return resourceFilenames.map((resourceFilename) => {
return {
url: generateSourceResourceUrl(portalSharingUrl, storageItemId, resourceFilename),
...(0, convert_storage_resource_to_item_resource_1.convertStorageResourceToItemResource)(resourceFilename, storageVersion),
};
});
}
exports.generateStorageFilePaths = generateStorageFilePaths;
/**
* Generates a folder and filename for storing a copy of an item's thumbnail in a storage item.
*
* @param itemId Id of item
* @param thumbnailUrlPart Partial path to the thumbnail held in an item's JSON; can also be a filename
* @returns Folder and filename for storage; folder is the itemID suffixed with "_info_thumbnail";
* file is URI-encoded thumbnailUrlPart
* @see convertStorageResourceToItemResource
*/
function generateThumbnailStorageFilename(itemId, thumbnailurl) {
const folder = itemId + "_info_thumbnail";
const thumbnailUrlParts = thumbnailurl.split("/");
const filename = thumbnailUrlParts.length === 1 ? thumbnailUrlParts[0] : thumbnailUrlParts[1];
return { folder, filename };
}
exports.generateThumbnailStorageFilename = generateThumbnailStorageFilename;
function isSupportedFileType(filename) {
// Supported file formats are: .json, .xml, .txt, .png, .pbf, .zip, .jpeg, .jpg, .gif, .bmp, .gz, .svg,
// .svgz, .geodatabase (https://developers.arcgis.com/rest/users-groups-and-items/add-resources.htm)
const filenameExtension = filename.match(/\.([a-z]+)$/i);
const supportedExtensions = "|.json|.xml|.txt|.png|.pbf|.zip|.jpeg|.jpg|.gif|.bmp|.gz|.svg|.svgz|.geodatabase|";
return !!filenameExtension && supportedExtensions.indexOf("|" + filenameExtension[0] + "|") >= 0;
}
exports.isSupportedFileType = isSupportedFileType;
/**
* Gets the thumbnail of an item or group.
*
* @param authentication Credentials for the request to the storage
* @param filePaths List of item files' URLs and folder/filenames for storing the files
* @returns A promise which resolves to a boolean indicating if the copies were successful
*/
function getThumbnailFromStorageItem(authentication, filePaths) {
let thumbnailUrl;
let thumbnailFilename;
filePaths.forEach((path) => {
if (path.type === interfaces_1.EFileType.Thumbnail) {
thumbnailUrl = path.url;
thumbnailFilename = path.filename;
}
});
if (!thumbnailUrl) {
return Promise.resolve(null);
}
return (0, restHelpersGet_1.getThumbnailFile)(thumbnailUrl, thumbnailFilename, authentication);
}
exports.getThumbnailFromStorageItem = getThumbnailFromStorageItem;
/**
* Removes the item's resource that matches the filename with new content
*
* @param itemId Id of the item to remove
* @param filename Name of the resource file to remove
* @param authentication Credentials for the request to the storage
* @returns A promise which resolves with a success true/false response
*/
function removeItemResourceFile(itemId, filename, authentication) {
return (0, arcgisRestJS_1.removeItemResource)({
id: itemId,
resource: filename,
authentication: authentication,
});
}
exports.removeItemResourceFile = removeItemResourceFile;
/**
* Updates the item's resource that matches the filename with new content
*
* @param itemId Id of the item to update
* @param filename Name of the resource file to update; prefix optional (e.g., a/b/file.txt)
* @param resource The new content to update the resource with
* @param authentication Credentials for the request to the storage
* @returns A promise which resolves with a success true/false response
*/
function updateItemResourceFile(itemId, filename, resource, authentication) {
// Prefix has to be specified separately
const prefixedFilenameParts = filename.split("/");
const prefix = prefixedFilenameParts.length > 1
? prefixedFilenameParts.slice(0, prefixedFilenameParts.length - 1).join("/")
: undefined;
const suffix = prefixedFilenameParts[prefixedFilenameParts.length - 1];
return (0, arcgisRestJS_1.updateItemResource)({
id: itemId,
prefix: prefix,
name: suffix,
resource,
authentication: authentication,
});
}
exports.updateItemResourceFile = updateItemResourceFile;
//# sourceMappingURL=resourceHelpers.js.map