Skip to content

Commit 77b6340

Browse files
chore!: updating azure-storage package to new @azue/storage-blob package (#661)
BREAKING CHANGE: azure-storage package is now deprecated Co-authored-by: phillipperalez <peralez@google.com>
1 parent 55297e1 commit 77b6340

File tree

5 files changed

+219
-171
lines changed

5 files changed

+219
-171
lines changed

lib/actions/azure/azure_storage.js

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
33
exports.AzureStorageAction = void 0;
4+
const storage_blob_1 = require("@azure/storage-blob");
45
const Hub = require("../../hub");
5-
const azure = require("azure-storage");
66
class AzureStorageAction extends Hub.Action {
77
constructor() {
88
super(...arguments);
@@ -39,14 +39,11 @@ class AzureStorageAction extends Hub.Action {
3939
return new Hub.ActionResponse({ success: false, message: "Cannot determine a filename." });
4040
}
4141
const blobService = this.azureClientFromRequest(request);
42-
const writeStream = blobService.createWriteStreamToBlockBlob(container, fileName);
42+
const containerClient = blobService.getContainerClient(container);
43+
const blockBlobClient = containerClient.getBlockBlobClient(fileName);
4344
try {
4445
await request.stream(async (readable) => {
45-
return new Promise((resolve, reject) => {
46-
readable.pipe(writeStream)
47-
.on("error", reject)
48-
.on("finish", resolve);
49-
});
46+
return blockBlobClient.uploadStream(readable);
5047
});
5148
return new Hub.ActionResponse({ success: true });
5249
}
@@ -55,18 +52,12 @@ class AzureStorageAction extends Hub.Action {
5552
}
5653
}
5754
async form(request) {
58-
// error in type definition for listContainersSegmented currentToken?
59-
// https://github.com/Azure/azure-storage-node/issues/352
6055
const form = new Hub.ActionForm();
6156
const blobService = this.azureClientFromRequest(request);
62-
return new Promise((resolve, _reject) => {
63-
blobService.listContainersSegmented(null, (err, res) => {
64-
if (err) {
65-
form.error = err;
66-
resolve(form);
67-
}
68-
else {
69-
const entries = res.entries;
57+
return new Promise(async (resolve, _reject) => {
58+
for await (const response of blobService.listContainers().byPage()) {
59+
if (response.containerItems.length > 0) {
60+
const entries = response.containerItems;
7061
if (entries.length > 0) {
7162
form.fields = [{
7263
label: "Container",
@@ -84,17 +75,18 @@ class AzureStorageAction extends Hub.Action {
8475
}];
8576
resolve(form);
8677
}
87-
else {
88-
form.error = "Create a container in your Azure account.";
89-
resolve(form);
90-
}
9178
}
92-
});
79+
else {
80+
form.error = "Create a container in your Azure account.";
81+
resolve(form);
82+
}
83+
}
9384
});
9485
}
9586
azureClientFromRequest(request) {
9687
try {
97-
return azure.createBlobService(request.params.account, request.params.accessKey);
88+
const sharedKeyCredential = new storage_blob_1.StorageSharedKeyCredential(request.params.account, request.params.accessKey);
89+
return new storage_blob_1.BlobServiceClient(`https://${request.params.account}.blob.core.windows.net`, sharedKeyCredential);
9890
}
9991
catch (err) {
10092
if (err && err.toString().includes("base64")) {

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"test-dev": "./node_modules/.bin/nodemon --exec \"yarn test || true\"",
1515
"test": "NODE_OPTIONS=\"--trace-warnings\" yarn test-ts --trace-warnings && ./node_modules/.bin/tsc --noEmit && yarn lint",
1616
"test-ts": "./node_modules/.bin/mocha --exit -r ./node_modules/ts-node/register test/**/test.ts",
17+
"test:single": "NODE_OPTIONS=\"--trace-warnings\" ./node_modules/.bin/mocha --exit -r ./node_modules/ts-node/register \"$1\"",
1718
"lint": "./node_modules/.bin/tslint -c tslint.json 'src/**/*.ts' 'bin/**/*.ts' 'test/**/*.ts' -p tsconfig.json",
1819
"lint-fix": "./node_modules/.bin/tslint --fix -c tslint.json 'src/**/*.ts' 'bin/**/*.ts' 'test/**/*.ts' -p tsconfig.json",
1920
"lint-dependencies": "./node_modules/.bin/depcheck . --ignores '@types/*,depcheck,tslint-language-service' --ignore-dirs 'lib'",
@@ -31,6 +32,7 @@
3132
},
3233
"license": "MIT",
3334
"dependencies": {
35+
"@azure/storage-blob": "^12.26.0",
3436
"@google-cloud/storage": "^1.5.2",
3537
"@hubspot/api-client": "^2.0.1",
3638
"@sendgrid/helpers": "7.2.0",
@@ -59,7 +61,6 @@
5961
"analytics-node": "^3.3.0",
6062
"async-mutex": "^0.1.4",
6163
"aws-sdk": "^2.1691.0",
62-
"azure-storage": "^2.10.3",
6364
"base64-url": "^2.3.3",
6465
"blocked-at": "^1.2.0",
6566
"body-parser": "^1.20.3",
@@ -70,6 +71,7 @@
7071
"dropbox": "^3.0.0",
7172
"express": "^4.21.1",
7273
"express-winston": "^4.2.0",
74+
"firebase-admin": "^13.0.2",
7375
"googleapis": "^59.0.0",
7476
"i18n-iso-countries": "^7.1.0",
7577
"jira-client": "^6.18.0",
@@ -94,7 +96,6 @@
9496
"typescript": "^5.7.3",
9597
"uuid": "^3.4.0",
9698
"winston": "^2.4.5",
97-
"firebase-admin": "^13.0.2",
9899
"yarn": "^1.22.18"
99100
},
100101
"typings": "lib/index",

src/actions/azure/azure_storage.ts

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob"
12
import * as Hub from "../../hub"
23

3-
import * as azure from "azure-storage"
4-
54
export class AzureStorageAction extends Hub.Action {
65

76
name = "azure_storage"
@@ -41,15 +40,12 @@ export class AzureStorageAction extends Hub.Action {
4140
}
4241

4342
const blobService = this.azureClientFromRequest(request)
44-
const writeStream = blobService.createWriteStreamToBlockBlob(container, fileName)
43+
const containerClient = blobService.getContainerClient(container)
44+
const blockBlobClient = containerClient.getBlockBlobClient(fileName)
4545

4646
try {
4747
await request.stream(async (readable) => {
48-
return new Promise<any>((resolve, reject) => {
49-
readable.pipe(writeStream)
50-
.on("error", reject)
51-
.on("finish", resolve)
52-
})
48+
return blockBlobClient.uploadStream(readable)
5349
})
5450
return new Hub.ActionResponse({ success: true })
5551
} catch (e: any) {
@@ -58,17 +54,12 @@ export class AzureStorageAction extends Hub.Action {
5854
}
5955

6056
async form(request: Hub.ActionRequest) {
61-
// error in type definition for listContainersSegmented currentToken?
62-
// https://github.com/Azure/azure-storage-node/issues/352
6357
const form = new Hub.ActionForm()
64-
const blobService: any = this.azureClientFromRequest(request)
65-
return new Promise<Hub.ActionForm>((resolve, _reject) => {
66-
blobService.listContainersSegmented(null, (err: any, res: any) => {
67-
if (err) {
68-
form.error = err
69-
resolve(form)
70-
} else {
71-
const entries: any[] = res.entries
58+
const blobService: BlobServiceClient = this.azureClientFromRequest(request)
59+
return new Promise<Hub.ActionForm>(async (resolve, _reject) => {
60+
for await (const response of blobService.listContainers().byPage()) {
61+
if (response.containerItems.length > 0) {
62+
const entries: any[] = response.containerItems
7263
if (entries.length > 0) {
7364
form.fields = [{
7465
label: "Container",
@@ -85,18 +76,22 @@ export class AzureStorageAction extends Hub.Action {
8576
type: "string",
8677
}]
8778
resolve(form)
88-
} else {
89-
form.error = "Create a container in your Azure account."
90-
resolve(form)
9179
}
80+
} else {
81+
form.error = "Create a container in your Azure account."
82+
resolve(form)
9283
}
93-
})
84+
}
9485
})
9586
}
9687

97-
private azureClientFromRequest(request: Hub.ActionRequest) {
88+
private azureClientFromRequest(request: Hub.ActionRequest): BlobServiceClient {
9889
try {
99-
return azure.createBlobService(request.params.account!, request.params.accessKey!)
90+
const sharedKeyCredential = new StorageSharedKeyCredential(request.params.account!, request.params.accessKey!)
91+
return new BlobServiceClient(
92+
`https://${request.params.account!}.blob.core.windows.net`,
93+
sharedKeyCredential,
94+
)
10095
} catch (err: any) {
10196
if (err && err.toString().includes("base64")) {
10297
throw "The provided Account Key is not a valid base64 string"

src/actions/azure/test_azure_storage.ts

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
import * as chai from "chai"
2+
import chaiAsPromised = require("chai-as-promised")
23
import * as sinon from "sinon"
3-
import { Stream } from "stream"
4-
54
import * as Hub from "../../hub"
6-
75
import { AzureStorageAction } from "./azure_storage"
86

7+
chai.use(chaiAsPromised)
8+
99
const action = new AzureStorageAction()
1010

11-
function expectAzureStorageMatch(
11+
async function expectAzureStorageMatch(
1212
request: Hub.ActionRequest, _container: string, _fileName: string, dataBuffer: Buffer) {
1313

14-
const createWriteStreamToBlockBlobSpy = sinon.spy(async () => {
15-
let data = Buffer.from("")
16-
const stream = new Stream()
17-
stream
18-
.on("data", (chunk: any) => {
19-
data = Buffer.concat([data, chunk])
20-
})
21-
.on("finish", () => {
22-
chai.expect(data).to.equal(dataBuffer)
23-
})
24-
return stream
14+
const uploadStreamSpy = sinon.spy(async (buffer: Buffer) => {
15+
chai.expect(buffer).to.deep.equal(dataBuffer)
2516
})
2617

18+
const mockBlockBlobClient = {
19+
uploadStream: uploadStreamSpy,
20+
}
21+
22+
const mockContainerClient = {
23+
getBlockBlobClient: sinon.stub().returns(mockBlockBlobClient),
24+
}
25+
2726
const stubClient = sinon.stub(action as any, "azureClientFromRequest")
2827
.callsFake(() => ({
29-
createWriteStreamToBlockBlob: createWriteStreamToBlockBlobSpy,
28+
getContainerClient: sinon.stub().withArgs(_container).returns(mockContainerClient),
3029
}))
3130

3231
const stubSuggestedFilename = sinon.stub(request as any, "suggestedFilename")
@@ -52,26 +51,26 @@ describe(`${action.constructor.name} unit tests`, () => {
5251
.be.rejectedWith("Need Azure container.")
5352
})
5453

55-
it("sends right body to filename and container", () => {
54+
it("sends right body to filename and container", async () => {
5655
const request = new Hub.ActionRequest()
5756
request.formParams = {
5857
container: "mycontainer",
5958
}
6059
request.attachment = {dataBuffer: Buffer.from("1,2,3,4", "utf8")}
61-
return expectAzureStorageMatch(request,
60+
return await expectAzureStorageMatch(request,
6261
"mycontainer",
6362
"stubSuggestedFilename",
6463
Buffer.from("1,2,3,4", "utf8"))
6564
})
6665

67-
it("sends to right filename if specified", () => {
66+
it("sends to right filename if specified", async () => {
6867
const request = new Hub.ActionRequest()
6968
request.formParams = {
7069
container: "mycontainer",
7170
filename: "mywackyfilename",
7271
}
7372
request.attachment = {dataBuffer: Buffer.from("1,2,3,4", "utf8")}
74-
return expectAzureStorageMatch(request,
73+
return await expectAzureStorageMatch(request,
7574
"mycontainer",
7675
"mywackyfilename",
7776
Buffer.from("1,2,3,4", "utf8"))
@@ -88,16 +87,18 @@ describe(`${action.constructor.name} unit tests`, () => {
8887
it("has form with correct containers", (done) => {
8988
const stubClient = sinon.stub(action as any, "azureClientFromRequest")
9089
.callsFake(() => ({
91-
listContainersSegmented: (filter: any, cb: (err: any, res: any) => void) => {
92-
chai.expect(filter).to.equal(null)
93-
const containers = {
94-
entries: [
95-
{id: "A", name: "A"},
96-
{id: "B", name: "B"},
97-
],
98-
}
99-
cb(null, containers)
100-
},
90+
listContainers: () => ({
91+
byPage: () => ({
92+
async *[Symbol.asyncIterator]() {
93+
yield {
94+
containerItems: [
95+
{id: "A", name: "A"},
96+
{id: "B", name: "B"},
97+
],
98+
}
99+
},
100+
}),
101+
}),
101102
}))
102103

103104
const request = new Hub.ActionRequest()
@@ -130,13 +131,15 @@ describe(`${action.constructor.name} unit tests`, () => {
130131
it("returns form error if no containers are present", (done) => {
131132
const stubClient = sinon.stub(action as any, "azureClientFromRequest")
132133
.callsFake(() => ({
133-
listContainersSegmented: (filter: any, cb: (err: any, res: any) => void) => {
134-
chai.expect(filter).to.equal(null)
135-
const containers = {
136-
entries: [],
137-
}
138-
cb(null, containers)
139-
},
134+
listContainers: () => ({
135+
byPage: () => ({
136+
async *[Symbol.asyncIterator]() {
137+
yield {
138+
containerItems: [],
139+
}
140+
},
141+
}),
142+
}),
140143
}))
141144

142145
const request = new Hub.ActionRequest()

0 commit comments

Comments
 (0)