Skip to content

Commit

Permalink
feat(azure-storage-blob): Added edge case tests and more verbose driv…
Browse files Browse the repository at this point in the history
…er properties
  • Loading branch information
itpropro committed Jan 28, 2025
1 parent 5a56d23 commit 2212e39
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 14 deletions.
43 changes: 34 additions & 9 deletions src/drivers/azure-storage-blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export interface AzureStorageBlobOptions {
/**
* The name of the Azure Storage account.
*/
accountName: string;
accountName?: string;

/**
* The name of the storage container. All entities will be stored in the same container.
* The name of the storage container. All entities will be stored in the same container. Will be created if it doesn't exist.
* @default "unstorage"
*/
containerName?: string;
Expand All @@ -24,41 +24,65 @@ export interface AzureStorageBlobOptions {
accountKey?: string;

/**
* The SAS key. If provided, the account key will be ignored.
* The SAS token. If provided, the account key will be ignored. Include at least read, list and write permissions to be able to list keys.
*/
sasKey?: string;

/**
* The SAS URL. If provided, the account key, SAS key and container name will be ignored.
*/
sasUrl?: string;

/**
* The connection string. If provided, the account key and SAS key will be ignored. Only available in Node.js runtime.
*/
connectionString?: string;

/**
* Storage account endpoint suffix. Need to be changed for Microsoft Azure operated by 21Vianet, Azure Government or Azurite.
* @default ".blob.core.windows.net"
*/
endpointSuffix?: string;
}

const DRIVER_NAME = "azure-storage-blob";

export default defineDriver((opts: AzureStorageBlobOptions) => {
let containerClient: ContainerClient;
const endpointSuffix = opts.endpointSuffix || ".blob.core.windows.net";
const getContainerClient = () => {
if (containerClient) {
return containerClient;
}
if (!opts.accountName) {
throw createError(DRIVER_NAME, "accountName");
if (!(opts.connectionString || opts.sasUrl) && !opts.accountName) {
throw createError(DRIVER_NAME, "missing accountName");
}
let serviceClient: BlobServiceClient;
if (opts.accountKey) {
// StorageSharedKeyCredential is only available in Node.js runtime, not in browsers
const credential = new StorageSharedKeyCredential(
opts.accountName,
opts.accountName!,
opts.accountKey
);
serviceClient = new BlobServiceClient(
`https://${opts.accountName}.blob.core.windows.net`,
`https://${opts.accountName}${endpointSuffix}`,
credential
);
} else if (opts.sasUrl) {
if (opts.containerName) {
containerClient = new ContainerClient(`${opts.sasUrl}`);
return containerClient;
}
serviceClient = new BlobServiceClient(opts.sasUrl);
} else if (opts.sasKey) {
if (opts.containerName) {
containerClient = new ContainerClient(
`https://${opts.accountName}${endpointSuffix}/${opts.containerName}?${opts.sasKey}`
);
return containerClient;
}
serviceClient = new BlobServiceClient(
`https://${opts.accountName}.blob.core.windows.net${opts.sasKey}`
`https://${opts.accountName}${endpointSuffix}?${opts.sasKey}`
);
} else if (opts.connectionString) {
// fromConnectionString is only available in Node.js runtime, not in browsers
Expand All @@ -68,13 +92,14 @@ export default defineDriver((opts: AzureStorageBlobOptions) => {
} else {
const credential = new DefaultAzureCredential();
serviceClient = new BlobServiceClient(
`https://${opts.accountName}.blob.core.windows.net`,
`https://${opts.accountName}${endpointSuffix}`,
credential
);
}
containerClient = serviceClient.getContainerClient(
opts.containerName || "unstorage"
);
containerClient.createIfNotExists();
return containerClient;
};

Expand Down
77 changes: 72 additions & 5 deletions test/drivers/azure-storage-blob.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,95 @@ import { describe, expect, beforeAll, afterAll, test } from "vitest";
import { readFile } from "../../src/drivers/utils/node-fs";
import driver from "../../src/drivers/azure-storage-blob";
import { testDriver } from "./utils";
import { BlobServiceClient } from "@azure/storage-blob";
import { AccountSASPermissions, BlobServiceClient } from "@azure/storage-blob";
import { ChildProcess, exec } from "node:child_process";
import { createStorage } from "../../src";

describe.skip("drivers: azure-storage-blob", () => {
let azuriteProcess: ChildProcess;
let sasUrl: string;
beforeAll(async () => {
azuriteProcess = exec("npx azurite-blob --silent");
azuriteProcess = exec("pnpm exec azurite-blob --silent");
const client = BlobServiceClient.fromConnectionString(
"UseDevelopmentStorage=true"
"UseDevelopmentStorage=true;"
);
const containerClient = client.getContainerClient("unstorage");
await containerClient.createIfNotExists();
sasUrl = client.generateAccountSasUrl(
new Date(Date.now() + 1000 * 60),
AccountSASPermissions.from({ read: true, list: true, write: true })
);
});
afterAll(() => {
azuriteProcess.kill(9);
});
testDriver({
driver: driver({
connectionString: "UseDevelopmentStorage=true",
accountName: "local",
connectionString: "UseDevelopmentStorage=true;",
accountName: "devstoreaccount1",
}),
additionalTests(ctx) {
test("no empty account name", async () => {
const invalidStorage = createStorage({
driver: driver({
accountKey: "UseDevelopmentStorage=true",
} as any),
});
await expect(
async () => await invalidStorage.hasItem("test")
).rejects.toThrowError("missing accountName");
});
test("sas key", async ({ skip }) => {
if (
!process.env.AZURE_STORAGE_BLOB_SAS_KEY ||
!process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME
) {
skip();
}
const storage = createStorage({
driver: driver({
sasKey: process.env.AZURE_STORAGE_BLOB_SAS_KEY,
accountName: process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME,
containerName: "unstorage",
}),
});
await storage.getKeys();
});
test("sas url", async () => {
const storage = createStorage({
driver: driver({
sasUrl,
}),
});
await storage.getKeys();
});
test("account key", async ({ skip }) => {
if (
!process.env.AZURE_STORAGE_BLOB_ACCOUNT_KEY ||
!process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME
) {
skip();
}
const storage = createStorage({
driver: driver({
accountName: process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME,
accountKey: process.env.AZURE_STORAGE_BLOB_ACCOUNT_KEY,
}),
});
await storage.getKeys();
});
test("use DefaultAzureCredential", async ({ skip }) => {
if (!process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME) {
skip();
}
const storage = createStorage({
driver: driver({
accountName: process.env.AZURE_STORAGE_BLOB_ACCOUNT_NAME,
containerName: "unstorage",
}),
});
await storage.getKeys();
});
test("properly encodes raw items", async () => {
const file = await readFile("./test/test.png");

Expand Down

0 comments on commit 2212e39

Please sign in to comment.