1
- import { Aborter , BlockBlobURL , ContainerURL , Credential , ServiceURL , StorageURL , TokenCredential , uploadFileToBlockBlob } from "@azure/storage-blob" ;
1
+ import { StorageAccounts , StorageManagementClientContext } from "@azure/arm-storage" ;
2
+ import { Aborter , BlobSASPermissions , BlockBlobURL , ContainerURL , generateBlobSASQueryParameters , SASProtocol ,
3
+ ServiceURL , SharedKeyCredential , StorageURL , TokenCredential , uploadFileToBlockBlob } from "@azure/storage-blob" ;
2
4
import Serverless from "serverless" ;
3
5
import { Guard } from "../shared/guard" ;
4
6
import { BaseService } from "./baseService" ;
5
7
import { AzureLoginService } from "./loginService" ;
6
8
9
+ export enum AzureStorageAuthType {
10
+ SharedKey ,
11
+ Token
12
+ }
13
+
7
14
/**
8
15
* Wrapper for operations on Azure Blob Storage account
9
16
*/
@@ -13,15 +20,26 @@ export class AzureBlobStorageService extends BaseService {
13
20
* Account URL for Azure Blob Storage account. Depends on `storageAccountName` being set in baseService
14
21
*/
15
22
private accountUrl : string ;
16
- private storageCredential : Credential ;
23
+ private authType : AzureStorageAuthType ;
24
+ private storageCredential : SharedKeyCredential | TokenCredential ;
17
25
18
- public constructor ( serverless : Serverless , options : Serverless . Options ) {
26
+ public constructor ( serverless : Serverless , options : Serverless . Options ,
27
+ authType : AzureStorageAuthType = AzureStorageAuthType . SharedKey ) {
19
28
super ( serverless , options ) ;
20
29
this . accountUrl = `https://${ this . storageAccountName } .blob.core.windows.net` ;
30
+ this . authType = authType ;
21
31
}
22
32
33
+ /**
34
+ * Initialize Blob Storage service. This creates the credentials required
35
+ * to perform any operation with the service
36
+ */
23
37
public async initialize ( ) {
24
- this . storageCredential = new TokenCredential ( await this . getToken ( ) ) ;
38
+ this . storageCredential = ( this . authType === AzureStorageAuthType . SharedKey )
39
+ ?
40
+ new SharedKeyCredential ( this . storageAccountName , await this . getKey ( ) )
41
+ :
42
+ new TokenCredential ( await this . getToken ( ) ) ;
25
43
}
26
44
27
45
/**
@@ -31,11 +49,15 @@ export class AzureBlobStorageService extends BaseService {
31
49
* @param blobName Name of blob file created as a result of upload
32
50
*/
33
51
public async uploadFile ( path : string , containerName : string , blobName ?: string ) {
34
- Guard . empty ( path ) ;
35
- Guard . empty ( containerName ) ;
52
+ Guard . empty ( path , "path" ) ;
53
+ Guard . empty ( containerName , "containerName" ) ;
54
+ this . checkInitialization ( ) ;
55
+
36
56
// Use specified blob name or replace `/` in path with `-`
37
57
const name = blobName || path . replace ( / ^ .* [ \\ \/ ] / , "-" ) ;
38
- uploadFileToBlockBlob ( Aborter . none , path , this . getBlockBlobURL ( containerName , name ) ) ;
58
+ this . log ( `Uploading file at '${ path } ' to container '${ containerName } ' with name '${ name } '` )
59
+ await uploadFileToBlockBlob ( Aborter . none , path , this . getBlockBlobURL ( containerName , name ) ) ;
60
+ this . log ( "Finished uploading blob" ) ;
39
61
} ;
40
62
41
63
/**
@@ -44,8 +66,10 @@ export class AzureBlobStorageService extends BaseService {
44
66
* @param blobName Blob to delete
45
67
*/
46
68
public async deleteFile ( containerName : string , blobName : string ) : Promise < void > {
47
- Guard . empty ( containerName ) ;
48
- Guard . empty ( blobName ) ;
69
+ Guard . empty ( containerName , "containerName" ) ;
70
+ Guard . empty ( blobName , "blobName" ) ;
71
+ this . checkInitialization ( ) ;
72
+
49
73
const blockBlobUrl = await this . getBlockBlobURL ( containerName , blobName )
50
74
await blockBlobUrl . delete ( Aborter . none ) ;
51
75
}
@@ -57,6 +81,8 @@ export class AzureBlobStorageService extends BaseService {
57
81
*/
58
82
public async listFiles ( containerName : string , ext ?: string ) : Promise < string [ ] > {
59
83
Guard . empty ( containerName , "containerName" ) ;
84
+ this . checkInitialization ( ) ;
85
+
60
86
const result : string [ ] = [ ] ;
61
87
let marker ;
62
88
const containerURL = this . getContainerURL ( containerName ) ;
@@ -80,6 +106,8 @@ export class AzureBlobStorageService extends BaseService {
80
106
* Lists the containers within the Azure Blob Storage account
81
107
*/
82
108
public async listContainers ( ) {
109
+ this . checkInitialization ( ) ;
110
+
83
111
const result : string [ ] = [ ] ;
84
112
let marker ;
85
113
do {
@@ -100,26 +128,73 @@ export class AzureBlobStorageService extends BaseService {
100
128
* Creates container specified in Azure Cloud Storage options
101
129
* @param containerName - Name of container to create
102
130
*/
103
- public async createContainer ( containerName : string ) : Promise < void > {
104
- Guard . empty ( containerName ) ;
105
- const containerURL = this . getContainerURL ( containerName ) ;
106
- await containerURL . create ( Aborter . none ) ;
131
+ public async createContainerIfNotExists ( containerName : string ) : Promise < void > {
132
+ Guard . empty ( containerName , "containerName" ) ;
133
+ this . checkInitialization ( ) ;
134
+
135
+ const containers = await this . listContainers ( ) ;
136
+ if ( ! containers . find ( ( name ) => name === containerName ) ) {
137
+ const containerURL = this . getContainerURL ( containerName ) ;
138
+ await containerURL . create ( Aborter . none ) ;
139
+ }
107
140
}
108
141
109
142
/**
110
143
* Delete a container from Azure Blob Storage Account
111
144
* @param containerName Name of container to delete
112
145
*/
113
146
public async deleteContainer ( containerName : string ) : Promise < void > {
114
- Guard . empty ( containerName ) ;
147
+ Guard . empty ( containerName , "containerName" ) ;
148
+ this . checkInitialization ( ) ;
149
+
115
150
const containerUrl = await this . getContainerURL ( containerName )
116
151
await containerUrl . delete ( Aborter . none ) ;
117
152
}
118
153
154
+ /**
155
+ * Generate URL with SAS token for a specific blob
156
+ * @param containerName Name of container containing blob
157
+ * @param blobName Name of blob to generate SAS token for
158
+ * @param days Number of days from current date until expiry of SAS token. Defaults to 1 year
159
+ */
160
+ public async generateBlobSasTokenUrl ( containerName : string , blobName : string , days : number = 365 ) : Promise < string > {
161
+ this . checkInitialization ( ) ;
162
+ if ( this . authType !== AzureStorageAuthType . SharedKey ) {
163
+ throw new Error ( "Need to authenticate with shared key in order to generate SAS tokens. " +
164
+ "Initialize Blob Service with SharedKey auth type" ) ;
165
+ }
166
+
167
+ const now = new Date ( ) ;
168
+ const endDate = new Date ( now ) ;
169
+ endDate . setDate ( endDate . getDate ( ) + days ) ;
170
+
171
+ const blobSas = generateBlobSASQueryParameters ( {
172
+ blobName,
173
+ cacheControl : "cache-control-override" ,
174
+ containerName,
175
+ contentDisposition : "content-disposition-override" ,
176
+ contentEncoding : "content-encoding-override" ,
177
+ contentLanguage : "content-language-override" ,
178
+ contentType : "content-type-override" ,
179
+ expiryTime : endDate ,
180
+ ipRange : { start : "0.0.0.0" , end : "255.255.255.255" } ,
181
+ permissions : BlobSASPermissions . parse ( "racwd" ) . toString ( ) ,
182
+ protocol : SASProtocol . HTTPSandHTTP ,
183
+ startTime : now ,
184
+ version : "2016-05-31"
185
+ } ,
186
+ this . storageCredential as SharedKeyCredential ) ;
187
+
188
+ const blobUrl = this . getBlockBlobURL ( containerName , blobName ) ;
189
+ return `${ blobUrl . url } ?${ blobSas } `
190
+ }
191
+
119
192
/**
120
193
* Get ServiceURL object for Azure Blob Storage Account
121
194
*/
122
195
private getServiceURL ( ) : ServiceURL {
196
+ this . checkInitialization ( ) ;
197
+
123
198
const pipeline = StorageURL . newPipeline ( this . storageCredential ) ;
124
199
const accountUrl = this . accountUrl ;
125
200
const serviceUrl = new ServiceURL (
@@ -135,7 +210,9 @@ export class AzureBlobStorageService extends BaseService {
135
210
* @param serviceURL Previously created ServiceURL object (will create if undefined)
136
211
*/
137
212
private getContainerURL ( containerName : string ) : ContainerURL {
138
- Guard . empty ( containerName ) ;
213
+ Guard . empty ( containerName , "containerName" ) ;
214
+ this . checkInitialization ( ) ;
215
+
139
216
return ContainerURL . fromServiceURL (
140
217
this . getServiceURL ( ) ,
141
218
containerName
@@ -148,19 +225,44 @@ export class AzureBlobStorageService extends BaseService {
148
225
* @param blobName Name of blob
149
226
*/
150
227
private getBlockBlobURL ( containerName : string , blobName : string ) : BlockBlobURL {
151
- Guard . empty ( containerName ) ;
152
- Guard . empty ( blobName ) ;
228
+ Guard . empty ( containerName , "containerName" ) ;
229
+ Guard . empty ( blobName , "blobName" ) ;
230
+ this . checkInitialization ( ) ;
231
+
153
232
return BlockBlobURL . fromContainerURL (
154
233
this . getContainerURL ( containerName ) ,
155
234
blobName ,
156
235
) ;
157
236
}
158
237
238
+ /**
239
+ * Get access token by logging in (again) with a storage-specific context
240
+ */
159
241
private async getToken ( ) : Promise < string > {
160
242
const authResponse = await AzureLoginService . login ( {
161
243
tokenAudience : "https://storage.azure.com/"
162
244
} ) ;
163
245
const token = await authResponse . credentials . getToken ( ) ;
164
246
return token . accessToken ;
165
247
}
248
+
249
+ /**
250
+ * Get access key for storage account
251
+ */
252
+ private async getKey ( ) : Promise < string > {
253
+ const context = new StorageManagementClientContext ( this . credentials , this . subscriptionId )
254
+ const storageAccounts = new StorageAccounts ( context ) ;
255
+ const keys = await storageAccounts . listKeys ( this . resourceGroup , this . storageAccountName ) ;
256
+ return keys . keys [ 0 ] . value ;
257
+ }
258
+
259
+ /**
260
+ * Ensure that the blob storage service has been initialized. If not initialized,
261
+ * the credentials will not be available for any operation
262
+ */
263
+ private checkInitialization ( ) {
264
+ Guard . null ( this . storageCredential , "storageCredential" ,
265
+ "Azure Blob Storage Service has not been initialized. Make sure .initialize() has been called " +
266
+ "before performing any operation" ) ;
267
+ }
166
268
}
0 commit comments