UNPKG

@esri/solution-common

Version:

Provides general helper functions for @esri/solution.js.

382 lines 18.9 kB
"use strict"; /** @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