UNPKG

8.11 kBPlain TextView Raw
1import BlobStorageContext from "../context/BlobStorageContext";
2import NotImplementedError from "../errors/NotImplementedError";
3import * as Models from "../generated/artifacts/models";
4import Context from "../generated/Context";
5import IServiceHandler from "../generated/handlers/IServiceHandler";
6import { parseXML } from "../generated/utils/xml";
7import {
8 BLOB_API_VERSION,
9 DEFAULT_LIST_CONTAINERS_MAX_RESULTS,
10 EMULATOR_ACCOUNT_KIND,
11 EMULATOR_ACCOUNT_SKUNAME
12} from "../utils/constants";
13import BaseHandler from "./BaseHandler";
14
15/**
16 * ServiceHandler handles Azure Storage Blob service related requests.
17 *
18 * @export
19 * @class ServiceHandler
20 * @implements {IHandler}
21 */
22export default class ServiceHandler extends BaseHandler
23 implements IServiceHandler {
24 /**
25 * Default service properties.
26 *
27 * @private
28 * @memberof ServiceHandler
29 */
30 private readonly defaultServiceProperties = {
31 cors: [],
32 defaultServiceVersion: BLOB_API_VERSION,
33 hourMetrics: {
34 enabled: false,
35 retentionPolicy: {
36 enabled: false
37 },
38 version: "1.0"
39 },
40 logging: {
41 deleteProperty: true,
42 read: true,
43 retentionPolicy: {
44 enabled: false
45 },
46 version: "1.0",
47 write: true
48 },
49 minuteMetrics: {
50 enabled: false,
51 retentionPolicy: {
52 enabled: false
53 },
54 version: "1.0"
55 },
56 staticWebsite: {
57 enabled: false
58 }
59 };
60
61 public getUserDelegationKey(
62 keyInfo: Models.KeyInfo,
63 options: Models.ServiceGetUserDelegationKeyOptionalParams,
64 context: Context
65 ): Promise<Models.ServiceGetUserDelegationKeyResponse> {
66 throw new NotImplementedError(context.contextId);
67 }
68
69 public submitBatch(
70 body: NodeJS.ReadableStream,
71 contentLength: number,
72 multipartContentType: string,
73 options: Models.ServiceSubmitBatchOptionalParams,
74 context: Context
75 ): Promise<Models.ServiceSubmitBatchResponse> {
76 throw new NotImplementedError(context.contextId);
77 }
78
79 /**
80 * Set blob service properties.
81 *
82 * @param {Models.StorageServiceProperties} storageServiceProperties
83 * @param {Models.ServiceSetPropertiesOptionalParams} options
84 * @param {Context} context
85 * @returns {Promise<Models.ServiceSetPropertiesResponse>}
86 * @memberof ServiceHandler
87 */
88 public async setProperties(
89 storageServiceProperties: Models.StorageServiceProperties,
90 options: Models.ServiceSetPropertiesOptionalParams,
91 context: Context
92 ): Promise<Models.ServiceSetPropertiesResponse> {
93 const blobCtx = new BlobStorageContext(context);
94 const accountName = blobCtx.account!;
95
96 // TODO: deserializor has a bug that when cors is undefined,
97 // it will serialize it to empty array instead of undefined
98 const body = blobCtx.request!.getBody();
99 const parsedBody = await parseXML(body || "");
100 if (
101 !parsedBody.hasOwnProperty("cors") &&
102 !parsedBody.hasOwnProperty("Cors")
103 ) {
104 storageServiceProperties.cors = undefined;
105 }
106
107 // Azure Storage allows allowedHeaders and exposedHeaders to be empty,
108 // Azurite will set to empty string for this scenario
109 for (const cors of storageServiceProperties.cors || []) {
110 cors.allowedHeaders = cors.allowedHeaders || "";
111 cors.exposedHeaders = cors.exposedHeaders || "";
112 }
113
114 await this.metadataStore.setServiceProperties(context, {
115 ...storageServiceProperties,
116 accountName
117 });
118
119 const response: Models.ServiceSetPropertiesResponse = {
120 requestId: context.contextId,
121 statusCode: 202,
122 version: BLOB_API_VERSION,
123 clientRequestId: options.requestId
124 };
125 return response;
126 }
127
128 /**
129 * Get blob service properties.
130 *
131 * @param {Models.ServiceGetPropertiesOptionalParams} options
132 * @param {Context} context
133 * @returns {Promise<Models.ServiceGetPropertiesResponse>}
134 * @memberof ServiceHandler
135 */
136 public async getProperties(
137 options: Models.ServiceGetPropertiesOptionalParams,
138 context: Context
139 ): Promise<Models.ServiceGetPropertiesResponse> {
140 const blobCtx = new BlobStorageContext(context);
141 const accountName = blobCtx.account!;
142
143 let properties = await this.metadataStore.getServiceProperties(
144 context,
145 accountName
146 );
147 if (!properties) {
148 properties = { ...this.defaultServiceProperties, accountName };
149 }
150
151 if (properties.cors === undefined) {
152 properties.cors = [];
153 }
154
155 if (properties.cors === undefined) {
156 properties.cors = [];
157 }
158
159 if (properties.hourMetrics === undefined) {
160 properties.hourMetrics = this.defaultServiceProperties.hourMetrics;
161 }
162
163 if (properties.logging === undefined) {
164 properties.logging = this.defaultServiceProperties.logging;
165 }
166
167 if (properties.minuteMetrics === undefined) {
168 properties.minuteMetrics = this.defaultServiceProperties.minuteMetrics;
169 }
170
171 if (properties.defaultServiceVersion === undefined) {
172 properties.defaultServiceVersion = this.defaultServiceProperties.defaultServiceVersion;
173 }
174
175 if (properties.staticWebsite === undefined) {
176 properties.staticWebsite = this.defaultServiceProperties.staticWebsite;
177 }
178
179 const response: Models.ServiceGetPropertiesResponse = {
180 ...properties,
181 requestId: context.contextId,
182 statusCode: 200,
183 version: BLOB_API_VERSION,
184 clientRequestId: options.requestId
185 };
186 return response;
187 }
188
189 public async getStatistics(
190 options: Models.ServiceGetStatisticsOptionalParams,
191 context: Context
192 ): Promise<Models.ServiceGetStatisticsResponse> {
193 const response: Models.ServiceGetStatisticsResponse = {
194 statusCode: 200,
195 requestId: context.contextId,
196 version: BLOB_API_VERSION,
197 date: context.startTime,
198 clientRequestId: options.requestId,
199 geoReplication: {
200 status: Models.GeoReplicationStatusType.Live,
201 lastSyncTime: context.startTime!
202 }
203 };
204 return response;
205 }
206
207 /**
208 * List containers.
209 *
210 * @param {Models.ServiceListContainersSegmentOptionalParams} options
211 * @param {Context} context
212 * @returns {Promise<Models.ServiceListContainersSegmentResponse>}
213 * @memberof ServiceHandler
214 */
215 public async listContainersSegment(
216 options: Models.ServiceListContainersSegmentOptionalParams,
217 context: Context
218 ): Promise<Models.ServiceListContainersSegmentResponse> {
219 const blobCtx = new BlobStorageContext(context);
220 const request = blobCtx.request!;
221 const accountName = blobCtx.account!;
222
223 options.maxresults =
224 options.maxresults || DEFAULT_LIST_CONTAINERS_MAX_RESULTS;
225 options.prefix = options.prefix || "";
226
227 const marker = options.marker || "";
228
229 const containers = await this.metadataStore.listContainers(
230 context,
231 accountName,
232 options.prefix,
233 options.maxresults,
234 marker
235 );
236
237 // TODO: Need update list out container lease properties with ContainerHandler.updateLeaseAttributes()
238 const serviceEndpoint = `${request.getEndpoint()}/${accountName}`;
239 const res: Models.ServiceListContainersSegmentResponse = {
240 containerItems: containers[0],
241 maxResults: options.maxresults,
242 nextMarker: `${containers[1] || ""}`,
243 prefix: options.prefix,
244 serviceEndpoint,
245 statusCode: 200,
246 requestId: context.contextId,
247 version: BLOB_API_VERSION,
248 clientRequestId: options.requestId
249 };
250
251 return res;
252 }
253
254 public async getAccountInfo(
255 context: Context
256 ): Promise<Models.ServiceGetAccountInfoResponse> {
257 const response: Models.ContainerGetAccountInfoResponse = {
258 statusCode: 200,
259 requestId: context.contextId,
260 clientRequestId: context.request!.getHeader("x-ms-client-request-id"),
261 skuName: EMULATOR_ACCOUNT_SKUNAME,
262 accountKind: EMULATOR_ACCOUNT_KIND,
263 date: context.startTime!,
264 version: BLOB_API_VERSION
265 };
266 return response;
267 }
268
269 public async getAccountInfoWithHead(
270 context: Context
271 ): Promise<Models.ServiceGetAccountInfoResponse> {
272 return this.getAccountInfo(context);
273 }
274}