Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Components] azure_storage - new components #15561

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jcortes
Copy link
Collaborator

@jcortes jcortes commented Feb 11, 2025

WHY

Resolves #15177

Summary by CodeRabbit

  • New Features

    • Introduced operations to create containers, upload blobs, and delete blobs from Azure Storage.
    • Added event sources that notify users on blob and container creation and deletion.
    • Enhanced integration with dynamic property settings, improved request handling, and robust error management.
    • Implemented a polling mechanism for continuous event monitoring.
    • Added utility functions for file handling and constants for Azure Storage configuration.
  • Chores

    • Updated the package version and integrated new external dependencies for platform support.

@jcortes jcortes added action New Action Request trigger / source New trigger / source request labels Feb 11, 2025
@jcortes jcortes self-assigned this Feb 11, 2025
Copy link

vercel bot commented Feb 11, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Ignored (Inspect) Visit Preview Feb 13, 2025 3:20pm
pipedream-docs ⬜️ Ignored (Inspect) Feb 13, 2025 3:20pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Feb 13, 2025 3:20pm

Copy link
Contributor

coderabbitai bot commented Feb 11, 2025

Walkthrough

This pull request introduces a set of new modules for Azure Storage operations, including actions for creating containers, uploading blobs, and deleting blobs. Event sources for new container creation, new blob creation, and blob deletion have also been added. The core Azure Storage application module has been enhanced with XML parsing and robust HTTP request handling, alongside new constants and utility functions. Additionally, the package configuration has been updated to reflect the new version and dependencies.

Changes

File(s) Change Summary
components/azure_storage/actions/.../create-container.mjs
components/azure_storage/actions/.../delete-blob.mjs
components/azure_storage/actions/.../upload-blob.mjs
Added new action modules for creating a container, deleting a blob, and uploading a blob; each defines metadata, properties, and a run method that calls underlying API methods.
components/azure_storage/azure_storage.app.mjs
components/azure_storage/common/constants.mjs
components/azure_storage/common/utils.mjs
Enhanced the core Azure Storage module with XML parsing, robust HTTP request handling, dynamic property definitions, constants for configuration, and file handling utilities.
components/azure_storage/package.json Updated package version from 0.0.1 to 0.1.0; added dependency on @pipedream/platform (^3.0.3).
components/azure_storage/sources/...
(includes blob-deleted, new-blob-created, new-container-created, common/polling, and associated test-event files)
Added new event source modules for blob deletion, new blob creation, and new container creation, including polling mechanisms and sample event payloads.

Sequence Diagram(s)

sequenceDiagram
    participant R as Runner
    participant A as Action Module
    participant App as AzureApp
    participant API as Azure Storage API

    R->>A: call run()
    A->>App: invoke createContainer / deleteBlob / uploadBlob
    App->>API: send HTTP request (PUT/DELETE)
    API-->>App: return response
    App-->>A: return success
    A-->>R: return confirmation
Loading
sequenceDiagram
    participant P as Poller
    participant App as AzureApp
    participant API as Azure Storage API
    participant E as Event Emitter

    P->>App: call listContainers / listBlobs
    App->>API: send GET request
    API-->>App: return resource list
    App-->>P: provide resources
    P->>E: emit event with metadata
Loading

Suggested reviewers

  • michelle0927

Poem

In the azure meadows where data streams,
I hop through code with joyful schemes.
Containers and blobs, created or deleted,
Every action harmoniously completed.
A little bunny cheers with a happy beat! 🐰

"Hop on, code on!"

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (8)
components/azure_storage/azure_storage.app.mjs (1)

19-28: Guard against undefined containers.
If containers is undefined or empty, this destructuring may fail or the fallback logic could produce unexpected results. Consider adding a check before mapping.

 const { EnumerationResults: { Containers: { Container: containers } } } =
   await this.listContainers();
+ if (!containers) {
+   return [];
+ }
 return Array.isArray(containers)
   ? containers.map(({ Name: value }) => value)
   : [containers.Name];
components/azure_storage/common/utils.mjs (1)

22-25: Add edge case handling to getFilenameFromPath.

