UNPKG

azurite

Version:

An open source Azure Storage API compatible server

293 lines 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const BlobStorageContext_1 = tslib_1.__importDefault(require("../context/BlobStorageContext")); const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory")); const Models = tslib_1.__importStar(require("../generated/artifacts/models")); const xml_1 = require("../generated/utils/xml"); const constants_1 = require("../utils/constants"); const BaseHandler_1 = tslib_1.__importDefault(require("./BaseHandler")); const BlobBatchHandler_1 = require("./BlobBatchHandler"); const stream_1 = require("stream"); const constants_2 = require("../../common/utils/constants"); const jsonwebtoken_1 = require("jsonwebtoken"); const utils_1 = require("../utils/utils"); /** * ServiceHandler handles Azure Storage Blob service related requests. * * @export * @class ServiceHandler * @implements {IHandler} */ class ServiceHandler extends BaseHandler_1.default { constructor(accountDataStore, oauth, metadataStore, extentStore, logger, loose, disableProductStyle) { super(metadataStore, extentStore, logger, loose); this.accountDataStore = accountDataStore; this.oauth = oauth; /** * Default service properties. * * @private * @memberof ServiceHandler */ this.defaultServiceProperties = { cors: [], defaultServiceVersion: constants_1.BLOB_API_VERSION, hourMetrics: { enabled: false, retentionPolicy: { enabled: false }, version: "1.0" }, logging: { deleteProperty: true, read: true, retentionPolicy: { enabled: false }, version: "1.0", write: true }, minuteMetrics: { enabled: false, retentionPolicy: { enabled: false }, version: "1.0" }, staticWebsite: { enabled: false } }; this.disableProductStyle = disableProductStyle; } async getUserDelegationKey(keyInfo, options, context) { const blobContext = new BlobStorageContext_1.default(context); const request = blobContext.request; const authHeaderValue = request.getHeader(constants_1.HeaderConstants.AUTHORIZATION); const token = authHeaderValue.substr(constants_2.BEARER_TOKEN_PREFIX.length + 1); const decodedToken = (0, jsonwebtoken_1.decode)(token); const keyValue = (0, utils_1.getUserDelegationKeyValue)(decodedToken.oid, decodedToken.tid, keyInfo.start, keyInfo.expiry, constants_1.BLOB_API_VERSION); const response = { statusCode: 200, signedOid: decodedToken.oid, signedTid: decodedToken.tid, signedService: "b", signedVersion: constants_1.BLOB_API_VERSION, signedStart: keyInfo.start, signedExpiry: keyInfo.expiry, value: keyValue }; return response; } async submitBatch(body, contentLength, multipartContentType, options, context) { const blobServiceCtx = new BlobStorageContext_1.default(context); const requestBatchBoundary = blobServiceCtx.request.getHeader("content-type").split("=")[1]; const blobBatchHandler = new BlobBatchHandler_1.BlobBatchHandler(this.accountDataStore, this.oauth, this.metadataStore, this.extentStore, this.logger, this.loose, this.disableProductStyle); const responseBodyString = await blobBatchHandler.submitBatch(body, requestBatchBoundary, "", context.request, context); const responseBody = new stream_1.Readable(); responseBody.push(responseBodyString); responseBody.push(null); // No client request id defined in batch response, should refine swagger and regenerate from it. // batch response succeed code should be 202 instead of 200, should refine swagger and regenerate from it. const response = { statusCode: 202, requestId: context.contextId, version: constants_1.BLOB_API_VERSION, contentType: "multipart/mixed; boundary=" + requestBatchBoundary, body: responseBody }; return response; } /** * Set blob service properties. * * @param {Models.StorageServiceProperties} storageServiceProperties * @param {Models.ServiceSetPropertiesOptionalParams} options * @param {Context} context * @returns {Promise<Models.ServiceSetPropertiesResponse>} * @memberof ServiceHandler */ async setProperties(storageServiceProperties, options, context) { const blobCtx = new BlobStorageContext_1.default(context); const accountName = blobCtx.account; // TODO: deserializer has a bug that when cors is undefined, // it will serialize it to empty array instead of undefined const body = blobCtx.request.getBody(); const parsedBody = await (0, xml_1.parseXML)(body || ""); if (!Object.hasOwnProperty.bind(parsedBody)('cors') && !Object.hasOwnProperty.bind(parsedBody)('Cors')) { storageServiceProperties.cors = undefined; } // Azure Storage allows allowedHeaders and exposedHeaders to be empty, // Azurite will set to empty string for this scenario for (const cors of storageServiceProperties.cors || []) { cors.allowedHeaders = cors.allowedHeaders || ""; cors.exposedHeaders = cors.exposedHeaders || ""; } await this.metadataStore.setServiceProperties(context, { ...storageServiceProperties, accountName }); const response = { requestId: context.contextId, statusCode: 202, version: constants_1.BLOB_API_VERSION, clientRequestId: options.requestId }; return response; } /** * Get blob service properties. * * @param {Models.ServiceGetPropertiesOptionalParams} options * @param {Context} context * @returns {Promise<Models.ServiceGetPropertiesResponse>} * @memberof ServiceHandler */ async getProperties(options, context) { const blobCtx = new BlobStorageContext_1.default(context); const accountName = blobCtx.account; let properties = await this.metadataStore.getServiceProperties(context, accountName); if (!properties) { properties = { ...this.defaultServiceProperties, accountName }; } if (properties.cors === undefined) { properties.cors = []; } if (properties.cors === undefined) { properties.cors = []; } if (properties.hourMetrics === undefined) { properties.hourMetrics = this.defaultServiceProperties.hourMetrics; } if (properties.logging === undefined) { properties.logging = this.defaultServiceProperties.logging; } if (properties.minuteMetrics === undefined) { properties.minuteMetrics = this.defaultServiceProperties.minuteMetrics; } if (properties.defaultServiceVersion === undefined) { properties.defaultServiceVersion = this.defaultServiceProperties.defaultServiceVersion; } if (properties.staticWebsite === undefined) { properties.staticWebsite = this.defaultServiceProperties.staticWebsite; } const response = { ...properties, requestId: context.contextId, statusCode: 200, version: constants_1.BLOB_API_VERSION, clientRequestId: options.requestId }; return response; } async getStatistics(options, context) { if (!context.context.isSecondary) { throw StorageErrorFactory_1.default.getInvalidQueryParameterValue(context.contextId); } const response = { statusCode: 200, requestId: context.contextId, version: constants_1.BLOB_API_VERSION, date: context.startTime, clientRequestId: options.requestId, geoReplication: { status: Models.GeoReplicationStatusType.Live, lastSyncTime: context.startTime } }; return response; } /** * List containers. * * @param {Models.ServiceListContainersSegmentOptionalParams} options * @param {Context} context * @returns {Promise<Models.ServiceListContainersSegmentResponse>} * @memberof ServiceHandler */ async listContainersSegment(options, context) { const blobCtx = new BlobStorageContext_1.default(context); const request = blobCtx.request; const accountName = blobCtx.account; options.maxresults = options.maxresults || constants_1.DEFAULT_LIST_CONTAINERS_MAX_RESULTS; options.prefix = options.prefix || ""; const marker = options.marker || ""; const containers = await this.metadataStore.listContainers(context, accountName, options.prefix, options.maxresults, marker); // Only the query parameter "include" contains the value "metadata" can the result present the metadata. let includeMetadata = false; if (options.include) { for (const item of options.include) { if (item.toLowerCase() === "metadata") { includeMetadata = true; break; } } } // TODO: Need update list out container lease properties with ContainerHandler.updateLeaseAttributes() const serviceEndpoint = `${request.getEndpoint()}/${accountName}`; const res = { containerItems: containers[0].map(item => { return { ...item, metadata: includeMetadata ? item.metadata : undefined }; }), maxResults: options.maxresults, nextMarker: `${containers[1] || ""}`, prefix: options.prefix, serviceEndpoint, statusCode: 200, requestId: context.contextId, version: constants_1.BLOB_API_VERSION, clientRequestId: options.requestId }; return res; } async getAccountInfo(context) { const response = { statusCode: 200, requestId: context.contextId, clientRequestId: context.request.getHeader("x-ms-client-request-id"), skuName: constants_1.EMULATOR_ACCOUNT_SKUNAME, accountKind: constants_1.EMULATOR_ACCOUNT_KIND, date: context.startTime, isHierarchicalNamespaceEnabled: constants_1.EMULATOR_ACCOUNT_ISHIERARCHICALNAMESPACEENABLED, version: constants_1.BLOB_API_VERSION }; return response; } async getAccountInfoWithHead(context) { return this.getAccountInfo(context); } async filterBlobs(options, context) { const blobCtx = new BlobStorageContext_1.default(context); const accountName = blobCtx.account; const request = context.request; const marker = options.marker; options.marker = options.marker || ""; if (options.maxresults === undefined || options.maxresults > constants_1.DEFAULT_LIST_BLOBS_MAX_RESULTS) { options.maxresults = constants_1.DEFAULT_LIST_BLOBS_MAX_RESULTS; } const [blobs, nextMarker] = await this.metadataStore.filterBlobs(context, accountName, undefined, options.where, options.maxresults, marker); const serviceEndpoint = `${request.getEndpoint()}/${accountName}`; const response = { statusCode: 200, requestId: context.contextId, version: constants_1.BLOB_API_VERSION, date: context.startTime, serviceEndpoint, where: options.where, blobs: blobs, clientRequestId: options.requestId, nextMarker: `${nextMarker || ""}` }; return response; } } exports.default = ServiceHandler; //# sourceMappingURL=ServiceHandler.js.map