azurite
Version:
An open source Azure Storage API compatible server
293 lines • 12.5 kB
JavaScript
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
;