The function should handle edge cases like empty paths or paths without filenames.

 function getFilenameFromPath(filePath) {
+  if (!filePath) {
+    throw new Error("File path cannot be empty");
+  }
   const pathParts = filePath.split("/");
-  return pathParts[pathParts.length - 1];
+  const filename = pathParts[pathParts.length - 1];
+  if (!filename) {
+    throw new Error("Invalid file path: no filename found");
+  }
+  return filename;
 }
components/azure_storage/sources/blob-deleted/test-event.mjs (2)

5-7: Use current or past dates in test data.

The test event uses future dates (2025) which could cause confusion. Consider using current or past dates to make the test data more realistic.

-    "Creation-Time": "Tue, 11 Feb 2025 23:05:34 GMT",
-    "Last-Modified": "Tue, 11 Feb 2025 23:05:34 GMT",
+    "Creation-Time": "Tue, 11 Feb 2024 23:05:34 GMT",
+    "Last-Modified": "Tue, 11 Feb 2024 23:05:34 GMT",

1-24: Add JSDoc documentation for the test event object.

Consider adding JSDoc documentation to describe the purpose and structure of the test event.

+/**
+ * Sample event for a deleted blob in Azure Storage.
+ * This object represents the metadata and properties of a deleted blob,
+ * used for testing the blob-deleted event source.
+ * @type {Object}
+ */
 export default {
components/azure_storage/sources/new-container-created/new-container-created.mjs (1)

32-38: Add timestamp validation in generateMeta.

The function should validate and normalize the timestamp.

 generateMeta(resource) {
+  if (!resource?.Name) {
+    throw new Error("Invalid resource: missing Name");
+  }
+  const timestamp = Date.now();
+  if (isNaN(timestamp)) {
+    throw new Error("Invalid timestamp generated");
+  }
   return {
     id: resource.Name,
     summary: `New Container: ${resource.Name}`,
-    ts: Date.now(),
+    ts: timestamp,
   };
 },
components/azure_storage/sources/new-blob-created/new-blob-created.mjs (1)

42-48: Use ISO timestamp for better precision and timezone support.

Using Date.now() for the timestamp loses timezone information and may cause issues with event ordering.

Consider using ISO format:

 generateMeta(resource) {
   return {
     id: resource.Name,
     summary: `New Blob: ${resource.Name}`,
-    ts: Date.now(),
+    ts: new Date().toISOString(),
   };
 },
components/azure_storage/actions/upload-blob/upload-blob.mjs (2)

12-17: Consider improving the info alert formatting.

The alert content could be more readable with proper markdown formatting.

-      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).",
+      content: "To set up the required permissions:\n\n1. Navigate to Azure Console > Storage Account > IAM > Add role assignment\n2. Add the following special permissions:\n   - Storage Blob Data Contributor\n   - Storage Queue Data Contributor\n\n[See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob?tabs=microsoft-entra-id#permissions) for more details.",

61-68: Consider making metadata headers configurable.

The headers include hardcoded metadata values (x-ms-meta-m1: v1, x-ms-meta-m2: v2). Consider making these configurable through props.

+    metadata: {
+      type: "object",
+      label: "Metadata",
+      description: "Custom metadata key-value pairs to set on the blob.",
+      optional: true,
+    },

Then in the run method:

       headers: {
         "x-ms-blob-type": "BlockBlob",
         "Content-Type": "text/plain; charset=UTF-8",
         "x-ms-blob-content-disposition": `attachment; filename=${fileName}`,
-        "x-ms-meta-m1": "v1",
-        "x-ms-meta-m2": "v2",
         "Content-Length": data?.length,
+        ...(this.metadata && Object.entries(this.metadata).reduce((acc, [key, value]) => ({
+          ...acc,
+          [`x-ms-meta-${key}`]: value,
+        }), {})),
       },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e51b8b1 and 212e8c3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • components/azure_storage/actions/create-container/create-container.mjs (1 hunks)
  • components/azure_storage/actions/delete-blob/delete-blob.mjs (1 hunks)
  • components/azure_storage/actions/upload-blob/upload-blob.mjs (1 hunks)
  • components/azure_storage/azure_storage.app.mjs (1 hunks)
  • components/azure_storage/common/constants.mjs (1 hunks)
  • components/azure_storage/common/utils.mjs (1 hunks)
  • components/azure_storage/package.json (2 hunks)
  • components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1 hunks)
  • components/azure_storage/sources/blob-deleted/test-event.mjs (1 hunks)
  • components/azure_storage/sources/common/polling.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/test-event.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/new-container-created.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/test-event.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/azure_storage/sources/new-blob-created/test-event.mjs
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (13)
components/azure_storage/azure_storage.app.mjs (7)

