Skip to content

Commit d77c967

Browse files
authored
[Components] azure_storage - new components (#15561)
1 parent 41c3dce commit d77c967

File tree

15 files changed

+634
-6
lines changed

15 files changed

+634
-6
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import app from "../../azure_storage.app.mjs";
2+
3+
export default {
4+
key: "azure_storage-create-container",
5+
name: "Create Container",
6+
description: "Creates a new container under the specified account. If a container with the same name already exists, the operation fails. [See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/create-container?tabs=microsoft-entra-id).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
12+
info: {
13+
type: "alert",
14+
alertType: "info",
15+
content: "The name of your container can include only lowercase characters and needs to follow [these naming rules](https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names).",
16+
},
17+
containerName: {
18+
type: "string",
19+
label: "Container Name",
20+
description: "The name of the container within the specified storage account.",
21+
},
22+
},
23+
methods: {
24+
createContainer({
25+
containerName, params, ...args
26+
} = {}) {
27+
return this.app.put({
28+
...args,
29+
path: `/${containerName}`,
30+
params: {
31+
...params,
32+
restype: "container",
33+
},
34+
});
35+
},
36+
},
37+
async run({ $ }) {
38+
const {
39+
createContainer,
40+
containerName,
41+
} = this;
42+
43+
await createContainer({
44+
$,
45+
containerName,
46+
});
47+
48+
$.export("$summary", "Successfully created container.");
49+
50+
return {
51+
success: true,
52+
};
53+
},
54+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import app from "../../azure_storage.app.mjs";
2+
3+
export default {
4+
key: "azure_storage-delete-blob",
5+
name: "Delete Blob",
6+
description: "Deletes a specific blob from a container in Azure Storage. [See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/delete-blob?tabs=microsoft-entra-id).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
12+
info: {
13+
type: "alert",
14+
alertType: "info",
15+
content: "In order to have the right permissions to use this feature you need to go to the Azure Console in `Storage Account > IAM > Add role assignment`, and add the special permissions for this type of request:\n - Storage Blob Data Contributor\n - Storage Queue Data Contributor\n [See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/delete-blob?tabs=microsoft-entra-id#permissions).",
16+
},
17+
containerName: {
18+
propDefinition: [
19+
app,
20+
"containerName",
21+
],
22+
},
23+
blobName: {
24+
propDefinition: [
25+
app,
26+
"blobName",
27+
({ containerName }) => ({
28+
containerName,
29+
}),
30+
],
31+
},
32+
},
33+
methods: {
34+
deleteBlob({
35+
containerName, blobName,
36+
} = {}) {
37+
return this.app.delete({
38+
path: `/${containerName}/${blobName}`,
39+
});
40+
},
41+
},
42+
async run({ $ }) {
43+
const {
44+
deleteBlob,
45+
containerName,
46+
blobName,
47+
} = this;
48+
49+
await deleteBlob({
50+
$,
51+
containerName,
52+
blobName,
53+
});
54+
55+
$.export("$summary", "Successfully deleted blob.");
56+
return {
57+
success: true,
58+
};
59+
},
60+
};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import mime from "mime-types";
2+
import app from "../../azure_storage.app.mjs";
3+
import utils from "../../common/utils.mjs";
4+
5+
export default {
6+
key: "azure_storage-upload-blob",
7+
name: "Upload Blob",
8+
description: "Uploads a new blob to a specified container in Azure Storage. [See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob?tabs=microsoft-entra-id).",
9+
version: "0.0.1",
10+
type: "action",
11+
props: {
12+
app,
13+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
14+
info: {
15+
type: "alert",
16+
alertType: "info",
17+
content: "In order to have the right permissions to use this feature you need to go to the Azure Console in `Storage Account > IAM > Add role assignment`, and add the special permissions for this type of request:\n - Storage Blob Data Contributor\n - Storage Queue Data Contributor\n [See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob?tabs=microsoft-entra-id#permissions).",
18+
},
19+
containerName: {
20+
propDefinition: [
21+
app,
22+
"containerName",
23+
],
24+
},
25+
blobName: {
26+
type: "string",
27+
label: "Blob Name",
28+
description: "The name of the blob within the specified container.",
29+
},
30+
filePath: {
31+
type: "string",
32+
label: "File",
33+
description: "The file to be uploaded, please provide a file from `/tmp` Eg. `/tmp/my-file.txt`. To upload a file to `/tmp` folder, please follow the doc [here](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp)",
34+
},
35+
},
36+
methods: {
37+
uploadBlob({
38+
containerName, blobName, ...args
39+
} = {}) {
40+
return this.app.put({
41+
path: `/${containerName}/${blobName}`,
42+
...args,
43+
});
44+
},
45+
},
46+
async run({ $ }) {
47+
const {
48+
uploadBlob,
49+
containerName,
50+
blobName,
51+
filePath,
52+
} = this;
53+
54+
const data = utils.getDataFromFile(filePath);
55+
const fileName = utils.getFilenameFromPath(filePath);
56+
const contentType = mime.lookup(fileName) || "application/octet-stream";
57+
58+
await uploadBlob({
59+
$,
60+
containerName,
61+
blobName,
62+
data,
63+
headers: {
64+
"x-ms-blob-type": "BlockBlob",
65+
// "Content-Type": "text/plain; charset=UTF-8",
66+
"Content-Type": contentType,
67+
"x-ms-blob-content-disposition": `attachment; filename=${fileName}`,
68+
"x-ms-meta-m1": "v1",
69+
"x-ms-meta-m2": "v2",
70+
"Content-Length": data?.length,
71+
},
72+
});
73+
74+
$.export("$summary", "Successfully uploaded the blob.");
75+
return {
76+
success: true,
77+
};
78+
},
79+
};
Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,125 @@
1+
import { XMLParser } from "fast-xml-parser";
2+
import { axios } from "@pipedream/platform";
3+
import constants from "./common/constants.mjs";
4+
5+
const parser = new XMLParser({
6+
ignoreAttributes: false,
7+
arrayMode: true,
8+
textNodeName: "value",
9+
});
10+
111
export default {
212
type: "app",
313
app: "azure_storage",
4-
propDefinitions: {},
14+
propDefinitions: {
15+
containerName: {
16+
type: "string",
17+
label: "Container Name",
18+
description: "The name of the container within the specified storage account.",
19+
async options() {
20+
const { EnumerationResults: { Containers: { Container: containers } } } =
21+
await this.listContainers();
22+
if (!containers) {
23+
return [];
24+
}
25+
return Array.isArray(containers)
26+
? containers.map(({ Name: value }) => value)
27+
: [
28+
containers.Name,
29+
];
30+
},
31+
},
32+
blobName: {
33+
type: "string",
34+
label: "Blob Name",
35+
description: "The name of the blob within the specified container.",
36+
async options({ containerName }) {
37+
const { EnumerationResults: { Blobs: { Blob: blobs } } } = await this.listBlobs({
38+
containerName,
39+
});
40+
if (!blobs) {
41+
return [];
42+
}
43+
return Array.isArray(blobs)
44+
? blobs.map(({ Name: value }) => value)
45+
: [
46+
blobs.Name,
47+
];
48+
},
49+
},
50+
},
551
methods: {
6-
// this.$auth contains connected account data
7-
authKeys() {
8-
console.log(Object.keys(this.$auth));
52+
getUrl(path) {
53+
const { storage_account_name: storageAccount } = this.$auth;
54+
const baseUrl = constants.BASE_URL
55+
.replace(constants.STORAGE_ACCOUNT_PLACEHOLDER, storageAccount);
56+
return `${baseUrl}${path}`;
57+
},
58+
getHeaders(headers) {
59+
return {
60+
...headers,
61+
"x-ms-date": new Date().toUTCString(),
62+
"x-ms-version": constants.API_VERSION,
63+
"Authorization": `Bearer ${this.$auth.oauth_access_token}`,
64+
};
65+
},
66+
async _makeRequest({
67+
$ = this, path, headers, jsonOutput = true, ...args
68+
} = {}) {
69+
let response;
70+
try {
71+
response = await axios($, {
72+
...args,
73+
url: this.getUrl(path),
74+
headers: this.getHeaders(headers),
75+
});
76+
77+
} catch (error) {
78+
const errorResponse = parser.parse(error.response.data);
79+
if (errorResponse.Error) {
80+
throw new Error(JSON.stringify(errorResponse.Error, null, 2));
81+
}
82+
throw error;
83+
}
84+
85+
return jsonOutput
86+
? parser.parse(response)
87+
: response;
88+
},
89+
put(args = {}) {
90+
return this._makeRequest({
91+
method: "PUT",
92+
...args,
93+
});
94+
},
95+
delete(args = {}) {
96+
return this._makeRequest({
97+
method: "DELETE",
98+
...args,
99+
});
100+
},
101+
listContainers(args = {}) {
102+
return this._makeRequest({
103+
...args,
104+
path: "/",
105+
params: {
106+
...args.params,
107+
comp: "list",
108+
},
109+
});
110+
},
111+
listBlobs({
112+
containerName, ...args
113+
} = {}) {
114+
return this._makeRequest({
115+
...args,
116+
path: `/${containerName}`,
117+
params: {
118+
...args.params,
119+
restype: "container",
120+
comp: "list",
121+
},
122+
});
9123
},
10124
},
11125
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const STORAGE_ACCOUNT_PLACEHOLDER = "{storageAccount}";
2+
const BASE_URL = `https://${STORAGE_ACCOUNT_PLACEHOLDER}.blob.core.windows.net`;
3+
const API_VERSION = "2025-01-05";
4+
5+
export default {
6+
STORAGE_ACCOUNT_PLACEHOLDER,
7+
BASE_URL,
8+
API_VERSION,
9+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import fs from "fs";
2+
import { ConfigurationError } from "@pipedream/platform";
3+
4+
function checkTmp(filePath) {
5+
const adjustedPath = filePath.startsWith("/tmp")
6+
? filePath
7+
: `/tmp/${filePath}`;
8+
9+
if (!fs.existsSync(adjustedPath)) {
10+
throw new ConfigurationError("File does not exist!");
11+
}
12+
13+
return adjustedPath;
14+
}
15+
16+
function getDataFromFile(filePath) {
17+
const path = checkTmp(filePath);
18+
const file = fs.readFileSync(path);
19+
return file;
20+
}
21+
22+
function getFilenameFromPath(filePath) {
23+
const pathParts = filePath.split("/");
24+
return pathParts[pathParts.length - 1];
25+
}
26+
27+
export default {
28+
getDataFromFile,
29+
getFilenameFromPath,
30+
};

components/azure_storage/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/azure_storage",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Pipedream Azure Storage Components",
55
"main": "azure_storage.app.mjs",
66
"keywords": [
@@ -11,5 +11,9 @@
1111
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "^3.0.3",
17+
"mime-types": "^2.1.35"
1418
}
1519
}

0 commit comments

Comments
 (0)