Skip to content

Commit e0a2a42

Browse files
committed
[Components] azure_storage - new components
1 parent 9485ca2 commit e0a2a42

File tree

15 files changed

+628
-9
lines changed

15 files changed

+628
-9
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: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import app from "../../azure_storage.app.mjs";
2+
import utils from "../../common/utils.mjs";
3+
4+
export default {
5+
key: "azure_storage-upload-blob",
6+
name: "Upload Blob",
7+
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).",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
app,
12+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
13+
info: {
14+
type: "alert",
15+
alertType: "info",
16+
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).",
17+
},
18+
containerName: {
19+
propDefinition: [
20+
app,
21+
"containerName",
22+
],
23+
},
24+
blobName: {
25+
type: "string",
26+
label: "Blob Name",
27+
description: "The name of the blob within the specified container.",
28+
},
29+
filePath: {
30+
type: "string",
31+
label: "File",
32+
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)",
33+
},
34+
},
35+
methods: {
36+
uploadBlob({
37+
containerName, blobName, ...args
38+
} = {}) {
39+
return this.app.put({
40+
path: `/${containerName}/${blobName}`,
41+
...args,
42+
});
43+
},
44+
},
45+
async run({ $ }) {
46+
const {
47+
uploadBlob,
48+
containerName,
49+
blobName,
50+
filePath,
51+
} = this;
52+
53+
const data = utils.getDataFromFile(filePath);
54+
const fileName = utils.getFilenameFromPath(filePath);
55+
56+
await uploadBlob({
57+
$,
58+
containerName,
59+
blobName,
60+
data,
61+
headers: {
62+
"x-ms-blob-type": "BlockBlob",
63+
"Content-Type": "text/plain; charset=UTF-8",
64+
"x-ms-blob-content-disposition": `attachment; filename=${fileName}`,
65+
"x-ms-meta-m1": "v1",
66+
"x-ms-meta-m2": "v2",
67+
"Content-Length": data?.length,
68+
},
69+
});
70+
71+
$.export("$summary", "Successfully uploaded the blob.");
72+
return {
73+
success: true,
74+
};
75+
},
76+
};
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, "utf8");
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 & 2 deletions
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,8 @@
1111
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "^3.0.3"
1417
}
15-
}
18+
}

0 commit comments

Comments
 (0)