1-4: Imports look good.
These dependencies are essential for XML parsing and HTTP requests. No issues spotted here.


5-9: Confirm arrayMode configuration.
Setting arrayMode: true causes all XML nodes to parse as arrays. Ensure this is the intended behavior to simplify subsequent parsing logic.


52-59: Verify authorization approach.
Using a direct OAuth Bearer token with Azure Blob Storage is atypical. Usually, a shared access signature (SAS) or shared key is used. Confirm your OAuth credentials are valid for these calls.


83-88: PUT method logic is succinct.
No issues identified. This wrapper clearly delegates to _makeRequest.


89-94: DELETE method name is acceptable.
While naming a method delete can be confusing in some contexts, here it’s contained and unambiguous. Implementation looks good.


95-104: listContainers usage is correct.
Passing comp: "list" is standard for enumerating Azure containers.


105-117: listBlobs method is consistent with Azure standards.
Setting restype=container and comp=list is the correct approach for listing blobs.

components/azure_storage/common/constants.mjs (1)

1-9: Confirm the API version.
"2025-01-05" may not be a valid or officially supported Azure Storage API version. Verify if this version is recognized or if a stable version like "2021-12-02" should be used.

✅ Verification successful

🌐 Web query:

Is "2025-01-05" a valid Azure Storage API version?

💡 Result:

Yes, "2025-01-05" is a valid Azure Storage API version based on the evidence from multiple sources:

  1. Current recommended version:
    Microsoft explicitly states in their documentation that 2025-01-05 is the current Azure Storage service version as of October 2024[3][7]. This version includes features like:

    • Provisioned v2 billing model support for Azure Files[3][4]
    • Binary format specification for file permissions during copy operations[3]
  2. SDK/Client Library Support:
    The azure-storage-file-share Python package added support for this version in its 12.20.0b1 release (October 2024)[2].

  3. Tooling Compatibility:
    Azurite (Azure Storage emulator) specifically added support for 2025-01-05 in its November 2024 release[6][10].

  4. Feature Documentation:
    Multiple REST API endpoints like:

    • Create Share[4]
    • Get Share Properties[8]
      explicitly mention 2025-01-05 as a supported version.

The documentation recommends always using the latest version (2025-01-05) to benefit from performance optimizations and new features[1][3].

Citations:


API Version is Valid

The "2025-01-05" version is confirmed as a valid Azure Storage API version as per the latest Microsoft documentation and associated sources. No changes are needed.

components/azure_storage/sources/new-container-created/test-event.mjs (1)

1-14: Test event structure is clearly defined.
This static object is fine for testing, but ensure it remains updated if the production schema changes.

components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1)

48-54: Maintain consistency with new-blob-created timestamp format.

For consistency with the previous recommendation, the timestamp should use ISO format here as well.

components/azure_storage/actions/upload-blob/upload-blob.mjs (1)

1-9: LGTM! Component metadata and imports are well-structured.

The component is well-documented with clear name, description, and version. The documentation link is properly included.

components/azure_storage/package.json (2)

3-3: Version bump looks appropriate.

The version bump from 0.0.1 to 0.1.0 aligns with the addition of new features.


15-17: Verify the platform dependency version.

Please ensure that version 3.0.3 of @pipedream/platform is the latest stable version.

✅ Verification successful

🌐 Web query:

What is the latest version of @pipedream/platform?

💡 Result:

The latest version of @pipedream/platform is 3.0.3, as confirmed by its npm registry entry updated in September 2024[6][8]. This version includes type definitions and runtime type checking for Pipedream platform globals, with usage examples available in its documentation[6][8]. For security and compatibility, always verify you’re using this version via npm update @pipedream/platform.

Citations:


@pipedream/platform Version Verified

The npm registry confirms that version 3.0.3 is the latest stable release, so the dependency in your package.json is correct.

@jcortes jcortes force-pushed the azure-storage-new-components branch from 212e8c3 to 911b4e3 Compare February 11, 2025 23:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1)

34-36: ⚠️ Potential issue

Strengthen deleted status check.

The current implementation might miss edge cases where resource is null/undefined.

Apply this diff to add explicit type checking:

 isResourceRelevant(resource) {
-  return resource?.Deleted === true;
+  return Boolean(resource) && resource.Deleted === true;
 },
components/azure_storage/azure_storage.app.mjs (1)

85-87: ⚠️ Potential issue

Fix XML parsing of response data.

The current implementation attempts to parse the entire response object instead of its data property.

Apply this diff to fix the parsing:

 return jsonOutput
-  ? parser.parse(response)
+  ? parser.parse(response.data)
   : response;
🧹 Nitpick comments (1)
components/azure_storage/sources/blob-deleted/test-event.mjs (1)

5-6: Update test event timestamps to use realistic dates.

The test event uses future dates (Feb 2025) for timestamps. This could cause confusion or test failures when the dates become past dates.

Use current or past dates relative to the current time:

-    "Creation-Time": "Tue, 11 Feb 2025 23:05:34 GMT",
-    "Last-Modified": "Tue, 11 Feb 2025 23:05:34 GMT",
+    "Creation-Time": "Tue, 11 Feb 2024 23:05:34 GMT",
+    "Last-Modified": "Tue, 11 Feb 2024 23:05:34 GMT",
-    "DeletedTime": "Tue, 11 Feb 2025 23:12:16 GMT",
+    "DeletedTime": "Tue, 11 Feb 2024 23:12:16 GMT",

Also applies to: 20-20

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 212e8c3 and 911b4e3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • components/azure_storage/actions/create-container/create-container.mjs (1 hunks)
  • components/azure_storage/actions/delete-blob/delete-blob.mjs (1 hunks)
  • components/azure_storage/actions/upload-blob/upload-blob.mjs (1 hunks)
  • components/azure_storage/azure_storage.app.mjs (1 hunks)
  • components/azure_storage/common/constants.mjs (1 hunks)
  • components/azure_storage/common/utils.mjs (1 hunks)
  • components/azure_storage/package.json (2 hunks)
  • components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1 hunks)
  • components/azure_storage/sources/blob-deleted/test-event.mjs (1 hunks)
  • components/azure_storage/sources/common/polling.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/test-event.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/new-container-created.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/test-event.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • components/azure_storage/sources/new-container-created/test-event.mjs
  • components/azure_storage/package.json
  • components/azure_storage/common/constants.mjs
  • components/azure_storage/sources/new-blob-created/test-event.mjs
  • components/azure_storage/actions/delete-blob/delete-blob.mjs
  • components/azure_storage/common/utils.mjs
  • components/azure_storage/sources/new-container-created/new-container-created.mjs
  • components/azure_storage/sources/common/polling.mjs
  • components/azure_storage/actions/create-container/create-container.mjs
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs
🧰 Additional context used
🪛 GitHub Check: Lint Code Base
components/azure_storage/sources/blob-deleted/blob-deleted.mjs

[warning] 7-7:
Source names should start with "New". See https://pipedream.com/docs/components/guidelines/#source-name

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components

GTFalcao
GTFalcao previously approved these changes Feb 12, 2025
Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
components/azure_storage/azure_storage.app.mjs (2)

58-65: ⚠️ Potential issue

Add OAuth token validation.

The authorization header is set without validating the OAuth token.

 getHeaders(headers) {
   return {
     ...headers,
     "x-ms-date": new Date().toUTCString(),
     "x-ms-version": constants.API_VERSION,
-    "Authorization": `Bearer ${this.$auth.oauth_access_token}`,
+    "Authorization": this.$auth.oauth_access_token 
+      ? `Bearer ${this.$auth.oauth_access_token}`
+      : throw new Error("OAuth token is required for authentication"),
   };
 },

66-88: ⚠️ Potential issue

Fix XML parsing of response data.

The response parsing is incorrect as it parses the entire response object instead of its data.

 return jsonOutput
-  ? parser.parse(response)
+  ? parser.parse(response.data)
   : response;
components/azure_storage/actions/delete-blob/delete-blob.mjs (2)

34-40: ⚠️ Potential issue

Add path validation and error handling in deleteBlob method.

The path construction lacks validation for special characters and spaces in blob/container names.

 deleteBlob({
   containerName, blobName,
 } = {}) {
+  if (!containerName || !blobName) {
+    throw new Error('Container name and blob name are required');
+  }
+  const encodedPath = `/${encodeURIComponent(containerName)}/${encodeURIComponent(blobName)}`;
   return this.app.delete({
-    path: `/${containerName}/${blobName}`,
+    path: encodedPath,
   });
 },

42-59: ⚠️ Potential issue

Improve error handling and response in run method.

The method should provide more detailed error information and handle specific error cases.

 async run({ $ }) {
+  try {
     const {
       deleteBlob,
       containerName,
       blobName,
     } = this;

     await deleteBlob({
       $,
       containerName,
       blobName,
     });

     $.export("$summary", "Successfully deleted blob.");
     return {
       success: true,
+      containerName,
+      blobName,
     };
+  } catch (error) {
+    $.export("$summary", `Failed to delete blob: ${error.message}`);
+    throw error;
+  }
 },
🧹 Nitpick comments (2)
components/azure_storage/azure_storage.app.mjs (1)

5-9: Consider refining parser configuration for better error handling.

The current parser configuration might need additional options for robustness:

  • Add isArray function to handle array detection more precisely
  • Add parseAttributeValue to handle boolean/number attributes
 const parser = new XMLParser({
   ignoreAttributes: false,
   arrayMode: true,
   textNodeName: "value",
+  isArray: (name) => ["Container", "Blob"].includes(name),
+  parseAttributeValue: true,
 });
components/azure_storage/actions/delete-blob/delete-blob.mjs (1)

11-16: Improve eslint configuration and info alert formatting.

  1. The eslint-disable comment should be more specific to the rules being disabled.
  2. The info alert content could be formatted better for readability.
-    // eslint-disable-next-line pipedream/props-label, pipedream/props-description
+    /* eslint-disable-next-line pipedream/props-label, pipedream/props-description */
     info: {
       type: "alert",
       alertType: "info",
-      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).",
+      content: [
+        "To set up the required permissions:",
+        "1. Navigate to Azure Console > Storage Account > IAM > Add role assignment",
+        "2. Add the following special permissions:",
+        "   - Storage Blob Data Contributor",
+        "   - Storage Queue Data Contributor",
+        "",
+        "[See the documentation](https://learn.microsoft.com/en-us/rest/api/storageservices/delete-blob?tabs=microsoft-entra-id#permissions)",
+      ].join("\n"),
     },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 911b4e3 and e0a2a42.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • components/azure_storage/actions/create-container/create-container.mjs (1 hunks)
  • components/azure_storage/actions/delete-blob/delete-blob.mjs (1 hunks)
  • components/azure_storage/actions/upload-blob/upload-blob.mjs (1 hunks)
  • components/azure_storage/azure_storage.app.mjs (1 hunks)
  • components/azure_storage/common/constants.mjs (1 hunks)
  • components/azure_storage/common/utils.mjs (1 hunks)
  • components/azure_storage/package.json (2 hunks)
  • components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1 hunks)
  • components/azure_storage/sources/blob-deleted/test-event.mjs (1 hunks)
  • components/azure_storage/sources/common/polling.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/test-event.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/new-container-created.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/test-event.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • components/azure_storage/sources/blob-deleted/test-event.mjs
  • components/azure_storage/sources/new-blob-created/test-event.mjs
  • components/azure_storage/actions/create-container/create-container.mjs
  • components/azure_storage/sources/new-container-created/new-container-created.mjs
  • components/azure_storage/sources/common/polling.mjs
  • components/azure_storage/common/constants.mjs
  • components/azure_storage/sources/blob-deleted/blob-deleted.mjs
  • components/azure_storage/package.json
  • components/azure_storage/sources/new-container-created/test-event.mjs
  • components/azure_storage/actions/upload-blob/upload-blob.mjs
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs
  • components/azure_storage/common/utils.mjs
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (3)
components/azure_storage/azure_storage.app.mjs (2)

14-50: Well-structured property definitions with proper null checks!

The implementation includes:

  • Clear type definitions and descriptions
  • Proper null checks before accessing nested properties
  • Consistent handling of single vs array responses

89-123: LGTM: Well-structured CRUD operations!

The implementation of PUT, DELETE, and LIST operations is clean and consistent:

  • Proper method encapsulation
  • Consistent parameter handling
  • Clear separation of concerns
components/azure_storage/actions/delete-blob/delete-blob.mjs (1)

1-8: LGTM! Well-documented component setup.

The component is properly configured with clear documentation and version information.

@jcortes jcortes force-pushed the azure-storage-new-components branch from e0a2a42 to 4d8d396 Compare February 13, 2025 15:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
components/azure_storage/common/utils.mjs (2)

4-14: ⚠️ Potential issue

Add path traversal protection to checkTmp.

The function should prevent path traversal attacks by validating that the final path does not escape the /tmp directory.

Apply this diff to add path traversal protection:

 function checkTmp(filePath) {
+  const normalizedPath = path.normalize(filePath);
+  if (normalizedPath.includes('..')) {
+    throw new ConfigurationError("Path traversal is not allowed!");
+  }
+
   const adjustedPath = filePath.startsWith("/tmp")
     ? filePath
     : `/tmp/${filePath}`;

   if (!fs.existsSync(adjustedPath)) {
     throw new ConfigurationError("File does not exist!");
   }

   return adjustedPath;
 }

16-20: ⚠️ Potential issue

Improve error handling and performance in getDataFromFile.

The function has two issues:

  1. Uses synchronous file reading which can block the event loop
  2. Missing error handling for file read operation

Apply this diff to improve error handling and make the operation asynchronous:

-function getDataFromFile(filePath) {
+async function getDataFromFile(filePath) {
   const path = checkTmp(filePath);
-  const file = fs.readFileSync(path);
-  return file;
+  try {
+    const file = await fs.promises.readFile(path);
+    return file;
+  } catch (error) {
+    throw new Error(`Failed to read file: ${error.message}`);
+  }
 }
🧹 Nitpick comments (3)
components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1)

49-55: Use blob's deletion time instead of current time.

The generateMeta method uses Date.now() which might not reflect the actual deletion time of the blob. Consider using the blob's Deleted timestamp if available.

 generateMeta(resource) {
   return {
     id: resource.Name,
     summary: `Blob Deleted: ${resource.Name}`,
-    ts: Date.now(),
+    ts: resource.Properties?.['Deleted-Time'] 
+      ? new Date(resource.Properties['Deleted-Time']).getTime()
+      : Date.now(),
   };
 },
components/azure_storage/azure_storage.app.mjs (1)

5-9: Consider moving parser instance inside _makeRequest.

Creating a global parser instance might lead to memory leaks in long-running applications. Consider moving the parser creation inside the _makeRequest method where it's used.

-const parser = new XMLParser({
-  ignoreAttributes: false,
-  arrayMode: true,
-  textNodeName: "value",
-});

 export default {
   // ...
   methods: {
     async _makeRequest({ $ = this, path, headers, jsonOutput = true, ...args } = {}) {
+      const parser = new XMLParser({
+        ignoreAttributes: false,
+        arrayMode: true,
+        textNodeName: "value",
+      });
       // ... rest of the method
     }
   }
 }
components/azure_storage/package.json (1)

15-18: Consider using exact versions and adding missing dependencies.

  1. Use exact versions for better reproducibility
  2. Add fast-xml-parser as it's used in the app module
 "dependencies": {
-  "@pipedream/platform": "^3.0.3",
-  "mime-types": "^2.1.35"
+  "@pipedream/platform": "3.0.3",
+  "fast-xml-parser": "4.3.2",
+  "mime-types": "2.1.35"
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0a2a42 and 4d8d396.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • components/azure_storage/actions/create-container/create-container.mjs (1 hunks)
  • components/azure_storage/actions/delete-blob/delete-blob.mjs (1 hunks)
  • components/azure_storage/actions/upload-blob/upload-blob.mjs (1 hunks)
  • components/azure_storage/azure_storage.app.mjs (1 hunks)
  • components/azure_storage/common/constants.mjs (1 hunks)
  • components/azure_storage/common/utils.mjs (1 hunks)
  • components/azure_storage/package.json (2 hunks)
  • components/azure_storage/sources/blob-deleted/blob-deleted.mjs (1 hunks)
  • components/azure_storage/sources/blob-deleted/test-event.mjs (1 hunks)
  • components/azure_storage/sources/common/polling.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs (1 hunks)
  • components/azure_storage/sources/new-blob-created/test-event.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/new-container-created.mjs (1 hunks)
  • components/azure_storage/sources/new-container-created/test-event.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • components/azure_storage/sources/new-container-created/test-event.mjs
  • components/azure_storage/sources/new-blob-created/test-event.mjs
  • components/azure_storage/actions/upload-blob/upload-blob.mjs
  • components/azure_storage/sources/blob-deleted/test-event.mjs
  • components/azure_storage/actions/delete-blob/delete-blob.mjs
  • components/azure_storage/common/constants.mjs
  • components/azure_storage/sources/common/polling.mjs
  • components/azure_storage/sources/new-blob-created/new-blob-created.mjs
  • components/azure_storage/actions/create-container/create-container.mjs
  • components/azure_storage/sources/new-container-created/new-container-created.mjs
🧰 Additional context used
🪛 GitHub Check: Lint Code Base
components/azure_storage/sources/blob-deleted/blob-deleted.mjs

[warning] 7-7:
Source names should start with "New". See https://pipedream.com/docs/components/guidelines/#source-name

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (2)
components/azure_storage/sources/blob-deleted/blob-deleted.mjs (2)

34-36: Strengthen deleted status check.

The current implementation might miss edge cases where resource is null/undefined.

 isResourceRelevant(resource) {
-  return resource?.Deleted === true;
+  return Boolean(resource) && resource.Deleted === true;
 },

6-8:

✅ Verification successful

Update source name to follow guidelines.

According to the Pipedream documentation, source names should start with "New". Consider renaming to "New Blob Deleted".


🌐 Web query:

What is the naming convention for Pipedream source components?

💡 Result:

Pipedream source components follow specific naming conventions to ensure clarity and consistency across their registry. According to the guidelines:

Source names should:

  • Be singular and title-cased (e.g., "New File" instead of "New Files")
  • Start with "New" when emitting events about newly created/added items (e.g., "New Shared Drive")
  • Avoid including app names redundantly if already part of their key property
  • Not be slugified (e.g., use spaces instead of hyphens)

Examples from official sources:

  • New Shared Drive (for Google Drive)[2]
  • New Files (Instant) (Google Drive)[3]
  • New Order (Shopify example from video content)[7]

For component keys in source code:

  • Use past-tense verbs describing events (e.g., linear_app-issue-created-instant)[2]
  • Follow slugified patterns like app_name_slug-slugified-component-name[2]

These conventions help users quickly identify component functionality while maintaining consistency across Pipedream's ecosystem[2][3][7].

Citations:


Action Required: Update Source Name to Conform to Guidelines

Based on the Pipedream naming conventions, source names must be singular, title-cased, and prefixed with “New” when appropriate. Please update the source name in components/azure_storage/sources/blob-deleted/blob-deleted.mjs from:

  name: "Blob Deleted",

to

  name: "New Blob Deleted",

This change will ensure consistency with the guidelines outlined in the Pipedream documentation.

🧰 Tools
🪛 GitHub Check: Lint Code Base

[warning] 7-7:
Source names should start with "New". See https://pipedream.com/docs/components/guidelines/#source-name

Comment on lines +22 to +25
function getFilenameFromPath(filePath) {
const pathParts = filePath.split("/");
return pathParts[pathParts.length - 1];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use path.basename for robust filename extraction.

The current implementation may fail with Windows-style paths or paths with trailing slashes. Use Node's built-in path.basename for more robust filename extraction.

Apply this diff to improve the implementation:

+import path from "path";
+
 function getFilenameFromPath(filePath) {
-  const pathParts = filePath.split("/");
-  return pathParts[pathParts.length - 1];
+  return path.basename(filePath);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function getFilenameFromPath(filePath) {
const pathParts = filePath.split("/");
return pathParts[pathParts.length - 1];
}
import path from "path";
function getFilenameFromPath(filePath) {
return path.basename(filePath);
}

Comment on lines +23 to +33
getResourcesFromResponse(response) {
const resources = response.EnumerationResults.Blobs.Blob;
if (!resources) {
return [];
}
return Array.isArray(resources)
? resources
: [
resources,
];
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for XML parsing.

The getResourcesFromResponse method should handle cases where EnumerationResults or Blobs might be undefined.

 getResourcesFromResponse(response) {
+  if (!response?.EnumerationResults?.Blobs) {
+    return [];
+  }
   const resources = response.EnumerationResults.Blobs.Blob;
   if (!resources) {
     return [];
   }
   return Array.isArray(resources)
     ? resources
     : [
       resources,
     ];
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getResourcesFromResponse(response) {
const resources = response.EnumerationResults.Blobs.Blob;
if (!resources) {
return [];
}
return Array.isArray(resources)
? resources
: [
resources,
];
},
getResourcesFromResponse(response) {
if (!response?.EnumerationResults?.Blobs) {
return [];
}
const resources = response.EnumerationResults.Blobs.Blob;
if (!resources) {
return [];
}
return Array.isArray(resources)
? resources
: [
resources,
];
},

Comment on lines +66 to +88
async _makeRequest({
$ = this, path, headers, jsonOutput = true, ...args
} = {}) {
let response;
try {
response = await axios($, {
...args,
url: this.getUrl(path),
headers: this.getHeaders(headers),
});

} catch (error) {
const errorResponse = parser.parse(error.response.data);
if (errorResponse.Error) {
throw new Error(JSON.stringify(errorResponse.Error, null, 2));
}
throw error;
}

return jsonOutput
? parser.parse(response)
: response;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add rate limiting and retry logic.

The _makeRequest method should implement rate limiting and retry logic for transient failures. Consider using exponential backoff for retries.

 async _makeRequest({
   $ = this, path, headers, jsonOutput = true, ...args
 } = {}) {
   let response;
+  const maxRetries = 3;
+  const baseDelay = 1000;
+
+  for (let attempt = 0; attempt < maxRetries; attempt++) {
     try {
       response = await axios($, {
         ...args,
         url: this.getUrl(path),
         headers: this.getHeaders(headers),
       });
+      break;
     } catch (error) {
+      if (error.response?.status === 429 || 
+          (error.response?.status >= 500 && error.response?.status < 600)) {
+        if (attempt === maxRetries - 1) throw error;
+        await new Promise(resolve => 
+          setTimeout(resolve, baseDelay * Math.pow(2, attempt)));
+        continue;
+      }
       const errorResponse = parser.parse(error.response.data);
       if (errorResponse.Error) {
         throw new Error(JSON.stringify(errorResponse.Error, null, 2));
       }
       throw error;
     }
+  }
   return jsonOutput
     ? parser.parse(response)
     : response;
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async _makeRequest({
$ = this, path, headers, jsonOutput = true, ...args
} = {}) {
let response;
try {
response = await axios($, {
...args,
url: this.getUrl(path),
headers: this.getHeaders(headers),
});
} catch (error) {
const errorResponse = parser.parse(error.response.data);
if (errorResponse.Error) {
throw new Error(JSON.stringify(errorResponse.Error, null, 2));
}
throw error;
}
return jsonOutput
? parser.parse(response)
: response;
},
async _makeRequest({
$ = this, path, headers, jsonOutput = true, ...args
} = {}) {
let response;
const maxRetries = 3;
const baseDelay = 1000;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
response = await axios($, {
...args,
url: this.getUrl(path),
headers: this.getHeaders(headers),
});
break;
} catch (error) {
if (
error.response?.status === 429 ||
(error.response?.status >= 500 && error.response?.status < 600)
) {
if (attempt === maxRetries - 1) throw error;
await new Promise((resolve) =>
setTimeout(resolve, baseDelay * Math.pow(2, attempt))
);
continue;
}
const errorResponse = parser.parse(error.response.data);
if (errorResponse.Error) {
throw new Error(JSON.stringify(errorResponse.Error, null, 2));
}
throw error;
}
}
return jsonOutput ? parser.parse(response) : response;
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action New Action Request trigger / source New trigger / source request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Components] azure_storage
2 participants