Skip to content

17984 xero #18071

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open

17984 xero #18071

wants to merge 7 commits into from

Conversation

luancazarine
Copy link
Collaborator

@luancazarine luancazarine commented Aug 15, 2025

Resolves #17984

Summary by CodeRabbit

  • New Features

    • Added many Xero actions: create/list/update/delete tracking categories & options, bank transactions, payments, items, employees, file upload, and invoice-related operations.
  • Improvements

    • Centralized Xero client with unified calls, clearer success summaries, stricter validation, dynamic tenant selection, and richer UI labels.
  • Changes

    • Public inputs standardized to camelCase; multiple actions and sources version-bumped; one legacy "Find contact" action removed.
  • Maintenance

    • Consolidated request handling and added payload parsing and polling timer support for relevant sources.

…bumps and new features. Key changes include:

- Bumped version to 0.4.0 for the Xero Accounting API package.
- Added new actions for creating, updating, and deleting tracking categories and options.
- Enhanced existing actions with improved error handling and response formatting.
- Refactored code for better readability and maintainability, including consistent use of the xeroAccountingApi instance.
- Updated utility functions and constants for better modularity.

This update improves the overall functionality and usability of the Xero Accounting API integration.
@luancazarine luancazarine linked an issue Aug 15, 2025 that may be closed by this pull request
Copy link

vercel bot commented Aug 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
docs-v2 Ignored Ignored Preview Aug 18, 2025 7:54pm
pipedream-docs Ignored Ignored Aug 18, 2025 7:54pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 18, 2025 7:54pm

Copy link
Contributor

coderabbitai bot commented Aug 15, 2025

Walkthrough

Broad refactor migrating Xero actions/sources to a centralized xeroAccountingApi app client with object-parameter signatures, camelCase props, ConfigurationError validation, new parseObject util and named constants, many version bumps, and added tracking category CRUD (create/list/get/update/delete) and tracking option update/delete actions.

Changes

Cohort / File(s) Summary
App wrapper refactor and expansion
components/xero_accounting_api/xero_accounting_api.app.mjs
Centralized _makeRequest, use named BASE_URL, normalized object-argument method signatures, added many helper methods (create/get/update/delete for invoices, contacts, items, payments, tracking categories/options, reports, file upload, employees), and new propDefinitions (lineItems, trackingCategoryId, trackingOptionId, tenant-related helpers).
Common utilities and constants
components/xero_accounting_api/common/constants.mjs, components/xero_accounting_api/common/util.mjs
Convert constants to named exports (BASE_URL, DB_LAST_DATE_CHECK) and remove default export; add parseObject util and reorder exports.
Tracking categories: new & updated actions
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs, .../get-tracking-category/get-tracking-category.mjs, .../list-tracking-categories/list-tracking-categories.mjs, .../update-tracking-category/update-tracking-category.mjs, .../update-tracking-category-option/update-tracking-category-option.mjs, .../delete-tracking-category/delete-tracking-category.mjs, .../delete-tracking-category-option/delete-tracking-category-option.mjs
Added actions to create/list/get/update/delete tracking categories and to update/delete tracking options. create-tracking-category can also create options by parsing an options array; actions use xeroAccountingApi methods and propDefinitions, include summary exports, and throw ConfigurationError on API validation failures.
Action migrations to app client (HTTP → app methods, camelCase, object args)
Multiple files under components/xero_accounting_api/actions/* (e.g., create-bank-transaction, create-credit-note, create-history-note, create-item, create-payment, download-invoice, email-an-invoice, get-bank-statements-report, get-bank-summary, get-contact, get-history-of-changes, get-invoice-online-url, get-invoice, get-item, get-tenant-connections, list-contacts, list-credit-notes, list-invoices, list-manual-journals, make-an-api-call, upload-file, xero-accounting-create-employee, xero-accounting-create-or-update-contact, xero-accounting-update-contact, xero-create-purchase-bill, xero-create-sales-invoice)
Replaced raw axios HTTP calls with xeroAccountingApi app calls, renamed props to camelCase, introduced tenantId propDefinitions, switched to ConfigurationError for validation, added $.export("$summary", ...) in many actions, migrated payload keys to camelCase/TitleCase as needed, and bumped versions.
Call signature normalization on existing actions
components/xero_accounting_api/actions/add-line-item-to-invoice/*, .../create-bill/*, .../create-update-contact/*, .../find-contact/*, .../find-invoice/*, .../find-or-create-contact/*
Updated internal calls to the app’s new object-argument signatures (e.g., createInvoice({ $, tenantId, data }), getContact({ $, tenantId, queryParam })), minor import reorderings, version increments, and small error message formatting changes.
Sources updates
components/xero_accounting_api/sources/new-updated-contact/*, .../new-updated-invoice/*, .../webhook-event-received/*
Added timer polling prop defaults, moved imports, and changed getContact/getInvoice calls to object-argument signatures ({ tenantId, modifiedSince }), with version bumps.
Package version
components/xero_accounting_api/package.json
Version bumped 0.3.1 → 0.4.0; no dependency changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User Action
  participant Action as Create Tracking Category Action
  participant App as xeroAccountingApi App
  participant Xero as Xero API

  User->>Action: Provide tenantId, name, options[]
  Action->>App: createTrackingCategory({ tenantId, data:{ Name } })
  App->>Xero: PUT /api.xro/2.0/TrackingCategories
  Xero-->>App: TrackingCategoryID
  loop For each option (if provided)
    Action->>App: createTrackingOption({ tenantId, trackingCategoryId, data:{ Name } })
    App->>Xero: PUT /TrackingCategories/{id}/Options
    Xero-->>App: TrackingOptionID
  end
  App-->>Action: Combined category + options
  Action-->>User: Return response + $summary
Loading
sequenceDiagram
  autonumber
  participant Action as Update Tracking Option Action
  participant App as xeroAccountingApi App
  participant Xero as Xero API

  Action->>App: updateTrackingOption({ tenantId, trackingCategoryId, trackingOptionId, data:{ Name, Status } })
  App->>Xero: POST /TrackingCategories/{catId}/Options/{optId}
  Xero-->>App: Updated Option
  App-->>Action: Response
  Action-->>User: Export $summary
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90 minutes

Assessment against linked issues

Objective Addressed Explanation
Create tracking category options (#17984) create-tracking-category can create options from an options array, but there is no dedicated create-tracking-option action in the actions list.
Update tracking category options (#17984) update-tracking-category-option action exists and calls xeroAccountingApi.updateTrackingOption.
Delete tracking category options (#17984) delete-tracking-category-option action exists and calls xeroAccountingApi.deleteTrackingOption.
Get tracking category options (#17984) get-tracking-category action exists and returns the category including its Options.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs Migration to app client and prop renames — unrelated to tracking category option CRUD (implementation-wide refactor).
components/xero_accounting_api/actions/upload-file/upload-file.mjs File upload refactor to app wrapper and stream-to-buffer helper — not related to tracking category option functionality.
components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs Employee creation action rework — unrelated to tracking category option requirements.

"A rabbit taps keys with gleeful might,
New tracks in Xero spring to light —
Options hop, update, delete, create,
Categories lined in tidy state.
With app calls guiding every call,
We bound through code, ears up and tall. 🐇✨"

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 17984-xero

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 40

🔭 Outside diff range comments (6)
components/xero_accounting_api/actions/find-contact/find-contact.mjs (1)

99-107: Validate presence and exclusivity of search criteria

Currently, you only enforce mutual exclusivity when “Create a new contact if not found” is “No”, and there’s no guard when neither name nor accountNumber is provided. This can lead to fetching all contacts unintentionally or ambiguous matches when both fields are passed with creation enabled.

Recommend:

  • Always enforce mutual exclusivity (regardless of createContactIfNotFound).
  • Require at least one of name or accountNumber to be provided.

Apply this diff:

-    if (createContactIfNotFound === "No" && accountNumber && name) {
-      throw new ConfigurationError(
-        "Choose exclusively between Account Number or Name to find a contact.",
-      );
-    }
+    if (accountNumber && name) {
+      throw new ConfigurationError(
+        "Choose exclusively between Account Number or Name to find a contact.",
+      );
+    }
+    if (!accountNumber && !name) {
+      throw new ConfigurationError(
+        "Provide either Account Number or Name to find a contact.",
+      );
+    }
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs (1)

148-154: Address validation and payload bug: addressLine2 not considered and mis-assigned

  • addressEntered checks this.addressLine1 twice and never checks addressLine2. This can incorrectly skip validation when only line 2 is provided.
  • In the payload, AddressLine2 is assigned this.addressLine1 instead of this.addressLine2.

These cause incorrect validation and contact payload data.

Apply this diff:

-    const addressEntered = this.addressLine1
-      || this.addressLine1
+    const addressEntered = this.addressLine1
+      || this.addressLine2
       || this.city
       || this.region
       || this.postalCode
       || this.country;
-            AddressLine2: this.addressLine1,
+            AddressLine2: this.addressLine2,

Also applies to: 170-176

components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (1)

116-135: Payload still references old snake_case props — request will send mostly undefined fields

This breaks the action: nearly all fields map to undefined. Use the new camelCase props.

Apply this diff:

       data: {
-        Type: "ACCPAY", //ACCPAY = Purchase Bill
-        Contact: {
-          ContactID: this.contact_id,
-          Name: this.contact_name,
-        },
-        LineItems: this.line_items,
-        Date: this.date,
-        DueDate: this.due_date,
-        LineAmountTypes: this.line_amount_type,
-        InvoiceNumber: this.purchase_bill_number,
-        Reference: this.reference,
-        BrandingThemeID: this.branding_theme_id,
-        Url: this.url,
-        CurrencyCode: this.currency_code,
-        CurrencyRate: this.currency_rate,
-        Status: this.status,
-        SentToContact: this.sent_to_contact,
-        PlannedPaymentDate: this.planned_payment_date,
+        Type: "ACCPAY", // ACCPAY = Purchase Bill
+        Contact: {
+          ContactID: this.contactId,
+          Name: this.contactName,
+        },
+        LineItems: this.lineItems,
+        Date: this.date,
+        DueDate: this.dueDate,
+        LineAmountTypes: this.lineAmountType,
+        InvoiceNumber: this.purchaseBillNumber,
+        Reference: this.reference,
+        BrandingThemeID: this.brandingThemeId,
+        Url: this.url,
+        CurrencyCode: this.currencyCode,
+        CurrencyRate: this.currencyRate,
+        Status: this.status,
+        SentToContact: this.sentToContact,
+        PlannedPaymentDate: this.plannedPaymentDate,
       },
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)

196-223: Payload maps to snake_case props — data will be empty/incorrect

All fields should reference the newly introduced camelCase props. Current code sends undefineds.

Apply this diff:

       data: {
-        Name: this.name,
-        ContactID: this.contact_id,
-        ContactNumber: this.contact_number,
-        AccountNumber: this.account_number,
-        ContactStatus: this.contact_status,
-        FirstName: this.first_name,
-        LastName: this.last_name,
-        EmailAddress: this.email_address,
-        SkypeUserName: this.skype_user_name,
-        ContactPersons: this.contact_persons,
-        BankAccountDetails: this.bank_account_details,
-        TaxNumber: this.tax_number,
-        AccountsReceivableTaxType: this.account_receivable_tax_type,
-        AccountsPayableTaxType: this.account_payable_type,
-        Addresses: this.addresses,
-        Phones: this.phones,
-        IsSupplier: this.is_supplier,
-        IsCustomer: this.is_customer,
-        DefaultCurrency: this.default_currency,
-        XeroNetworkKey: this.xero_network_key,
-        SalesDefaultAccountCode: this.sales_default_account_code,
-        PurchasesDefaultAccountCode: this.puchases_default_account_code,
-        SalesTrackingCategories: this.sales_tracking_categories,
-        PurchasesTrackingCategories: this.puechases_tracking_categories,
-        TrackingCategoryName: this.tracking_category_name,
-        TrackingOptionName: this.tracking_option_name,
-        PaymentTerms: this.payment_terms,
+        Name: this.name,
+        ContactID: this.contactId,
+        ContactNumber: this.contactNumber,
+        AccountNumber: this.accountNumber,
+        ContactStatus: this.contactStatus,
+        FirstName: this.firstName,
+        LastName: this.lastName,
+        EmailAddress: this.emailAddress,
+        SkypeUserName: this.skypeUserName,
+        ContactPersons: this.contactPersons,
+        BankAccountDetails: this.bankAccountDetails,
+        TaxNumber: this.taxNumber,
+        AccountsReceivableTaxType: this.accountReceivableTaxType,
+        AccountsPayableTaxType: this.accountPayableType,
+        Addresses: this.addresses,
+        Phones: this.phones,
+        IsSupplier: this.isSupplier,
+        IsCustomer: this.isCustomer,
+        DefaultCurrency: this.defaultCurrency,
+        XeroNetworkKey: this.xeroNetworkKey,
+        SalesDefaultAccountCode: this.salesDefaultAccountCode,
+        PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,
+        SalesTrackingCategories: this.salesTrackingCategories,
+        PurchasesTrackingCategories: this.purchasesTrackingCategories,
+        TrackingCategoryName: this.trackingCategoryName,
+        TrackingOptionName: this.trackingOptionName,
+        PaymentTerms: this.paymentTerms,
       },
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (2)

196-229: Undefined variables in data payload

The data payload references old snake_case variables that no longer exist after the camelCase refactoring. This will cause the update to fail or send undefined values.

 data: {
   Name: this.name,
-  ContactID: this.contact_id,
-  ContactNumber: this.contact_number,
-  AccountNumber: this.account_number,
-  ContactStatus: this.contact_status,
-  FirstName: this.first_name,
-  LastName: this.last_name,
-  EmailAddress: this.email_address,
-  SkypeUserName: this.skype_user_name,
-  ContactPersons: this.contact_persons,
-  BankAccountDetails: this.bank_account_details,
-  TaxNumber: this.tax_number,
-  AccountsReceivableTaxType: this.account_receivable_tax_type,
-  AccountsPayableTaxType: this.account_payable_type,
+  ContactID: this.contactId,
+  ContactNumber: this.contactNumber,
+  AccountNumber: this.accountNumber,
+  ContactStatus: this.contactStatus,
+  FirstName: this.firstName,
+  LastName: this.lastName,
+  EmailAddress: this.emailAddress,
+  SkypeUserName: this.skypeUserName,
+  ContactPersons: this.contactPersons,
+  BankAccountDetails: this.bankAccountDetails,
+  TaxNumber: this.taxNumber,
+  AccountsReceivableTaxType: this.accountReceivableTaxType,
+  AccountsPayableTaxType: this.accountPayableType,
   Addresses: this.addresses,
   Phones: this.phones,
-  IsSupplier: this.is_supplier,
-  IsCustomer: this.is_customer,
-  DefaultCurrency: this.default_currency,
-  XeroNetworkKey: this.xero_network_key,
-  SalesDefaultAccountCode: this.sales_default_account_code,
-  PurchasesDefaultAccountCode: this.puchases_default_account_code,
-  SalesTrackingCategories: this.sales_tracking_categories,
-  PurchasesTrackingCategories: this.puechases_tracking_categories,
-  TrackingCategoryName: this.tracking_category_name,
-  TrackingOptionName: this.tracking_option_name,
-  PaymentTerms: this.payment_terms,
+  IsSupplier: this.isSupplier,
+  IsCustomer: this.isCustomer,
+  DefaultCurrency: this.defaultCurrency,
+  XeroNetworkKey: this.xeroNetworkKey,
+  SalesDefaultAccountCode: this.salesDefaultAccountCode,
+  PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,
+  SalesTrackingCategories: this.salesTrackingCategories,
+  PurchasesTrackingCategories: this.purchasesTrackingCategories,
+  TrackingCategoryName: this.trackingCategoryName,
+  TrackingOptionName: this.trackingOptionName,
+  PaymentTerms: this.paymentTerms,
 },

222-224: Additional typo in data payload

The data payload has typos that correspond to the property name typos: puchases_default_account_code should use the corrected property name, and puechases_tracking_categories should also be corrected.

This is already addressed in the previous fix for undefined variables, where we correct to:

  • PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode
  • PurchasesTrackingCategories: this.purchasesTrackingCategories
🧹 Nitpick comments (43)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (4)

107-107: Make the summary reflect create vs update

Currently always says “Contact created successfully” even when updating. Use a dynamic summary.

-    response && $.export("$summary", "Contact created successfully");
+    response && $.export("$summary", `Contact ${contactID ? "updated" : "created"} successfully`);

6-6: Remove trailing space from action name

Minor polish: drop the trailing space to avoid inconsistent display in the UI.

-  name: "Create or update contact ",
+  name: "Create or update contact",

37-37: Fix spacing before period

Grammar nit: remove the extra space before the period.

-      description: "First name of contact person .",
+      description: "First name of contact person.",

55-55: Fix double period and hyphenate “User-defined”

Minor grammar/style improvement.

-      description: "User defined account number..",
+      description: "User-defined account number.",
components/xero_accounting_api/actions/delete-tracking-category-option/delete-tracking-category-option.mjs (1)

49-51: Optional: return a consistent shape for delete actions

Delete endpoints often return 204/empty responses. Consider returning a simple success payload so downstream steps don’t have to handle undefined.

-      $.export("$summary", `Successfully deleted tracking category option with ID: ${this.trackingOptionId}`);
-      return response;
+      $.export("$summary", `Successfully deleted tracking category option with ID: ${this.trackingOptionId}`);
+      return {
+        success: true,
+        trackingOptionId: this.trackingOptionId,
+        data: response,
+      };
components/xero_accounting_api/actions/delete-tracking-category/delete-tracking-category.mjs (1)

5-9: Prefer Title Case for action name for consistency

Most actions in this package use Title Case for the name (e.g., “Delete Tracking Category Option”). Consider aligning.

Apply this small change:

-  name: "Delete tracking category",
+  name: "Delete Tracking Category",
components/xero_accounting_api/common/util.mjs (1)

111-135: Don’t drop falsy-but-valid values in parseObject

if (!obj) return undefined; treats 0 and false as “absent”. Prefer nullish check so valid falsy values are preserved.

Apply this minimal fix:

-const parseObject = (obj) => {
-  if (!obj) return undefined;
+const parseObject = (obj) => {
+  if (obj == null) return undefined;

Optional: consider reusing parseObject within formatLineItems to de-duplicate parsing and add resilience to malformed JSON (use try/catch similar to parseObject).

components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)

3-9: Nit: Title casing for name

Consider “Get Tracking Category” for consistency with other actions’ capitalization.

-  name: "Get tracking category",
+  name: "Get Tracking Category",

Also, please double-check the documentation anchor in the description resolves correctly.

components/xero_accounting_api/actions/find-contact/find-contact.mjs (1)

34-43: Inconsistent UX: boolean vs string for “Create if not found” across actions

This action uses a string prop with “Yes/No”, while find-or-create-contact uses a boolean. Aligning to boolean improves UX consistency and reduces string comparisons.

Suggested minimal change:

-    createContactIfNotFound: {
-      description: "Create a new contact if not found?.",
-      label: "Create a new contact if not found",
-      type: "string",
-      options: [
-        "Yes",
-        "No",
-      ],
-      reloadProps: true,
-    },
+    createContactIfNotFound: {
+      description: "Create a new contact if not found.",
+      label: "Create a new contact if not found",
+      type: "boolean",
+      reloadProps: true,
+    },

Then update usages:

-    if (this.createContactIfNotFound === "Yes") {
+    if (this.createContactIfNotFound) {
-    if (createContactIfNotFound === "No" && accountNumber && name) {
+    if (!createContactIfNotFound && accountNumber && name) {
-      if (createContactIfNotFound === "Yes") {
+      if (createContactIfNotFound) {
-      createContactIfNotFound === "Yes"
+      createContactIfNotFound

Also applies to: 47-53, 99-103

components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (2)

126-126: Error message references “Type” which isn’t user-provided here

“Type” is hardcoded to “ACCREC” and not a required user input in this action. Adjust the message to avoid confusion.

-      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, **Type**, and **Line Items** parameters.");
+      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, plus **Tenant ID** and **Line Items**.");

129-152: Payload restructuring to app method is correct; consider boolean handling for SentToContact and prop naming

  • Using { $, tenantId, data } is correct. LGTM.
  • Minor: sentToContact prop is typed as string but the Xero field expects boolean. Either change the prop type to boolean, or coerce here.

Coerce to boolean non-destructively:

-        SentToContact: this.sentToContact,
+        SentToContact: typeof this.sentToContact === "string"
+          ? this.sentToContact.toLowerCase() === "true"
+          : this.sentToContact,

Also consider aligning the prop name expectedPaymentData to expectedPaymentDate. To avoid breaking change, support both:

-        ExpectedPaymentDate: this.expectedPaymentData,
+        ExpectedPaymentDate: this.expectedPaymentDate ?? this.expectedPaymentData,

If desired, I can follow up with a PR to migrate the prop name and mark the old prop as deprecated in the description.

components/xero_accounting_api/actions/update-tracking-category/update-tracking-category.mjs (1)

7-7: Confirm documentation anchor targets the update endpoint

The link currently points to the general tracking categories doc with the create anchor. Please verify the anchor for updating by ID (likely “…#post-trackingcategories-trackingcategoryid”) so users land on the right section.

components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)

25-27: Redundant required-prop validation

tenantId and invoiceId are required via props, so this runtime check is typically redundant. Consider removing to reduce noise.

Apply this diff (optional):

-    if (!this.tenantId || !this.invoiceId) {
-      throw new ConfigurationError("Must provide **Tenant ID**, and **Invoice ID** parameters.");
-    }
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)

25-27: Redundant required-prop validation

Props enforce required inputs; the runtime check is optional and can be removed to streamline code.

Apply this diff (optional):

-    if (!this.tenantId || !this.contactIdentifier) {
-      throw new ConfigurationError("Must provide **Tenant ID**, and **Contact Identifier** parameters.");
-    }
components/xero_accounting_api/actions/get-item/get-item.mjs (1)

25-27: Redundant required-prop validation

Given required props, this guard is optional. Removing it reduces boilerplate.

Apply this diff (optional):

-    if (!this.tenantId || !this.itemId) {
-      throw new ConfigurationError("Must provide **Tenant ID**, and **Item ID** parameters.");
-    }
components/xero_accounting_api/actions/get-bank-statements-report/get-bank-statements-report.mjs (2)

44-49: Build params without undefined values

Avoid sending undefined query params. Only include dates if provided.

Apply this diff:

-      params: {
-        bankAccountId: this.bankAccountId,
-        fromDate: this.fromDate,
-        toDate: this.toDate,
-      },
+      params: {
+        bankAccountId: this.bankAccountId,
+        ...(this.fromDate !== undefined && { fromDate: this.fromDate }),
+        ...(this.toDate !== undefined && { toDate: this.toDate }),
+      },

26-33: Clarify expected date format in descriptions

Specify the expected format (e.g., YYYY-MM-DD) to reduce user error when entering dates.

Apply this diff (optional):

-      description: "Get the bank statements of the specified bank account from this date",
+      description: "Get the bank statements of the specified bank account from this date (YYYY-MM-DD)",
@@
-      description: "Get the bank statements of the specified bank account to this date",
+      description: "Get the bank statements of the specified bank account to this date (YYYY-MM-DD)",
components/xero_accounting_api/actions/update-tracking-category-option/update-tracking-category-option.mjs (2)

7-7: Fix wording: This updates a tracking category option, not a category

The description should match the action’s intent (and file/key/name). Also consider using the specific docs anchor for options if available.

-  description: "Update a tracking category by ID [See the documentation](https://developer.xero.com/documentation/api/accounting/trackingcategories#post-trackingcategories).",
+  description: "Update a tracking category option by ID [See the documentation](https://developer.xero.com/documentation/api/accounting/trackingcategories#post-trackingcategories).",

55-66: Don’t send undefined fields; build the payload conditionally

Avoid sending Status when it’s not provided. This reduces the chance of API validation issues and keeps the request minimal.

-      const response = await this.xeroAccountingApi.updateTrackingOption({
-        $,
-        tenantId: this.tenantId,
-        trackingCategoryId: this.trackingCategoryId,
-        trackingOptionId: this.trackingOptionId,
-        data: {
-          Name: this.optionName,
-          Status: this.status,
-        },
-      });
+      const data = {
+        Name: this.optionName,
+        ...(this.status && { Status: this.status }),
+      };
+
+      const response = await this.xeroAccountingApi.updateTrackingOption({
+        $,
+        tenantId: this.tenantId,
+        trackingCategoryId: this.trackingCategoryId,
+        trackingOptionId: this.trackingOptionId,
+        data,
+      });
components/xero_accounting_api/actions/email-an-invoice/email-an-invoice.mjs (1)

21-22: Fix typo in allowed status values in prop description

“SUMBITTED” → “SUBMITTED”.

-      description: "Xero generated unique identifier for the invoice to send by email out of Xero. The invoice must be of Type ACCREC and a valid Status for sending (SUMBITTED,AUTHORISED or PAID).",
+      description: "Xero generated unique identifier for the invoice to send by email out of Xero. The invoice must be of Type ACCREC and a valid Status for sending (SUBMITTED, AUTHORISED or PAID).",
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)

21-22: Minor: capitalize “URL” in the prop description

Aligns with conventional capitalization.

-      description: "Xero generated unique identifier for the invoice to retrieve its online url.",
+      description: "Xero generated unique identifier for the invoice to retrieve its online URL.",
components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (2)

62-63: Confirm response shape; summary may not show ID

Xero often returns created entities inside an array (e.g., Employees[0].EmployeeID). If createEmployee returns that shape, response.EmployeeID will be undefined.

Apply this defensive diff to support both shapes:

-    $.export("$summary", `Successfully created employee with ID: ${response.EmployeeID}`);
-    return response;
+    const employeeId = response?.EmployeeID ?? response?.Employees?.[0]?.EmployeeID;
+    $.export("$summary", `Successfully created employee with ID: ${employeeId ?? "unknown"}`);
+    return response;

Please confirm the shape returned by xeroAccountingApi.createEmployee and adjust if it’s guaranteed to be one or the other.


39-44: Validate External Link shape matches Xero expectation

Docstring suggests only a URL is needed. Xero’s ExternalLink entity typically expects a nested object with a specific key (often Url). Ensure callers pass { Url: "https://..." } (or the expected shape) so Xero accepts the payload.

components/xero_accounting_api/actions/get-bank-summary/get-bank-summary.mjs (1)

51-52: Optional: Improve summary context

Consider including the date range in the summary to aid observability.

-    $.export("$summary", `Bank summary retrieved successfully: ${this.bankAccountId}`);
+    $.export("$summary", `Bank summary retrieved for ${this.bankAccountId}${this.fromDate || this.toDate ? ` (${this.fromDate || "…" } → ${this.toDate || "…"})` : ""}`);
components/xero_accounting_api/actions/download-invoice/download-invoice.mjs (2)

42-47: Avoid base64 round-trip; use syncDir; add a $summary

The current implementation converts the buffer to base64 and back, and ignores the syncDir prop. Write the buffer directly and honor syncDir when provided.

-    const invoicePdf = data.toString("base64");
-    const buffer = Buffer.from(invoicePdf, "base64");
-    const tmpDir = "/tmp";
-    const invoicePath = `${tmpDir}/${this.invoiceId}.pdf`;
-    $.export("invoice_path", invoicePath); //This is where the invoice is saved at the workflow's temporary files.
-    fs.writeFileSync(invoicePath, buffer);
+    const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
+    const outDir = this.syncDir || "/tmp";
+    const invoicePath = `${outDir}/${this.invoiceId}.pdf`;
+    $.export("invoice_path", invoicePath); // This is where the invoice is saved at the workflow's temporary files.
+    $.export("$summary", `Invoice ${this.invoiceId} saved to ${invoicePath}`);
+    fs.writeFileSync(invoicePath, buffer);

24-29: syncDir prop is unused

Leverage syncDir for the output path (see suggested diff above) or remove the prop to avoid confusion.

components/xero_accounting_api/actions/create-history-note/create-history-note.mjs (1)

50-52: Add max-length guard for Details (<= 250 chars)

Xero enforces 250 chars. Proactively validate to surface a clearer error before the API call.

   if (!this.tenantId || !this.endpoint || !this.guid || !this.details) {
     throw new ConfigurationError("Must provide **Tenant ID**, **Endpoint**, **GUID**, and **Details** parameters.");
   }
+  if (this.details.length > 250) {
+    throw new ConfigurationError("Details must be 250 characters or fewer.");
+  }
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (2)

75-76: More informative summary (fallback when no identifier provided)

Current summary prints undefined when contactIdentifier is omitted. Prefer a count-based fallback.

-    $.export("$summary", `Successfully fetched contacts with ID: ${this.contactIdentifier}`);
+    const count = response?.Contacts?.length ?? 0;
+    const descriptor = this.contactIdentifier ? `ID: ${this.contactIdentifier}` : `${count} contact(s)`;
+    $.export("$summary", `Successfully fetched contacts (${descriptor})`);

47-52: Minor: page is numeric

Consider using type: "integer" for page to prevent accidental non-numeric input.

     page: {
       label: "Page",
-      type: "string",
+      type: "integer",
       description: "Up to 100 contacts will be returned per call when the page parameter is used e.g. page=1.",
       optional: true,
     },
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (3)

17-22: Fix description: references “contact” instead of “credit note” and “contact note” typo

The description mixes entities. It should refer to a credit note, not a contact/contact note.

Apply this diff:

-      description: "Credit note identifier of the contact to get. Possible values: 
-* **CreditNoteID** - The Xero identifier for a contact note e.g. 297c2dc5-cc47-4afd-8ec8-74990b8761e9
-* **CreditNoteNumber** -  Identifier for Credit Note CN-8743802",
+      description: "Identifier for the credit note to retrieve. Possible values:
+* **CreditNoteID** — The Xero identifier for a credit note, e.g. 297c2dc5-cc47-4afd-8ec8-74990b8761e9
+* **CreditNoteNumber** — Identifier for a credit note, e.g. CN-8743802",

42-45: Consider numeric type for Page prop

Xero’s page parameter is numeric. Using an integer type improves validation and UI.

-      type: "string",
+      type: "integer",

61-62: Avoid “undefined” in summary when no identifier is provided

Make the summary conditional so it reads cleanly without an identifier.

-    $.export("$summary", `Successfully fetched credit notes with ID: ${this.creditNoteIdentifier}`);
+    $.export("$summary", `Successfully fetched credit notes${this.creditNoteIdentifier ? ` with identifier: ${this.creditNoteIdentifier}` : ""}`);
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)

41-45: Headers prop description is outdated given tenantId handling by the app

The app wrapper should inject xero-tenant-id from tenantId. The current description instructs users to set it manually, which conflicts with the new approach.

-      description: "Headers to send in the request. Must include header `xero-tenant-id` with Id of the organization tenant to use on the Xero Accounting API. See [Get Tenant Connections](https://pipedream.com/@sergio/xero-accounting-api-get-tenant-connections-p_OKCzOgn/edit) for a workflow example on how to pull this data.",
+      description: "Additional headers to include in the request. The `xero-tenant-id` header is derived from the Tenant ID prop.",
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (3)

32-33: Grammar nit: “Filter by an any element”

Tighten the copy for the Where description.

-      description: "Filter by an any element (*see [Filters](https://developer.xero.com/documentation/api/requests-and-responses#get-modified)*)",
+      description: "Filter by any element (*see [Filters](https://developer.xero.com/documentation/api/requests-and-responses#get-modified)*)",

42-45: Consider numeric type for Page prop

Use integer for better validation and UI affordances.

-      type: "string",
+      type: "integer",

61-62: Avoid “undefined” in summary when no Manual Journal ID is provided

Make the summary conditional.

-    $.export("$summary", `Successfully fetched manual journals with ID: ${this.manualJournalId}`);
+    $.export("$summary", `Successfully fetched manual journals${this.manualJournalId ? ` with ID: ${this.manualJournalId}` : ""}`);
components/xero_accounting_api/actions/create-item/create-item.mjs (1)

71-80: Unitdp could be numeric

Optional: make Unitdp an integer with numeric options to avoid string-to-number coercion downstream.

-    unitdp: {
-      type: "string",
+    unitdp: {
+      type: "integer",
       label: "Unitdp",
       description: "By default UnitPrice is returned to two decimal places.",
       optional: true,
       options: [
-        "2",
-        "4",
+        2,
+        4,
       ],
     },
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)

96-97: $summary message is misleading; invoiceIdentifier isn’t an “ID”

invoiceIdentifier selects which identifier field to filter on (e.g., InvoiceID or InvoiceNumber). Consider a neutral summary to avoid confusion.

Apply this diff:

-    $.export("$summary", `Successfully fetched invoices with ID: ${this.invoiceIdentifier}`);
+    $.export("$summary", `Successfully fetched invoices${this.invoiceIdentifier ? ` with filter: ${this.invoiceIdentifier}` : ""}.`);
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (1)

24-29: Minor: Clarify description for Bank account ID

The text currently says “The ID of the Bank Account transaction,” which can confuse users.

Apply this diff:

-      description: "The ID of the Bank Account transaction. If AccountID is not included then Code is required.",
+      description: "The ID of the Bank Account. If AccountID is not included then Code is required.",
components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (1)

109-111: Validation message mentions Type, but Type isn’t a user prop here

Type is fixed as "ACCPAY" in the payload, so it shouldn’t be listed as a required input.

Apply this diff:

-    if ((!this.contactId && !this.contactName) || !this.tenantId || !this.lineItems) {
-      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, **Type**, and **Line Items** parameters.");
-    }
+    if ((!this.contactId && !this.contactName) || !this.tenantId || !this.lineItems) {
+      throw new ConfigurationError(
+        "Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, and **Line Items**."
+      );
+    }
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (2)

226-227: Make $summary resilient; prefer ID from response when contactId isn’t provided

On create-by-name flows, contactId may be empty. Use the API response when available.

Apply this diff:

-    $.export("$summary", `Successfully created or updated contact with ID: ${this.contactId}`);
-    return response;
+    $.export("$summary", `Successfully created or updated contact with ID: ${response?.Contacts?.[0]?.ContactID || this.contactId || "unknown"}`);
+    return response;

100-105: Description for Account Payable Type appears incorrect

It mentions addresses; this prop maps to AccountsPayableTaxType. Consider updating the description to avoid confusion.

Suggested text: "Default tax type used for contact on AP invoices."

components/xero_accounting_api/xero_accounting_api.app.mjs (1)

88-98: Remove the unnecessary spread operator for the headers parameter

The headers parameter is already being spread in Line 105, so spreading it again on Line 93 is redundant.

 getHeader({
   tenantId, modifiedSince = null, headers = {},
 }) {
   const header = {
     "Authorization": `Bearer ${this.$auth.oauth_access_token}`,
-    ...headers,
   };
   tenantId && (header["xero-tenant-id"] = tenantId);
   modifiedSince && (header["If-Modified-Since"] = modifiedSince);
-  return header;
+  return {
+    ...header,
+    ...headers,
+  };
 },

… improved data handling. Key changes include:

- Integrated `formatLineItems` in `create-credit-note` and `create-purchase-bill` actions.
- Implemented `parseObject` in `make-an-api-call`, `create-employee`, `create-or-update-contact`, and `update-contact` actions for better object parsing.
- Updated property names for consistency across actions.

These enhancements improve code maintainability and ensure consistent data formatting across API interactions.
…ke-an-api-call` method to directly return the axios call, simplifying the code and improving readability by removing unnecessary variable assignments.
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

🔭 Outside diff range comments (4)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (2)

111-116: sentToContact should be a boolean, not a string

The description calls it a boolean, and Xero expects a boolean. Using a string risks sending invalid payloads.

-    sentToContact: {
-      type: "string",
+    sentToContact: {
+      type: "boolean",
       label: "Sent To Contact",
       description: "Boolean to set whether the invoice in the Xero app should be marked as \"sent\". This can be set only on invoices that have been approved",
       optional: true,
     },

117-122: Fix typo: expectedPaymentDataexpectedPaymentDate (+ backward compatibility in payload)

Current prop name is inconsistent with the label and Xero field. Recommend renaming while keeping a fallback to avoid breaking existing uses.

-    expectedPaymentData: {
+    expectedPaymentDate: {
       type: "string",
       label: "Expected Payment Date",
       description: "Shown on the sales invoices when this has been set",
       optional: true,
     },
-        SentToContact: this.sentToContact,
-        ExpectedPaymentDate: this.expectedPaymentData,
+        SentToContact: this.sentToContact,
+        ExpectedPaymentDate: this.expectedPaymentDate ?? this.expectedPaymentData,

Also applies to: 149-151

components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)

179-190: PaymentTerms shape is incorrect — must be an object (Bills/Sales with Day/Type)

Xero expects:

  • PaymentTerms: { Bills: { Day, Type }, Sales: { Day, Type } }
    Providing a string will 400. Allow object input and parse.

Apply this diff:

     paymentTerms: {
       label: "Payment Terms",
-      type: "string",
-      description: "The default payment terms for the contact - see Payment Terms",
+      type: "any",
+      description: "The default payment terms for the contact. Provide a JSON object with Bills and/or Sales keys, each containing Day (number) and Type (DAYSAFTERBILLDATE, DAYSAFTERBILLMONTH, OFCURRENTMONTH, OFFOLLOWINGMONTH).",
       optional: true,
       options: [
         "DAYSAFTERBILLDATE",
         "DAYSAFTERBILLMONTH",
         "OFCURRENTMONTH",
         "OFFOLLOWINGMONTH",
       ],
     },
-        PaymentTerms: this.paymentTerms,
+        PaymentTerms: parseObject(this.paymentTerms),

Also applies to: 223-223

components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)

179-190: PaymentTerms shape mismatch; current string with enum options will fail against Xero

Xero’s PaymentTerms is an object (e.g., { Sales: { Day, Type }, Bills: { Day, Type } }), not a plain string. The current prop will not serialize correctly.

Update the prop to accept JSON/object and parse before sending:

     paymentTerms: {
       label: "Payment Terms",
-      type: "string",
-      description: "The default payment terms for the contact - see Payment Terms",
+      type: "any",
+      description: "The default payment terms for the contact. Accepts an object or a JSON string with shape { Sales: { Day, Type }, Bills: { Day, Type } }. See Xero docs for supported Type values.",
       optional: true,
-      options: [
-        "DAYSAFTERBILLDATE",
-        "DAYSAFTERBILLMONTH",
-        "OFCURRENTMONTH",
-        "OFFOLLOWINGMONTH",
-      ],
     },
-        PaymentTerms: this.paymentTerms,
+        PaymentTerms: parseObject(this.paymentTerms),

Also applies to: 228-228

♻️ Duplicate comments (7)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (1)

27-29: Pass $ to getContact options loader to preserve request context

Omitting $ can break client-level logging, headers, and rate limit handling.

Apply this diff:

-        const { Contacts: contacts } = await this.xero.getContact({
-          tenantId: this.tenantId,
-        });
+        const { Contacts: contacts } = await this.xero.getContact({
+          $,
+          tenantId: this.tenantId,
+        });
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (2)

149-154: Prop key typo: puchasesDefaultAccountCode → purchasesDefaultAccountCode

This is a public prop name; keeping the typo will lock users into a misspelling. Fix both the prop and the mapping.

Apply this diff:

-    puchasesDefaultAccountCode: {
+    purchasesDefaultAccountCode: {
       label: "Purchases Default Account Code",
       type: "string",
       description: "The default purchases [account code](https://developer.xero.com/documentation/api/accounts) for contacts",
       optional: true,
     },
-        PurchasesDefaultAccountCode: this.puchasesDefaultAccountCode,
+        PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,

To catch any other usages of the misspelling across the repo, run:

#!/bin/bash
rg -nP -C2 '\bpuchasesDefaultAccountCode\b|\bpuchases\b'

Also applies to: 218-218


161-166: Prop key typo: puechasesTrackingCategories → purchasesTrackingCategories

Same issue as the previous typo. Fix the public prop and the mapping.

Apply this diff:

-    puechasesTrackingCategories: {
+    purchasesTrackingCategories: {
       label: "Purchases Tracking Categories",
       type: "string",
       description: "The default purchases [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
       optional: true,
     },
-        PurchasesTrackingCategories: this.puechasesTrackingCategories,
+        PurchasesTrackingCategories: this.purchasesTrackingCategories,

To verify there aren’t other misspellings, run:

#!/bin/bash
rg -nP -C2 '\bpuechasesTrackingCategories\b|\bpuechases\b'

Also applies to: 220-220

components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)

149-154: Fix typos in purchases props and usage (puchases/puechases → purchases)*

These misspellings leak into the public action props and can confuse/break consumers. Please correct the prop keys and update their usage in the payload.

Apply:

-    puchasesDefaultAccountCode: {
+    purchasesDefaultAccountCode: {
       label: "Purchases Default Account Code",
       type: "string",
       description: "The default purchases [account code](https://developer.xero.com/documentation/api/accounts) for contacts",
       optional: true,
     },
-    puechasesTrackingCategories: {
+    purchasesTrackingCategories: {
       label: "Purchases Tracking Categories",
       type: "string",
       description: "The default purchases [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
       optional: true,
     },
-        PurchasesDefaultAccountCode: this.puchasesDefaultAccountCode,
+        PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,
-        PurchasesTrackingCategories: this.puechasesTrackingCategories,
+        PurchasesTrackingCategories: this.purchasesTrackingCategories,

Backward compatibility suggestion (optional, to avoid a breaking change): accept both old and new prop names and prefer the new ones when both are provided:

// place before constructing the `data` object
const purchasesDefaultAccountCode =
  this.purchasesDefaultAccountCode ?? this.puchasesDefaultAccountCode;
const purchasesTrackingCategories =
  this.purchasesTrackingCategories ?? this.puechasesTrackingCategories;

Then use the local variables in the payload.

Also, please propagate the same fix across related actions (e.g., create-or-update-contact) to keep the surface consistent.

Run this to find other occurrences across the repo:

#!/bin/bash
rg -n -C2 -P 'puechasesTrackingCategories|puchasesDefaultAccountCode'

Also applies to: 161-166, 223-225

components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (2)

139-140: Fix summary: Xero returns an Invoices array for ACCPAY

Purchase bills (ACCPAY) are returned under Invoices. PurchaseBillID doesn’t exist; use Invoices[0].InvoiceID with a safe fallback.

Apply this diff:

-    $.export("$summary", `Successfully created purchase bill with ID: ${response.PurchaseBillID}`);
-    return response;
+    $.export("$summary", `Successfully created purchase bill with ID: ${response?.Invoices?.[0]?.InvoiceID || response?.InvoiceID || "unknown"}`);
+    return response;

96-101: Use boolean type for sentToContact prop

The description and Xero API expect a boolean, not a string.

Apply this diff:

     sentToContact: {
       label: "Sent to Contact",
-      type: "string",
-      description: "Boolean to set whether the invoice in the Xero app should be marked as \"sent\". This can be set only on invoices that have been approved",
+      type: "boolean",
+      description: "Set to true to mark the invoice as sent (only applies to approved invoices).",
       optional: true,
     },
components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs (1)

77-82: Correct prop type for lineItems

This should be an array, not a single object. Other actions use a string[] of JSON strings parsed at runtime via formatLineItems.

Apply this diff:

     lineItems: {
-      label: "Line items",
-      type: "object",
-      description: "See [Invoice Line Items](https://developer.xero.com/documentation/api/Invoices#LineItems)",
-      optional: true,
+      label: "Line items",
+      type: "string[]",
+      description: "Provide one or more line items as JSON strings. See https://developer.xero.com/documentation/api/Invoices#LineItems",
+      optional: true,
     },

Runtime is already using formatLineItems(this.lineItems), so this aligns the schema with the implementation.

🧹 Nitpick comments (16)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (4)

126-126: Fix validation message: remove “Type” (not a prop)
The action hardcodes Type to “ACCREC”; the error message mentions a non-existent input.

-      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, **Type**, and **Line Items** parameters.");
+      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, and **Line Items**.");

132-151: Avoid sending undefined/null fields to Xero (filter payload)
Reduce payload noise and align with patterns used in other actions by stripping null/undefined fields from data.

Option A (no imports needed, inline filter):

-      data: {
+      data: Object.fromEntries(Object.entries({
         Type: "ACCREC", //ACCREC = Sales Invoice
         Contact: {
           ContactID: this.contactId,
           Name: this.contactName,
         },
         LineItems: formatLineItems(this.lineItems),
         Date: this.date,
         DueDate: this.dueDate,
         LineAmountTypes: this.lineAmountType,
         InvoiceNumber: this.invoiceNumber,
         Reference: this.reference,
         BrandingThemeID: this.brandingThemeId,
         Url: this.url,
         CurrencyCode: this.currencyCode,
         CurrencyRate: this.currencyRate,
         Status: this.status,
         SentToContact: this.sentToContact,
-        ExpectedPaymentDate: this.expectedPaymentData,
-      },
+        ExpectedPaymentDate: this.expectedPaymentDate ?? this.expectedPaymentData,
+      }).filter(([, v]) => v !== undefined && v !== null)),

Option B (preferred, if a shared removeNullEntries util is available in your codebase; import as appropriate):

-      data: {
+      data: removeNullEntries({
         Type: "ACCREC", //ACCREC = Sales Invoice
         Contact: {
           ContactID: this.contactId,
           Name: this.contactName,
         },
         LineItems: formatLineItems(this.lineItems),
         Date: this.date,
         DueDate: this.dueDate,
         LineAmountTypes: this.lineAmountType,
         InvoiceNumber: this.invoiceNumber,
         Reference: this.reference,
         BrandingThemeID: this.brandingThemeId,
         Url: this.url,
         CurrencyCode: this.currencyCode,
         CurrencyRate: this.currencyRate,
         Status: this.status,
         SentToContact: this.sentToContact,
-        ExpectedPaymentDate: this.expectedPaymentData,
-      },
+        ExpectedPaymentDate: this.expectedPaymentDate ?? this.expectedPaymentData,
+      }),

63-68: Consider constraining lineAmountType to allowed values
To prevent invalid inputs, provide options (e.g., “Exclusive”, “Inclusive”, “NoTax”) or validate in run.


3-3: Nit: align app prop naming with adjacent actions (“xeroAccountingApi”)
Other files often use xeroAccountingApi as the prop name. This file uses xero. Consider standardizing for consistency across actions.

components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (4)

95-106: Fix AR/AP tax type labels and descriptions (copy/paste errors)

  • Accounts Receivable description incorrectly says “AP invoices”.
  • Accounts Payable field label misses “Tax” and description references addresses.

Apply this diff:

     accountReceivableTaxType: {
       label: "Account Receivable Tax Type",
       type: "string",
-      description: "Default tax type used for contact on AP invoices",
+      description: "Default tax type used for contact on AR invoices",
       optional: true,
     },
     accountPayableType: {
-      label: "Account Payable Type",
+      label: "Accounts Payable Tax Type",
       type: "string",
-      description: "Store certain address types for a contact - see address types",
+      description: "Default tax type used for contact on AP invoices",
       optional: true,
     },

119-130: Replace stray control character in descriptions (use hyphen instead)

The “Is Supplier/Is Customer” descriptions contain a stray “�” character.

Apply this diff:

     isSupplier: {
       label: "Is Supplier",
       type: "boolean",
-      description: "true or false � Boolean that describes if a contact that has any AP invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts payable invoice is generated against this contact.",
+      description: "true or false - Boolean that describes if a contact that has any AP invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts payable invoice is generated against this contact.",
       optional: true,
     },
     isCustomer: {
       label: "Is Customer",
       type: "boolean",
-      description: "true or false � Boolean that describes if a contact has any AR invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts receivable invoice is generated against this contact.",
+      description: "true or false - Boolean that describes if a contact has any AR invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts receivable invoice is generated against this contact.",
       optional: true,
     },

191-196: Preflight validation: require either contactId or name

For a better UX, fail early if both are missing. If the app client already throws a ConfigurationError, feel free to skip this. Otherwise, add:

   async run({ $ }) {
+    if (!this.contactId && !this.name) {
+      throw new ConfigurationError("Either contactId or name is required when creating or updating a contact.");
+    }
     const response = await this.xeroAccountingApi.createOrUpdateContact({

And add this import at the top of the file:

import { ConfigurationError } from "@pipedream/platform";

227-228: Use returned ContactID in summary (fallback to input)

this.contactId may be undefined for creates. Prefer the API response.

Apply this diff:

-    $.export("$summary", `Successfully created or updated contact with ID: ${this.contactId}`);
-    return response;
+    const id = response?.Contacts?.[0]?.ContactID || this.contactId;
+    $.export("$summary", `Successfully created or updated contact with ID: ${id}`);
+    return response;
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (3)

95-106: Fix labels/descriptions for receivable/payable tax type props; consider consistent naming

  • Labels: use “Accounts” (plural) and include “Tax Type”.
  • Descriptions: AR vs AP are currently reversed/irrelevant.

Apply the label/description fixes:

-    accountReceivableTaxType: {
-      label: "Account Receivable Tax Type",
+    accountReceivableTaxType: {
+      label: "Accounts Receivable Tax Type",
       type: "string",
-      description: "Default tax type used for contact on AP invoices",
+      description: "Default tax type used for contact on AR invoices.",
       optional: true,
     },
-    accountPayableType: {
-      label: "Account Payable Type",
+    accountPayableType: {
+      label: "Accounts Payable Tax Type",
       type: "string",
-      description: "Store certain address types for a contact - see address types",
+      description: "Default tax type used for contact on AP invoices.",
       optional: true,
     },

Optional follow-up: for consistency with the payload keys, consider renaming the props to accountsReceivableTaxType and accountsPayableTaxType. If you do, keep backward compatibility by accepting both names and preferring the new ones.


119-130: IsSupplier/IsCustomer are read-only in Xero; avoid sending them or clearly mark as read-only

These fields are not settable via the API. Sending them may be ignored or could cause validation errors depending on Xero behavior.

  • Update descriptions to clearly state “Read-only; cannot be set via this action.”
  • Prefer removing them from the payload.

Apply:

     isSupplier: {
       label: "Is Supplier",
       type: "boolean",
-      description: "true or false � Boolean that describes if a contact that has any AP invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts payable invoice is generated against this contact.",
+      description: "Read-only. Boolean indicating if the contact has any AP invoices. Cannot be set via this action; it is automatically set when an accounts payable invoice is generated against this contact.",
       optional: true,
     },
     isCustomer: {
       label: "Is Customer",
       type: "boolean",
-      description: "true or false � Boolean that describes if a contact has any AR invoices entered against them. Cannot be set via PUT or POST - it is automatically set when an accounts receivable invoice is generated against this contact.",
+      description: "Read-only. Boolean indicating if the contact has any AR invoices. Cannot be set via this action; it is automatically set when an accounts receivable invoice is generated against this contact.",
       optional: true,
     },
-        IsSupplier: this.isSupplier,
-        IsCustomer: this.isCustomer,
+        // IsSupplier/IsCustomer are read-only in Xero; do not send

Also applies to: 218-219


66-76: Minor: update email description and remove legacy character

If possible, drop the note about “umlauts not supported” (legacy limitation) and ensure no non-ASCII control characters slip into descriptions elsewhere.

components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (3)

110-112: Clarify validation error message (remove non-existent “Type”)

This action hardcodes Type: "ACCPAY" and has no type prop. The error text should not mention “Type”.

Apply this diff:

-    if ((!this.contactId && !this.contactName) || !this.tenantId || !this.lineItems) {
-      throw new ConfigurationError("Must provide one of **Contact ID** or **Contact Name**, **Tenant ID**, **Type**, and **Line Items** parameters.");
-    }
+    if ((!this.contactId && !this.contactName) || !this.tenantId || !this.lineItems) {
+      throw new ConfigurationError("Must provide one of Contact ID or Contact Name, Tenant ID, and Line Items.");
+    }

31-35: Narrow prop type for lineItems and clean up description

formatLineItems supports string arrays (JSON per line item) or arrays of objects, but using any weakens validation and UI. Recommend string[] to align with similar actions and clarify the “at least one” requirement (remove stray markdown).

Apply this diff:

-    lineItems: {
-      label: "Line Items",
-      type: "any",
-      description: "See [LineItems](https://developer.xero.com/documentation/api/invoices#LineItemsPOST). The LineItems collection can contain any number of individual LineItem sub-elements. At least * **one** * is required to create a complete Invoice.",
-    },
+    lineItems: {
+      label: "Line Items",
+      type: "string[]",
+      description: "Provide one or more line items as JSON strings. See https://developer.xero.com/documentation/api/invoices#LineItemsPOST",
+    },

Note: formatLineItems(this.lineItems) already parses string arrays, so no runtime change required.


48-53: Provide options for lineAmountType

Adding explicit options improves UX and prevents invalid values.

Apply this diff:

     lineAmountType: {
       label: "Line Amount Type",
       type: "string",
       description: "Line amounts are exclusive of tax by default if you don't specify this element. See [Line Amount Types](https://developer.xero.com/documentation/api/types#LineAmountTypes)",
       optional: true,
+      options: [
+        "Exclusive",
+        "Inclusive",
+        "NoTax",
+      ],
     },
components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs (2)

31-36: Fix copy: “this name” → “this number” in Contact Number description

Minor text fix to avoid confusion.

Apply this diff:

     contactNumber: {
       type: "string",
       label: "Contact Number",
-      description: "Number of the contact associated to the credit note. If there is no contact matching this name, a new contact is created.",
+      description: "Number of the contact associated to the credit note. If there is no contact matching this number, a new contact is created.",
       optional: true,
     },

153-155: Harden summary extraction with safe fallback

Avoid possible runtime errors on unexpected responses or empty arrays.

Apply this diff:

-    $.export("$summary", `Successfully created credit note with ID: ${response.CreditNotes[0].CreditNoteID}`);
-    return response;
+    $.export("$summary", `Successfully created credit note with ID: ${response?.CreditNotes?.[0]?.CreditNoteID || "unknown"}`);
+    return response;
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 906b647 and ef36669.

📒 Files selected for processing (7)
  • components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs (4 hunks)
  • components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (2 hunks)
  • components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (2 hunks)
  • components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (3 hunks)
  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (3 hunks)
  • components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (1 hunks)
  • components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.

Applied to files:

  • components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs
  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
📚 Learning: 2024-10-30T15:24:39.294Z
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".

Applied to files:

  • components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs
  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
🧬 Code Graph Analysis (6)
components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (13)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (59-66)
components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (1)
  • response (52-61)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (193-225)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (197-230)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (1)
  • response (129-152)
components/xero_accounting_api/actions/add-line-item-to-invoice/add-line-item-to-invoice.mjs (1)
  • response (38-45)
components/xero_accounting_api/actions/create-history-note/create-history-note.mjs (1)
  • response (54-66)
components/xero_accounting_api/actions/create-payment/create-payment.mjs (1)
  • response (160-164)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (102-106)
components/xero_accounting_api/actions/delete-tracking-category-option/delete-tracking-category-option.mjs (1)
  • response (42-47)
components/xero_accounting_api/actions/delete-tracking-category/delete-tracking-category.mjs (1)
  • response (30-34)
components/xero_accounting_api/common/util.mjs (1)
  • formatLineItems (22-40)
components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (4)
components/xero_accounting_api/actions/create-item/create-item.mjs (1)
  • response (87-104)
components/xero_accounting_api/actions/create-payment/create-payment.mjs (1)
  • response (160-164)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (102-106)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (3)
components/xero_accounting_api/sources/new-updated-contact/new-updated-contact.mjs (1)
  • contacts (37-42)
components/xero_accounting_api/actions/add-line-item-to-invoice/add-line-item-to-invoice.mjs (1)
  • response (38-45)
components/xero_accounting_api/common/util.mjs (1)
  • formatLineItems (22-40)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (13)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (59-66)
components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (1)
  • response (52-61)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (197-230)
components/xero_accounting_api/actions/xero-create-purchase-bill/xero-create-purchase-bill.mjs (1)
  • response (114-137)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (1)
  • response (129-152)
components/xero_accounting_api/actions/add-line-item-to-invoice/add-line-item-to-invoice.mjs (1)
  • response (38-45)
components/xero_accounting_api/actions/create-history-note/create-history-note.mjs (1)
  • response (54-66)
components/xero_accounting_api/actions/create-payment/create-payment.mjs (1)
  • response (160-164)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (102-106)
components/xero_accounting_api/actions/delete-tracking-category-option/delete-tracking-category-option.mjs (1)
  • response (42-47)
components/xero_accounting_api/actions/delete-tracking-category/delete-tracking-category.mjs (1)
  • response (30-34)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (3)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (193-225)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (102-106)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs (4)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (1)
  • response (129-152)
components/xero_accounting_api/actions/create-payment/create-payment.mjs (1)
  • response (160-164)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (102-106)
components/xero_accounting_api/common/util.mjs (1)
  • formatLineItems (22-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Ensure component commits modify component versions
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: pnpm publish
🔇 Additional comments (12)
components/xero_accounting_api/actions/xero-create-sales-invoice/xero-create-sales-invoice.mjs (2)

94-104: Verify numeric fields typed as strings
currencyRate is numeric in Xero. Keeping it a string might be intentional for high-precision decimals, but confirm consistency across actions and UI.

Would you like me to scan the repo and list all places where numeric Xero fields are typed as “string” to standardize them (or justify exceptions)?


129-133: Good migration to object-style client call and nested payload
Switching to createInvoice({ $, tenantId, data }) aligns with the new client interface and improves readability.

components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)

1-2: Good migration to app client + consistent payload shaping

Uses the shared Xero app client and parseObject for nested fields, and passes tenantId explicitly. This aligns with the PR-wide refactor and improves maintainability.

Also applies to: 191-225

components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)

1-3: Solid migration to app client + propDefinition wiring

  • Imports and propDefinition usage look correct.
  • Validation for tenantId/contactId and the switch to xeroAccountingApi.updateContact are sound.
  • Summary export is helpful.

Also applies to: 12-18, 192-201, 232-233

components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs (1)

101-106: SentToContact is writable now — do not remove; update the description and keep sending it

Short: Xero changed this field (changelog / xero-node v9.2.0). SentToContact is writable for credit notes since 26 Aug 2024 — the original suggestion to stop sending it is outdated.

Files to update:

  • components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs — lines 101-106: update the description that currently says "currently read only".
  • components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs — line ~147: keep sending SentToContact in the payload (do not apply the diff that removes it). Verify the property name/casing matches Xero's API.

Suggested change (replace description):

@@ components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs
-      description: "Boolean to indicate if a credit note has been sent to a contact via the Xero app (currently read only)",
+      description: "Boolean to indicate if a credit note has been sent to a contact via the Xero app. Writable via the Xero API (writable since 26 Aug 2024); ensure SDK/API version supports this.",

Recommendation: ensure consumers use an SDK/API version that supports this (xero-node >= 9.2.0 / updated API); if you must support older integrations, add a graceful guard or feature flag.

Likely an incorrect or invalid review comment.

components/xero_accounting_api/actions/xero-accounting-create-employee/xero-accounting-create-employee.mjs (7)

1-3: LGTM! Clean migration to centralized app pattern.

The imports properly migrate from direct API calls to the centralized xero app client, adding necessary utility functions and error handling. The ConfigurationError import aligns with the improved validation pattern used across the Xero actions.


12-18: LGTM! Consistent app integration pattern.

The prop definition correctly uses the centralized xeroAccountingApi app and leverages its propDefinition for tenantId. This aligns with the broader migration pattern seen across other Xero actions in the PR.


19-28: LGTM! Props correctly migrated to camelCase.

The firstName and lastName props are properly defined with camelCase naming, which fixes the previous snake_case issue that would have caused undefined values in the API payload.


40-45: LGTM! Consistent naming and typing.

The externalLink prop uses camelCase naming consistently and maintains the correct object type for complex data structures.


48-50: LGTM! Improved validation with proper error handling.

The validation logic correctly checks the camelCase property names and uses ConfigurationError for consistent error handling across the Xero actions.


52-61: LGTM! Proper API call migration and payload construction.

The implementation correctly:

  • Uses the centralized xeroAccountingApi.createEmployee method
  • Maps camelCase props to the expected API payload keys (FirstName, LastName, ExternalLink)
  • Applies parseObject utility to handle complex ExternalLink data
  • Follows the established pattern from other migrated actions

This resolves the previous issue where snake_case property accessors would have sent undefined values to the API.


63-64: LGTM! Consistent response handling.

The success message and return pattern align with other migrated Xero actions, providing clear feedback about the created employee.

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 (10)
components/xero_accounting_api/xero_accounting_api.app.mjs (10)

117-121: Use template literal for URL construction (style consistency)

Adopt the established template literal pattern for URL concatenation.

     getTenantConnections() {
       return this._makeRequest({
-        url: BASE_URL + "/connections",
+        url: `${BASE_URL}/connections`,
       });
     },

187-193: Normalize HTTP verb casing in createInvoice

Use "POST" to be consistent. Lowercase likely works, but consistency helps readability and avoids surprises.

     createInvoice(opts = {}) {
       return this._makeRequest({
-        method: "post",
+        method: "POST",
         path: "/Invoices",
         ...opts,
       });
     },

206-210: Normalize HTTP verb casing in emailInvoice

Use "POST" to be consistent across methods.

       return this._makeRequest({
-        method: "post",
+        method: "POST",
         path: `/Invoices/${invoiceId}/Email`,
         ...opts,
       });

377-381: Normalize HTTP verb casing in uploadFile

Use "POST" to match the rest of the codebase.

       return this._makeRequest({
-        method: "post",
+        method: "POST",
         path: `/${documentType}/${documentId}/Attachments/${fileName}`,
         ...opts,
       });

57-62: Guard against missing TrackingCategory/Options to avoid runtime crash

If the tracking category isn’t found or has no Options, trackingCategories[0].Options will throw. Return an empty array in those cases.

Apply this diff:

-        return trackingCategories[0].Options.map(({
-          TrackingOptionID, Name,
-        }) => ({
-          label: Name,
-          value: TrackingOptionID,
-        }));
+        const trackingCategory = trackingCategories?.[0];
+        const options = Array.isArray(trackingCategory?.Options)
+          ? trackingCategory.Options
+          : [];
+        return options.map(({ TrackingOptionID, Name }) => ({
+          label: Name,
+          value: TrackingOptionID,
+        }));

168-174: Fix HTTP verb casing and path case for Contacts creation

  • Use "POST" to match HTTP verb casing used elsewhere.
  • Xero endpoints are case-sensitive; use /Contacts (capital C) for consistency with other methods and to avoid 404s.
     createContact(opts = {}) {
       return this._makeRequest({
-        method: "post",
-        path: "/contacts",
+        method: "POST",
+        path: "/Contacts",
         ...opts,
       });
     },

342-349: Fix listContacts: collection endpoint should not accept identifier

This implementation fetches a single contact. Listing should call /Contacts and accept only options.

-    listContacts({
-      contactIdentifier, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/Contacts/${contactIdentifier}`,
-        ...opts,
-      });
-    },
+    listContacts(opts = {}) {
+      return this._makeRequest({
+        path: `/Contacts`,
+        ...opts,
+      });
+    },

350-357: Fix listCreditNotes: collection endpoint should not accept identifier

This fetches a single credit note; adjust to call /CreditNotes without an identifier.

-    listCreditNotes({
-      creditNoteIdentifier, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/CreditNotes/${creditNoteIdentifier}`,
-        ...opts,
-      });
-    },
+    listCreditNotes(opts = {}) {
+      return this._makeRequest({
+        path: `/CreditNotes`,
+        ...opts,
+      });
+    },

358-365: Fix listInvoices: collection endpoint should not accept identifier

This currently fetches a single invoice; adjust to call /Invoices.

-    listInvoices({
-      invoiceIdentifier, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/Invoices/${invoiceIdentifier}`,
-        ...opts,
-      });
-    },
+    listInvoices(opts = {}) {
+      return this._makeRequest({
+        path: `/Invoices`,
+        ...opts,
+      });
+    },

366-373: Fix listManualJournals: collection endpoint should not accept identifier

This currently retrieves a single manual journal; adjust to call /ManualJournals.

-    listManualJournals({
-      manualJournalId, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/ManualJournals/${manualJournalId}`,
-        ...opts,
-      });
-    },
+    listManualJournals(opts = {}) {
+      return this._makeRequest({
+        path: `/ManualJournals`,
+        ...opts,
+      });
+    },
🧹 Nitpick comments (2)
components/xero_accounting_api/xero_accounting_api.app.mjs (2)

99-116: Make _makeRequest explicitly support absolute URLs

Right now absolute URLs work by overriding via ...opts, which is implicit. Accept a url param explicitly and prefer it over path to make intent clear and reduce confusion.

     _makeRequest({
       $ = this,
       tenantId,
       modifiedSince,
       path,
       headers,
+      url,
       ...opts
     }) {
-      return axios($, {
-        url: `${BASE_URL}/api.xro/2.0${path}`,
+      const finalUrl = url || `${BASE_URL}/api.xro/2.0${path}`;
+      return axios($, {
+        url: finalUrl,
         headers: this.getHeader({
           tenantId,
           modifiedSince,
           headers,
         }),
         ...opts,
       });
     },

194-202: Optional: Confirm headers for PDF download (if applicable)

downloadInvoice currently performs a GET on /Invoices/{id} identical to getInvoiceById. If the intent is to download a PDF, ensure callers pass headers: { Accept: "application/pdf" } and appropriate responseType (e.g., arraybuffer). Otherwise, consider consolidating with getInvoiceById.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ef36669 and 4a2a9a6.

📒 Files selected for processing (1)
  • components/xero_accounting_api/xero_accounting_api.app.mjs (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-29T22:59:38.825Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15436
File: components/printful/printful.app.mjs:55-63
Timestamp: 2025-01-29T22:59:38.825Z
Learning: Console.log statements should be removed before merging PRs to maintain code quality and prevent potential security risks from exposing sensitive information in logs.

Applied to files:

  • components/xero_accounting_api/xero_accounting_api.app.mjs
🧬 Code Graph Analysis (1)
components/xero_accounting_api/xero_accounting_api.app.mjs (2)
components/xero_accounting_api/common/constants.mjs (4)
  • DB_LAST_DATE_CHECK (1-1)
  • DB_LAST_DATE_CHECK (1-1)
  • BASE_URL (2-2)
  • BASE_URL (2-2)
components/xero_accounting_api/common/util.mjs (1)
  • chainQueryString (108-109)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base

michelle0927
michelle0927 previously approved these changes Aug 15, 2025
Copy link
Collaborator

@michelle0927 michelle0927 left a comment

Choose a reason for hiding this comment

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

LGTM!

…consistency. Key changes include:

- Updated API request methods to use consistent casing for paths (e.g., `/Contacts` and `/Invoices`).
- Enhanced error handling in actions to provide clearer feedback when no results are found.
- Removed deprecated `find-contact` action and streamlined contact-related actions.
- Bumped versions for several actions to reflect updates and improvements.

These changes enhance the reliability and maintainability of the Xero Accounting API integration.
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: 13

♻️ Duplicate comments (14)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)

18-21: ContactNumber is not supported by GET /Contacts/{id}; update prop description or add fallback in app client
The action routes to getContactById, which generally maps to GET /Contacts/{ContactID}. Xero does not natively support fetching a contact by ContactNumber at that endpoint. Either (a) clarify the prop to accept ContactID (GUID) only, or (b) add a fallback in the app to search by ContactNumber.

-      description: "Xero identifier of the contact to get. Possible values: \n* **ContactID** - The Xero identifier for a contact e.g. 297c2dc5-cc47-4afd-8ec8-74990b8761e9\n* **ContactNumber** -  A custom identifier specified from another system e.g. a CRM system has a contact number of CUST100",
+      description: "Xero ContactID (GUID) of the contact to retrieve (e.g. 297c2dc5-cc47-4afd-8ec8-74990b8761e9).\nNote: Retrieval by ContactNumber is not supported by GET /Contacts/{id}. To fetch by ContactNumber, use the List Contacts action with an appropriate Where filter.",

If you prefer the fallback behavior, I can propose a patch to components/xero_accounting_api/xero_accounting_api.app.mjs to attempt GET by ID first and, on 404 or non-GUID, list and filter by ContactNumber.

components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)

54-60: Pass tenantId to _makeRequest (required for xero-tenant-id header); parse queryString into params object

Without tenantId, the app can’t set the xero-tenant-id header, and requests will likely fail. Also, axios params typically expects an object; parse the query string if provided.

-    const response = await this.xeroAccountingApi._makeRequest({
-      $,
-      method: this.requestMethod,
-      path: this.relativeUrl,
-      params: this.queryString,
-      data: parseObject(this.requestBody),
-    });
+    const response = await this.xeroAccountingApi._makeRequest({
+      $,
+      method: this.requestMethod,
+      path: this.relativeUrl,
+      tenantId: this.tenantId,
+      params: (typeof this.queryString === "string" && this.queryString.trim())
+        ? Object.fromEntries(new URLSearchParams(this.queryString))
+        : this.queryString,
+      data: parseObject(this.requestBody),
+    });
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (2)

149-154: Fix typos: puchasesDefaultAccountCode → purchasesDefaultAccountCode

Spelling errors in prop names degrade DX and ecosystem consistency.

-    puchasesDefaultAccountCode: {
+    purchasesDefaultAccountCode: {
       label: "Purchases Default Account Code",
       type: "string",
       description: "The default purchases [account code](https://developer.xero.com/documentation/api/accounts) for contacts",
       optional: true,
     },
-        PurchasesDefaultAccountCode: this.puchasesDefaultAccountCode,
+        PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,

Also applies to: 241-241


155-166: sales/purchases tracking categories should accept arrays (parse JSON) and fix spelling of purchasesTrackingCategories

Xero expects arrays for SalesTrackingCategories and PurchasesTrackingCategories. Update types, parse inputs, and fix the “puechases” typo.

     salesTrackingCategories: {
       label: "Sales Tracking Categories",
-      type: "string",
-      description: "The default sales [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
+      type: "any",
+      description: "The default sales [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts. Accepts an array or a JSON string representing an array of tracking category assignments.",
       optional: true,
     },
-    puechasesTrackingCategories: {
+    purchasesTrackingCategories: {
       label: "Purchases Tracking Categories",
-      type: "string",
-      description: "The default purchases [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
+      type: "any",
+      description: "The default purchases [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts. Accepts an array or a JSON string representing an array of tracking category assignments.",
       optional: true,
     },
-        SalesTrackingCategories: this.salesTrackingCategories,
-        PurchasesTrackingCategories: this.puechasesTrackingCategories,
+        SalesTrackingCategories: parseObject(this.salesTrackingCategories),
+        PurchasesTrackingCategories: parseObject(this.purchasesTrackingCategories),

Also applies to: 242-243

components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (1)

146-147: Fix casing: LineItems key is misspelled (capital “I” required)

Xero expects LineItems with a capital “I”. Using Lineitems will drop all line items from the request.

Apply this diff:

-        Lineitems: this.lineItems,
+        LineItems: this.lineItems,
components/xero_accounting_api/xero_accounting_api.app.mjs (6)

117-121: Use consistent URL construction pattern

Follow the template-literal pattern used elsewhere.

-      return this._makeRequest({
-        url: BASE_URL + "/connections",
-      });
+      return this._makeRequest({
+        url: `${BASE_URL}/connections`,
+      });

57-62: Avoid potential crash when no options exist for the tracking category

This assumes TrackingCategories[0].Options exists. It will throw if the category isn’t found or has no options.

Apply this diff:

-        return trackingCategories[0].Options.map(({
-          TrackingOptionID, Name,
-        }) => ({
-          label: Name,
-          value: TrackingOptionID,
-        }));
+        const trackingCategory = trackingCategories?.[0];
+        if (!trackingCategory?.Options?.length) return [];
+        return trackingCategory.Options.map(({ TrackingOptionID, Name }) => ({
+          label: Name,
+          value: TrackingOptionID,
+        }));

168-178: Don’t pass boolean to axios params; preserve caller params

When where is falsy, params becomes false. Also, this overwrites opts.params. Build params by merging.

-      return this._makeRequest({
-        path: "/Contacts",
-        params: where && {
-          Where: where,
-        },
-        ...opts,
-      });
+      const params = {
+        ...(opts.params || {}),
+        ...(where ? { Where: where } : {}),
+      };
+      return this._makeRequest({
+        path: "/Contacts",
+        ...opts,
+        params,
+      });

205-215: Same issue: boolean params and lost caller params

Mirror the fix from getContact().

-      return this._makeRequest({
-        path: "/Invoices",
-        ...opts,
-        params: where && {
-          Where: where,
-        },
-      });
+      const params = {
+        ...(opts.params || {}),
+        ...(where ? { Where: where } : {}),
+      };
+      return this._makeRequest({
+        path: "/Invoices",
+        ...opts,
+        params,
+      });

347-353: listInvoices fetches a single invoice (wrong endpoint/signature)

This method should return the collection. Current implementation returns a single invoice by ID.

Apply this diff:

-    listInvoices({
-      invoiceIdentifier, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/Invoices/${invoiceIdentifier}`,
-        ...opts,
-      });
-    },
+    listInvoices(opts = {}) {
+      return this._makeRequest({
+        path: "/Invoices",
+        ...opts,
+      });
+    },

355-362: listManualJournals fetches a single manual journal (wrong endpoint/signature)

This should call the collection endpoint.

-    listManualJournals({
-      manualJournalId, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/ManualJournals/${manualJournalId}`,
-        ...opts,
-      });
-    },
+    listManualJournals(opts = {}) {
+      return this._makeRequest({
+        path: "/ManualJournals",
+        ...opts,
+      });
+    },
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (3)

149-154: Fix prop key typo: puchasesDefaultAccountCode → purchasesDefaultAccountCode

Public prop name has a typo and the payload pulls from the misspelled key. Correct now to avoid long-term API friction.

-    puchasesDefaultAccountCode: {
+    purchasesDefaultAccountCode: {
       label: "Purchases Default Account Code",
       type: "string",
       description: "The default purchases [account code](https://developer.xero.com/documentation/api/accounts) for contacts",
       optional: true,
     },
-        PurchasesDefaultAccountCode: this.puchasesDefaultAccountCode,
+        PurchasesDefaultAccountCode: this.purchasesDefaultAccountCode,

Also applies to: 236-236


155-160: Sales tracking categories should accept JSON and be parsed

Xero expects arrays/objects. Type "string" without parsing likely produces invalid payloads.

-    salesTrackingCategories: {
+    salesTrackingCategories: {
       label: "Sales Tracking Categories",
-      type: "string",
+      type: "any",
       description: "The default sales [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
       optional: true,
     },
-        SalesTrackingCategories: this.salesTrackingCategories,
+        SalesTrackingCategories: parseObject(this.salesTrackingCategories),

Also applies to: 237-237


161-166: Purchases tracking categories: same issue (parse and allow arrays/objects)

Mirror the change above for purchases.

-    puechasesTrackingCategories: {
+    purchasesTrackingCategories: {
       label: "Purchases Tracking Categories",
-      type: "string",
+      type: "any",
       description: "The default purchases [tracking categories](https://developer.xero.com/documentation/api/tracking-categories/) for contacts",
       optional: true,
     },
-        PurchasesTrackingCategories: this.puechasesTrackingCategories,
+        PurchasesTrackingCategories: parseObject(this.purchasesTrackingCategories),

Also applies to: 238-238

🧹 Nitpick comments (32)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (2)

17-22: Broaden date format guidance for modifiedAfter

Xero uses the If-Modified-Since header semantics and supports full timestamps. Limiting the UI hint to YYYY-MM-DD is unnecessarily restrictive.

Apply this diff to improve the hint:

-      description: "Filter by a date. Only contacts modified since this date will be returned. Format: YYYY-MM-DD",
+      description: "Return contacts modified since this date/time. Accepts ISO 8601 (e.g. 2025-01-01 or 2025-01-01T00:00:00Z).",

42-46: Use an integer type for Page

Page is numeric per Xero docs. Using an integer improves validation and UX.

Apply this diff:

-      type: "string",
+      type: "integer",
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (2)

4-8: Nit: tighten the description phrasing

Minor copy tweak improves clarity and matches other actions’ tone.

Apply this diff:

-  description: "Lists information from tracking categories [See the documentation](https://developer.xero.com/documentation/api/accounting/trackingcategories).",
+  description: "List tracking categories. See the documentation: https://developer.xero.com/documentation/api/accounting/trackingcategories",

9-17: Optional Enhancement: Add includeArchived Flag

The Xero wrapper’s getTrackingCategories(opts = {}) spreads all passed options (…opts) into its HTTP request, so a top‐level params object is supported. To expose archived categories:

• Add an includeArchived boolean prop:

   props: {
     xeroAccountingApi,
     tenantId: {
       propDefinition: [xeroAccountingApi, "tenantId"],
     },
+    includeArchived: {
+      type: "boolean",
+      label: "Include archived",
+      description: "Include archived tracking categories",
+      optional: true,
+    },
   },

• Pass it through the API call:

-    const response = await this.xeroAccountingApi.getTrackingCategories({
-      $,
-      tenantId: this.tenantId,
-    });
+    const response = await this.xeroAccountingApi.getTrackingCategories({
+      $,
+      tenantId: this.tenantId,
+      params: { includeArchived: this.includeArchived },
+    });

This addition is optional but follows common list-endpoint conventions. Let me know if you’d like any assistance integrating it.

components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (5)

19-24: Standardize prop casing: rename contactID → contactId to align with repo conventions

Most actions in this PR use camelCase IDs (invoiceId, itemId, contactId in other files). This prop stands out with PascalCase suffix and likely creates inconsistency for users.

Apply this diff within the prop block:

-    contactID: {
+    contactId: {
       type: "string",
-      label: "Contact ID",
+      label: "Contact ID",
       description: "ID of the contact that requires update.",
       optional: true,
     },

And update references in changed lines:

-    if (!this.contactID && !this.name) {
+    if (!this.contactId && !this.name) {

For lines outside the changed segment, also update:

  • Line 73: const { contactID, ... } = this;const { contactId, ... } = this;
  • Line 90: contactID && (data.ContactID = contactID);contactId && (data.ContactID = contactId);

96-96: Make the success summary accurate for both create and update flows

This message can mislead when an update occurs.

Apply this diff:

-    response && $.export("$summary", "Contact created successfully");
+    if (response) {
+      const action = this.contactId ? "updated" : "created";
+      $.export("$summary", `Contact ${action} successfully`);
+    }

7-7: Trim trailing whitespace in the action name

Minor polish to avoid UI oddities.

-  name: "Create or update contact ",
+  name: "Create or Update Contact",

34-34: Fix minor typos/punctuation in descriptions

Small textual nits that improve professionalism.

-      description: "First name of contact person .",
+      description: "First name of contact person.",
-      description: "User defined account number..",
+      description: "User-defined account number.",

Also applies to: 52-52


69-71: Validation logic: consider allowing other Xero identifiers if supported

Currently, only contactId or name passes validation. If the wrapper supports identifying contacts by fields like ContactNumber or EmailAddress for upsert semantics, consider relaxing this check accordingly.

Would you like me to adjust the validation to accept additional identifiers (e.g., emailAddress, accountNumber) if present?

components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (2)

55-55: Guard against missing response fields when summarizing count

Avoids TypeError if CreditNotes is absent or undefined.

-      $.export("$summary", `Successfully fetched ${response.CreditNotes.length} credit notes`);
+      $.export("$summary", `Successfully fetched ${response?.CreditNotes?.length ?? 0} credit notes`);

57-60: Don’t swallow all errors; only treat “not found/empty” as non-fatal

Returning {} on any exception makes debugging operational issues harder. Consider narrowing the catch to known empty/not-found cases or rethrowing unexpected errors.

-    } catch (e) {
-      $.export("$summary", "No credit notes found");
-      return {};
-    }
+    } catch (e) {
+      const status = e?.response?.status;
+      if (status === 404) {
+        $.export("$summary", "No credit notes found");
+        return {};
+      }
+      throw e;
+    }
components/xero_accounting_api/actions/upload-file/upload-file.mjs (3)

78-78: Avoid unnecessary Buffer copy

fileBinary is already a Buffer. Re-wrapping creates an extra allocation without benefit.

-      data: Buffer.from(fileBinary, "binary"),
+      data: fileBinary,

73-77: Set a safe default Content-Type

Some sources may not provide a MIME type; default to application/octet-stream.

-        "Content-Type": metadata.contentType,
+        "Content-Type": metadata.contentType || "application/octet-stream",

51-60: Potential memory usage concern for large files

Converting the entire stream to a Buffer can be memory-heavy for large attachments. If the app wrapper supports streams, prefer passing the Readable stream directly to avoid buffering.

I can update this action to pass the stream through (and adjust the wrapper if needed). Want me to draft that change?

components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)

33-36: Avoid masking unexpected errors

Catching all errors and returning {} hampers observability. Limit to 404 (not found) and rethrow others.

-    } catch (e) {
-      $.export("$summary", `No invoice found with identifier: ${this.invoiceId}`);
-      return {};
-    }
+    } catch (e) {
+      if (e?.response?.status === 404) {
+        $.export("$summary", `No invoice found with identifier: ${this.invoiceId}`);
+        return {};
+      }
+      throw e;
+    }
components/xero_accounting_api/actions/get-item/get-item.mjs (1)

33-36: Same error handling refinement as get-invoice

Only treat “not found” as a benign case; rethrow other errors.

-    } catch (e) {
-      $.export("$summary", `No item found with identifier: ${this.itemId}`);
-      return {};
-    }
+    } catch (e) {
+      if (e?.response?.status === 404) {
+        $.export("$summary", `No item found with identifier: ${this.itemId}`);
+        return {};
+      }
+      throw e;
+    }
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)

33-36: Differentiate 404 “not found” from other errors

Currently all errors are summarized as “No invoice found…”, which can mask auth/network issues. Recommend special-casing 404 and rethrowing others.

-    } catch (e) {
-      $.export("$summary", `No invoice found with identifier: ${this.invoiceId}`);
-      return {};
-    }
+    } catch (e) {
+      if (e?.response?.status === 404) {
+        $.export("$summary", `No invoice found with identifier: ${this.invoiceId}`);
+        return {};
+      }
+      throw e;
+    }
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)

33-36: Refine error handling to avoid masking non-404 errors

Mirror the pattern of surfacing “not found” only on 404, and rethrow other errors for visibility.

-    } catch (e) {
-      $.export("$summary", `No contact found with identifier: ${this.contactIdentifier}`);
-      return {};
-    }
+    } catch (e) {
+      if (e?.response?.status === 404) {
+        $.export("$summary", `No contact found with identifier: ${this.contactIdentifier}`);
+        return {};
+      }
+      throw e;
+    }
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (2)

90-91: Guard against undefined response shape when building summary

If the app returns a response without Invoices, accessing length will throw. Use optional chaining and provide a robust count.

-      $.export("$summary", `Successfully fetched ${response.Invoices.length} invoices`);
+      const count = response?.Invoices?.length ?? 0;
+      $.export("$summary", `Successfully fetched ${count} invoice${count === 1 ? "" : "s"}`);

24-27: Nit: fix wording to “Invoice IDs”

Minor doc cleanup.

-      description: "Filter by a comma-separated list of InvoicesIDs. See [details](https://developer.xero.com/documentation/api/invoices#optimised-queryparameters).",
+      description: "Filter by a comma-separated list of Invoice IDs. See [details](https://developer.xero.com/documentation/api/invoices#optimised-queryparameters).",
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (2)

151-152: Guard summary against missing response fields

If the API returns an unexpected shape (errors, empty array), accessing BankTransactions[0] will throw.

Apply this diff:

-    $.export("$summary", `Successfully created bank transaction with ID: ${response.BankTransactions[0].BankTransactionID}`);
+    const id = response?.BankTransactions?.[0]?.BankTransactionID || "unknown";
+    $.export("$summary", `Successfully created bank transaction with ID: ${id}`);

24-29: Nit: description says “Bank Account transaction” but should be “Bank Account”

Minor wording fix to avoid confusion.

-      description: "The ID of the Bank Account transaction. If AccountID is not included then Code is required.",
+      description: "The ID of the Bank Account. If AccountID is not included then Code is required.",
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (2)

101-106: Consistency: accountsPayableTaxType naming

Prop is named accountPayableType but maps to AccountsPayableTaxType. Rename for clarity and consistency with accountReceivableTaxType.

-    accountPayableType: {
+    accountsPayableTaxType: {
       label: "Account Payable Type",
       type: "string",
       description: "Store certain address types for a contact - see address types",
       optional: true,
     },
-        AccountsPayableTaxType: this.accountPayableType,
+        AccountsPayableTaxType: this.accountsPayableTaxType,

Also consider updating the description to match Receivable’s (“Default tax type used for contact on AP invoices”).

Also applies to: 228-228


250-251: Summary should use the ID returned by the API, not the input contactId

When creating a new contact, this.contactId may be empty. Use the response to populate the summary.

-    $.export("$summary", `Successfully created or updated contact with ID: ${this.contactId}`);
+    const id = response?.Contacts?.[0]?.ContactID || this.contactId || "unknown";
+    $.export("$summary", `Successfully created or updated contact with ID: ${id}`);
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (4)

17-22: Improve Modified After UX hint (explicit ISO 8601 with Z and placeholder)

Minor clarity tweak so users provide a proper UTC timestamp including the Z suffix.

Apply:

       label: "Modified After",
       type: "string",
-      description: "The ModifiedAfter filter is actually an HTTP header: **'If-Modified-Since'**. A UTC timestamp (yyyy-mm-ddThh:mm:ss) . Only manual journals created or modified since this timestamp will be returned e.g. 2009-11-12T00:00:00",
+      description: "The ModifiedAfter filter maps to the 'If-Modified-Since' HTTP header. Provide a UTC timestamp in ISO 8601 format (e.g., 2009-11-12T00:00:00Z). Only manual journals created or modified since this timestamp will be returned.",
+      placeholder: "2009-11-12T00:00:00Z",
       optional: true,

23-28: Fix small grammar nit in Where description

Replace “an any” with “any”.

       type: "string",
-      description: "Filter by an any element (*see [Filters](https://developer.xero.com/documentation/api/requests-and-responses#get-modified)*)",
+      description: "Filter by any element (*see [Filters](https://developer.xero.com/documentation/api/requests-and-responses#get-modified)*)",
       optional: true,

35-40: Page should be an integer (enforce numeric input and avoid coercion)

Using an integer type improves UX and prevents string-to-number conversion bugs. Add a minimum bound.

       label: "Page",
-      type: "string",
+      type: "integer",
+      min: 1,
       description: "Up to 100 manual journals will be returned per call, with journal lines shown for each, when the page parameter is used e.g. page=1",
       optional: true,

55-56: Guard summary against missing/undefined ManualJournals

Avoids a TypeError when the response has no ManualJournals array.

-      $.export("$summary", `Successfully fetched ${response.ManualJournals.length} manual journals`);
-      return response;
+      const count = Array.isArray(response?.ManualJournals) ? response.ManualJournals.length : 0;
+      $.export("$summary", `Successfully fetched ${count} manual journals`);
+      return response;
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs (4)

22-27: Nit: clean up minor copy/punctuation for “name” prop

Tighten the description to remove trailing space and improve readability.

-      description: "Full name of contact/organization ",
+      description: "Full name of the contact or organization.",

31-33: Nit: align wording for “emailAddress” prop

Small copy tweak for consistency and clarity.

-      description: "Email address of contact/organization.",
+      description: "Email address of the contact or organization.",

35-41: Nit: improve toggle copy and provide a default

Add a default of “No” and remove the stray punctuation in the description. This keeps behavior explicit when users don’t change the control.

-      description: "Create a new contact if not found?.",
+      description: "Create a new contact if not found?",
       label: "Create a new contact if not found",
       type: "string",
       options: [
         "Yes",
         "No",
       ],
+      default: "No",

47-72: Nit: minor grammar/punctuation in additional props

Tidy up descriptions for first/last name; current strings include an extra space and could be slightly clearer.

       props.firstName = {
         type: "string",
         label: "First name",
-        description: "First name of contact person .",
+        description: "First name of the contact person.",
         optional: true,
       };
       props.lastName = {
         type: "string",
         label: "Last name",
-        description: "Last name of contact person.",
+        description: "Last name of the contact person.",
         optional: true,
       };
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4a2a9a6 and 398aec8.

📒 Files selected for processing (21)
  • components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (3 hunks)
  • components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1 hunks)
  • components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (4 hunks)
  • components/xero_accounting_api/actions/download-invoice/download-invoice.mjs (2 hunks)
  • components/xero_accounting_api/actions/find-contact/find-contact.mjs (0 hunks)
  • components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs (3 hunks)
  • components/xero_accounting_api/actions/get-contact/get-contact.mjs (1 hunks)
  • components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1 hunks)
  • components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1 hunks)
  • components/xero_accounting_api/actions/get-item/get-item.mjs (1 hunks)
  • components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1 hunks)
  • components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1 hunks)
  • components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1 hunks)
  • components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1 hunks)
  • components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (1 hunks)
  • components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1 hunks)
  • components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (2 hunks)
  • components/xero_accounting_api/actions/upload-file/upload-file.mjs (2 hunks)
  • components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (2 hunks)
  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (2 hunks)
  • components/xero_accounting_api/xero_accounting_api.app.mjs (3 hunks)
💤 Files with no reviewable changes (1)
  • components/xero_accounting_api/actions/find-contact/find-contact.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/xero_accounting_api/actions/download-invoice/download-invoice.mjs
  • components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-10-30T15:24:39.294Z
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".

Applied to files:

  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
  • components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.

Applied to files:

  • components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
📚 Learning: 2025-01-29T22:59:38.825Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15436
File: components/printful/printful.app.mjs:55-63
Timestamp: 2025-01-29T22:59:38.825Z
Learning: Console.log statements should be removed before merging PRs to maintain code quality and prevent potential security risks from exposing sensitive information in logs.

Applied to files:

  • components/xero_accounting_api/xero_accounting_api.app.mjs
🧬 Code Graph Analysis (18)
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (13)
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (1)
  • response (126-149)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/upload-file/upload-file.mjs (1)
  • response (67-79)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (11)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (54-60)
components/xero_accounting_api/actions/upload-file/upload-file.mjs (1)
  • response (67-79)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (5)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/google_drive/actions/create-file-from-template/create-file-from-template.mjs (1)
  • e (77-79)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (5)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1)
  • response (56-67)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (8)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (8)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (54-60)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (10)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (7)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/upload-file/upload-file.mjs (3)
components/tinypng/actions/compress-image/compress-image.mjs (1)
  • getFileStreamAndMetadata (26-28)
components/signnow/actions/upload-document-with-tags/upload-document-with-tags.mjs (1)
  • getFileStreamAndMetadata (46-48)
components/zip_archive_api/actions/compress-files/compress-files.mjs (1)
  • getFileStreamAndMetadata (53-55)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (5)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1)
  • response (56-67)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (54-60)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (5)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1)
  • response (56-67)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/xero_accounting_api.app.mjs (3)
components/xero_accounting_api/common/constants.mjs (4)
  • DB_LAST_DATE_CHECK (1-1)
  • DB_LAST_DATE_CHECK (1-1)
  • BASE_URL (2-2)
  • BASE_URL (2-2)
components/xero_accounting_api/sources/new-updated-invoice/new-updated-invoice.mjs (1)
  • invoices (38-43)
components/xero_accounting_api/common/util.mjs (1)
  • chainQueryString (108-109)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (4)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1)
  • response (56-67)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (4)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/common/util.mjs (1)
  • parseObject (111-134)
components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (14)
components/xero_accounting_api/actions/create-tracking-category/create-tracking-category.mjs (1)
  • response (33-39)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-item/get-item.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (1)
  • response (56-67)
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (1)
  • response (20-23)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (1)
  • response (44-53)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (54-60)
components/xero_accounting_api/actions/upload-file/upload-file.mjs (1)
  • response (67-79)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/actions/get-item/get-item.mjs (9)
components/xero_accounting_api/actions/get-contact/get-contact.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/create-update-contact/create-update-contact.mjs (1)
  • response (91-95)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
  • response (29-33)
components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)
  • response (25-29)
components/xero_accounting_api/actions/list-invoices/list-invoices.mjs (1)
  • response (74-88)
components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs (1)
  • response (54-60)
components/xero_accounting_api/actions/upload-file/upload-file.mjs (1)
  • response (67-79)
components/xero_accounting_api/actions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs (1)
  • response (211-248)
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)
  • response (215-253)
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs (1)
components/xero_accounting_api/common/util.mjs (2)
  • removeNullEntries (3-20)
  • formatQueryString (65-74)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (23)
components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (4)

1-1: Import path and app reference look correct.

The relative path resolves to the app module and the default import naming is consistent with other actions.


10-26: Props wiring LGTM.

  • Uses shared propDefinitions for tenantId and trackingCategoryId.
  • Correctly scopes trackingCategoryId options to the selected tenantId.

35-36: Success summary is clear.

Returning the API response and exporting a concise $summary is consistent with other actions.


27-33: No changes needed—method name is correct
The Xero client defines and uses getTrackingCategory (see components/xero_accounting_api/xero_accounting_api.app.mjs), and there is no getTrackingCategoryById method. Your existing call is consistent with the client API.

components/xero_accounting_api/actions/list-contacts/list-contacts.mjs (3)

7-7: Confirm version bump reflects breaking prop changes

This action’s public props changed (tenantId via propDefinition, removed legacy props). If users’ workflows might break, consider a breaking version bump consistent with your repo’s conventions for Pipedream components.

What’s the expected semver policy here for components under 0.x? If breaking, consider 0.3.0 (or 1.0.0 if you’ve graduated the app).


10-16: Good move to app-based propDefinition for tenantId

Switching to the centralized app and pulling tenantId via propDefinition aligns with the rest of the PR and reduces duplication.


1-1: Import path is correct; no change required
Verified that components/xero_accounting_api/xero_accounting_api.app.mjs is reached by import xeroAccountingApi from "../../xero_accounting_api.app.mjs"; in actions/list-contacts/list-contacts.mjs. This pattern matches all other actions and the app exports (e.g., listContacts). No updates needed.

Likely an incorrect or invalid review comment.

components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs (3)

1-1: Import path and app binding look correct

The relative path to the app module is correct from this file’s location, and importing the default export aligns with other actions’ patterns.


9-17: Props wiring LGTM

Using the app and the tenantId propDefinition is consistent with the rest of the Xero actions.


20-23: Confirmed getTrackingCategories exists and returns TrackingCategories
I verified that:

  • getTrackingCategories is implemented in components/xero_accounting_api/xero_accounting_api.app.mjs (lines 269–272).
  • Its response object includes a TrackingCategories array (destructured at line 32), matching how it’s used in list-tracking-categories.
components/xero_accounting_api/actions/list-credit-notes/list-credit-notes.mjs (1)

1-16: Migration to app wrapper looks good

Props, tenantId propDefinition, and wrapper usage align with the broader PR pattern.

components/xero_accounting_api/actions/upload-file/upload-file.mjs (1)

1-50: Overall migration to wrapper is clean

The action cleanly delegates auth and endpoint handling to the app wrapper and surfaces a clear summary.

components/xero_accounting_api/actions/get-invoice/get-invoice.mjs (1)

1-31: Wrapper migration and prop wiring LGTM

Consistent with other actions: tenantId via propDefinition, camelCase props, and concise success summary.

components/xero_accounting_api/actions/get-item/get-item.mjs (1)

1-31: Action structure and wrapper usage look good

Matches the PR-wide pattern; clear inputs and summaries.

components/xero_accounting_api/actions/get-invoice-online-url/get-invoice-online-url.mjs (1)

24-33: LGTM: migrated to app client and standardized props/flow

The action correctly delegates to the app method, uses propDefinition for tenantId, and exports a clear success summary. This aligns with the broader refactor pattern.

components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs (1)

210-213: Validation is good

Guarding against missing tenantId/contactId with ConfigurationError is appropriate and matches adjacent actions.

components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs (1)

118-124: Validation is good

Requiring either Bank Account Code or ID, and either Contact ID or Name, via ConfigurationError is solid.

components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs (4)

1-1: Switched to xeroAccountingApi app wrapper import — LGTM

Import path matches the new app wrapper used across related actions in this PR.


10-16: PropDefinition for tenantId via app wrapper — LGTM

Consistent with other migrated actions in the PR; reduces duplication and centralizes validation.


7-7: No lingering old prop references in list-manual-journals action
Ran

rg -n -C2 -P '\btenant_id\b|\bmodified_after\b' components/xero_accounting_api/actions/list-manual-journals/

— no matches found. Proceed with version bump to 0.2.0, but please:

• Update release notes to call out the prop renames (tenant_idtenantId, modified_aftermodifiedAfter).
• Verify that existing workflows are pinned to the prior version to avoid unintended breaks.


43-53: ✅ Verified listManualJournals wrapper signature and parameter casing

I’ve confirmed in components/xero_accounting_api/xero_accounting_api.app.mjs (lines 355–362) that:

  • listManualJournals is defined with ({ manualJournalId, ...opts })
  • It forwards ...opts—including your params object with Where, order, and page—directly to _makeRequest
  • The call site in actions/list-manual-journals/list-manual-journals.mjs mirrors the existing listInvoices pattern

No further changes are needed here.

components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs (2)

11-12: Description and version bump look good

Clearer description with docs link and a semantic version bump to 0.1.0. No issues here.


92-108: getContact filter formatting verified

  • getContact expects a single Where parameter containing a preformatted Xero filter string.
  • formatQueryString(payload, true) URL-encodes each key/value pair and wraps values in double quotes; chainQueryString then replaces & with AND to form a valid Xero “where” expression.
  • When only Name or EmailAddress is provided, you get a single-condition filter; if both are supplied, they’re combined with AND. Change to OR semantics only if that’s your intent.

No further changes needed.

Comment on lines +55 to 59
lineItems: {
type: "object",
label: "Line items",
description: "See [LineItems](https://developer.xero.com/documentation/api/banktransactions#LineItemsPOST). The LineItems element can contain any number of individual LineItem sub-elements. At least **one** is required to create a bank transaction.",
},
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

Accept array JSON for line items and parse before sending

Current prop type "object" is awkward for array input and likely to produce invalid payloads. You already expose a standardized lineItems propDefinition in the app (string[] with JSON examples). Reuse it here and parse into objects before sending.

Apply these diffs:

  1. Use the app propDefinition for lineItems:
-    lineItems: {
-      type: "object",
-      label: "Line items",
-      description: "See [LineItems](https://developer.xero.com/documentation/api/banktransactions#LineItemsPOST). The LineItems element can contain any number of individual LineItem sub-elements. At least **one** is required to create a bank transaction.",
-    },
+    lineItems: {
+      propDefinition: [
+        xeroAccountingApi,
+        "lineItems",
+      ],
+    },
  1. Parse lineItems when mapping:
-        LineItems: this.lineItems,
+        LineItems: parseObject(this.lineItems),
  1. Add the missing import:
-import { ConfigurationError } from "@pipedream/platform";
-import xeroAccountingApi from "../../xero_accounting_api.app.mjs";
+import { ConfigurationError } from "@pipedream/platform";
+import xeroAccountingApi from "../../xero_accounting_api.app.mjs";
+import { parseObject } from "../../common/util.mjs";

Also applies to: 146-148, 1-2

Comment on lines +77 to 91
let contactDetail;
const {
tenantId,
name,
firstName,
lastName,
emailAddress,
contactStatus,
createContactIfNotFound,
} = this;
if (createContactIfNotFound === "No" && emailAddress && name) {
throw new ConfigurationError(
"Choose exclusively between Email Address or Name to find a contact.",
);
}
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 essential validation: require a search key; refine create-time requirements

Currently, if both Name and Email are omitted, the action builds an empty query, which likely returns many contacts and can be expensive/unexpected. Also, when creating, Xero typically needs a Name or a combination of fields; we should require at least Name or both First/Last names. Keep the existing “exclusive choice” enforcement when not creating.

   async run({ $ }) {
-    let contactDetail;
-    const {
+    let contactDetail;
+    const {
       tenantId,
       name,
       firstName,
       lastName,
       emailAddress,
       contactStatus,
       createContactIfNotFound,
     } = this;
-    if (createContactIfNotFound === "No" && emailAddress && name) {
-      throw new ConfigurationError(
-        "Choose exclusively between Email Address or Name to find a contact.",
-      );
-    }
+    const createIf = createContactIfNotFound === "Yes";
+    if (!name && !emailAddress) {
+      throw new ConfigurationError("Provide either Email address or Name to search for a contact.");
+    }
+    if (!createIf && emailAddress && name) {
+      throw new ConfigurationError("Choose exclusively between Email address or Name to find a contact.");
+    }
+    if (createIf && !name && !(firstName && lastName)) {
+      throw new ConfigurationError("To create a contact, provide Name or both First name and Last name.");
+    }
📝 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
let contactDetail;
const {
tenantId,
name,
firstName,
lastName,
emailAddress,
contactStatus,
createContactIfNotFound,
} = this;
if (createContactIfNotFound === "No" && emailAddress && name) {
throw new ConfigurationError(
"Choose exclusively between Email Address or Name to find a contact.",
);
}
async run({ $ }) {
let contactDetail;
const {
tenantId,
name,
firstName,
lastName,
emailAddress,
contactStatus,
createContactIfNotFound,
} = this;
const createIf = createContactIfNotFound === "Yes";
if (!name && !emailAddress) {
throw new ConfigurationError("Provide either Email address or Name to search for a contact.");
}
if (!createIf && emailAddress && name) {
throw new ConfigurationError("Choose exclusively between Email address or Name to find a contact.");
}
if (createIf && !name && !(firstName && lastName)) {
throw new ConfigurationError("To create a contact, provide Name or both First name and Last name.");
}
// ... existing logic continues ...
}
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs
around lines 77 to 91, the code currently allows building an empty query when
both name and emailAddress are omitted and doesn't enforce creation-time name
requirements; update validation so that when createContactIfNotFound === "No"
you keep the exclusive choice check (throw ConfigurationError if both name and
emailAddress are provided), and additionally always require at least one search
key (either emailAddress or name) — if both are missing throw a
ConfigurationError; when createContactIfNotFound !== "No" (creating a contact)
require either a Name or both FirstName and LastName (throw ConfigurationError
if neither condition is met) so create requests include sufficient identity
fields.

Comment on lines +92 to +95
const findPayload = removeNullEntries({
Name: name,
EmailAddress: emailAddress,
});
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

Prefer a single, deterministic search key (email if present, else name)

When both are provided and creation is enabled, sending both can translate to an AND filter and accidentally suppress matches. Prefer searching by Email first since it’s usually more specific, otherwise fall back to Name.

-    const findPayload = removeNullEntries({
-      Name: name,
-      EmailAddress: emailAddress,
-    });
+    const findPayload = emailAddress
+      ? { EmailAddress: emailAddress }
+      : { Name: name };
📝 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
const findPayload = removeNullEntries({
Name: name,
EmailAddress: emailAddress,
});
const findPayload = emailAddress
? { EmailAddress: emailAddress }
: { Name: name };
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs
around lines 92 to 95, the current find payload includes both Name and
EmailAddress which may be treated as an AND filter and miss matches; change the
logic so the search key is deterministic: if emailAddress is present use {
EmailAddress: emailAddress } else use { Name: name } (then pass that through
removeNullEntries as before) so searches use email first and only fall back to
name.

Comment on lines +103 to +116
try {
contactDetail = await this.xeroAccountingApi.getContact({
$,
tenantId,
queryParam: formatQueryString(findPayload, true),
});
} catch (error) {
if (createContactIfNotFound === "Yes") {
$.export("$summary", "Contact not found. Creating new contact");
} else {
$.export("$summary", "No contact found.");
return {};
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Do not treat request errors as “not found”

Xero’s Contacts read typically returns 200 with an empty list when no matches. Catching all errors here and assuming “not found” can mask auth/network/rate limit issues and lead to unintended creates. Let the error bubble, and handle the “no results” case via response inspection.

-    try {
-      contactDetail = await this.xeroAccountingApi.getContact({
-        $,
-        tenantId,
-        queryParam: formatQueryString(findPayload, true),
-      });
-    } catch (error) {
-      if (createContactIfNotFound === "Yes") {
-        $.export("$summary", "Contact not found. Creating new contact");
-      } else {
-        $.export("$summary", "No contact found.");
-        return {};
-      }
-    }
+    contactDetail = await this.xeroAccountingApi.getContact({
+      $,
+      tenantId,
+      queryParam: formatQueryString(findPayload, true),
+    });
📝 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
try {
contactDetail = await this.xeroAccountingApi.getContact({
$,
tenantId,
queryParam: formatQueryString(findPayload, true),
});
} catch (error) {
if (createContactIfNotFound === "Yes") {
$.export("$summary", "Contact not found. Creating new contact");
} else {
$.export("$summary", "No contact found.");
return {};
}
}
contactDetail = await this.xeroAccountingApi.getContact({
$,
tenantId,
queryParam: formatQueryString(findPayload, true),
});
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs
around lines 103 to 116, the catch block currently treats any thrown error as
"not found" and proceeds to create a contact; remove or narrow the try/catch so
network/auth/rate-limit errors are allowed to bubble and instead handle the "no
results" case by inspecting contactDetail (e.g., check for an empty contacts
array or missing items) after a successful getContact call; if
createContactIfNotFound === "Yes" then call create only when the response shows
no matches, otherwise export the "No contact found." summary and return {};
ensure only expected non-exception conditions trigger create and do not swallow
thrown errors.

Comment on lines +118 to 129
if (
(!contactDetail || !contactDetail?.Contacts?.length) &&
createContactIfNotFound === "Yes"
) {
return await this.xeroAccountingApi.createOrUpdateContact({
$,
tenantId,
data: createPayload,
});
}
return response;
return contactDetail;
},
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

Handle “no match” vs “create” explicitly and set clear summaries

Make the not-found path explicit post-fetch. Also, set outcome summaries for each path (created, found, or no match).

-    if (
-      (!contactDetail || !contactDetail?.Contacts?.length) &&
-      createContactIfNotFound === "Yes"
-    ) {
-      return await this.xeroAccountingApi.createOrUpdateContact({
-        $,
-        tenantId,
-        data: createPayload,
-      });
-    }
-    return contactDetail;
+    if (!contactDetail?.Contacts?.length) {
+      if (createIf) {
+        const created = await this.xeroAccountingApi.createOrUpdateContact({
+          $,
+          tenantId,
+          data: createPayload,
+        });
+        $.export("$summary", "Contact created.");
+        return created;
+      }
+      $.export("$summary", "No contact found.");
+      return {};
+    }
+    $.export("$summary", `Found ${contactDetail.Contacts.length} contact(s).`);
+    return contactDetail;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/find-or-create-contact/find-or-create-contact.mjs
around lines 118 to 129, the current code lumps the "not found" and "create"
logic together and does not set explicit outcome summaries; change it to
explicitly detect a not-found result after the fetch, then branch: if
createContactIfNotFound === "Yes" call createOrUpdateContact and set a
summary/outcome of "created" before returning the created contact, if
createContactIfNotFound !== "Yes" set a summary/outcome of "no match" and return
a clear not-found value, and otherwise when contactDetail exists set a
summary/outcome of "found" and return the existing contact; ensure the chosen
summary strings are set on the action/response object where other actions expect
them.

Comment on lines +55 to 74
try {
const response = await this.xeroAccountingApi.listContacts({
$,
tenantId: this.tenantId,
modifiedAfter: this.modifiedAfter,
params: {
IDs: this.ids,
Where: this.where,
order: this.order,
page: this.page,
includeArchived: this.includeArchived,
},
});

if (!this.tenant_id) {
throw new Error("Must provide tenant_id parameter.");
$.export("$summary", `Successfully fetched ${response.Contacts.length} contacts`);
return response;
} catch (e) {
$.export("$summary", "No contacts found");
return {};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Don’t swallow API errors; set summary based on result count

Catching all errors and returning {} masks authentication/network issues and leads to inconsistent return shapes. Let errors bubble. Also, set the summary to “No contacts found” only when the result is empty.

Apply this diff:

-    try {
-      const response = await this.xeroAccountingApi.listContacts({
-        $,
-        tenantId: this.tenantId,
-        modifiedAfter: this.modifiedAfter,
-        params: {
-          IDs: this.ids,
-          Where: this.where,
-          order: this.order,
-          page: this.page,
-          includeArchived: this.includeArchived,
-        },
-      });
-
-      $.export("$summary", `Successfully fetched ${response.Contacts.length} contacts`);
-      return response;
-    } catch (e) {
-      $.export("$summary", "No contacts found");
-      return {};
-    }
+    const response = await this.xeroAccountingApi.listContacts({
+      $,
+      tenantId: this.tenantId,
+      modifiedAfter: this.modifiedAfter,
+      params: Object.fromEntries(Object.entries({
+        IDs: this.ids,
+        Where: this.where,
+        order: this.order,
+        page: this.page,
+        includeArchived: this.includeArchived,
+      }).filter(([, v]) => v !== undefined && v !== "")),
+    });
+
+    const count = response?.Contacts?.length ?? 0;
+    $.export("$summary", count ? `Successfully fetched ${count} contacts` : "No contacts found");
+    return 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
try {
const response = await this.xeroAccountingApi.listContacts({
$,
tenantId: this.tenantId,
modifiedAfter: this.modifiedAfter,
params: {
IDs: this.ids,
Where: this.where,
order: this.order,
page: this.page,
includeArchived: this.includeArchived,
},
});
if (!this.tenant_id) {
throw new Error("Must provide tenant_id parameter.");
$.export("$summary", `Successfully fetched ${response.Contacts.length} contacts`);
return response;
} catch (e) {
$.export("$summary", "No contacts found");
return {};
}
const response = await this.xeroAccountingApi.listContacts({
$,
tenantId: this.tenantId,
modifiedAfter: this.modifiedAfter,
params: Object.fromEntries(
Object.entries({
IDs: this.ids,
Where: this.where,
order: this.order,
page: this.page,
includeArchived: this.includeArchived,
}).filter(([, v]) => v !== undefined && v !== "")
),
});
const count = response?.Contacts?.length ?? 0;
$.export(
"$summary",
count
? `Successfully fetched ${count} contacts`
: "No contacts found"
);
return response;
🤖 Prompt for AI Agents
In components/xero_accounting_api/actions/list-contacts/list-contacts.mjs around
lines 55 to 74, the current try/catch swallows all API errors and always returns
{} with a "No contacts found" summary; change it so errors are not suppressed
and the summary is set to "No contacts found" only when the API call succeeds
but returns zero contacts. Concretely, call this.xeroAccountingApi.listContacts
without catching all exceptions (or rethrow after handling), inspect the
successful response and export a success summary with the contact count or
export "No contacts found" when response.Contacts exists and has length 0, then
return the response; let any thrown errors bubble up instead of returning an
empty object.

Comment on lines +57 to +59
} catch (e) {
$.export("$summary", "No manual journals found");
return {};
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

Don’t swallow unexpected API errors; rethrow except for 304/404

Returning {} on any error masks auth/network issues and makes workflows silently succeed. Handle “no new data” (304) or “not found” (404) gracefully, but rethrow other errors so users can act.

-    } catch (e) {
-      $.export("$summary", "No manual journals found");
-      return {};
+    } catch (err) {
+      const status = err?.response?.status;
+      if (status === 304 || status === 404) {
+        $.export("$summary", "No manual journals found");
+        return { ManualJournals: [] };
+      }
+      $.export("$summary", `Failed to fetch manual journals${status ? ` (HTTP ${status})` : ""}`);
+      throw err;
📝 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
} catch (e) {
$.export("$summary", "No manual journals found");
return {};
} catch (err) {
const status = err?.response?.status;
if (status === 304 || status === 404) {
$.export("$summary", "No manual journals found");
return { ManualJournals: [] };
}
$.export(
"$summary",
`Failed to fetch manual journals${status ? ` (HTTP ${status})` : ""}`
);
throw err;
}
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/list-manual-journals/list-manual-journals.mjs
around lines 57-59, the catch block currently swallows all errors and returns an
empty object; change it to only handle Xero 304 and 404 responses by setting the
"$summary" and returning {} for those status codes, but for any other error
rethrow it (or throw a new error) so auth/network/API issues surface; ensure you
inspect e.response?.status (or the error shape used by the HTTP client) to
distinguish statuses and preserve the original error message when rethrowing.

Comment on lines +18 to +31
async run({ $ }) {
try {
const response = await this.xeroAccountingApi.getTrackingCategories({
$,
tenantId: this.tenantId,
});

$.export("$summary", `Successfully fetched ${response.TrackingCategories.length} tracking categories`);
return response;
} catch (e) {
$.export("$summary", "No tracking categories found");
return {};
}
},
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

Don’t swallow API errors; return consistent shapes and guard for missing data

Catching all errors and returning {} with a “No tracking categories found” summary will mask real issues (auth, network, rate limits) and mislead users. Also, accessing response.TrackingCategories.length without guards can throw if the property is missing. Recommend removing the generic try/catch, guarding length, and setting a neutral summary.

Apply this diff:

-  async run({ $ }) {
-    try {
-      const response = await this.xeroAccountingApi.getTrackingCategories({
-        $,
-        tenantId: this.tenantId,
-      });
-
-      $.export("$summary", `Successfully fetched ${response.TrackingCategories.length} tracking categories`);
-      return response;
-    } catch (e) {
-      $.export("$summary", "No tracking categories found");
-      return {};
-    }
-  },
+  async run({ $ }) {
+    const response = await this.xeroAccountingApi.getTrackingCategories({
+      $,
+      tenantId: this.tenantId,
+    });
+    const count = response?.TrackingCategories?.length ?? 0;
+    $.export("$summary", `Fetched ${count} tracking categor${count === 1 ? "y" : "ies"}`);
+    return 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 run({ $ }) {
try {
const response = await this.xeroAccountingApi.getTrackingCategories({
$,
tenantId: this.tenantId,
});
$.export("$summary", `Successfully fetched ${response.TrackingCategories.length} tracking categories`);
return response;
} catch (e) {
$.export("$summary", "No tracking categories found");
return {};
}
},
async run({ $ }) {
const response = await this.xeroAccountingApi.getTrackingCategories({
$,
tenantId: this.tenantId,
});
const count = response?.TrackingCategories?.length ?? 0;
$.export("$summary", `Fetched ${count} tracking categor${count === 1 ? "y" : "ies"}`);
return response;
},
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/list-tracking-categories/list-tracking-categories.mjs
around lines 18 to 31, the current code swallows all errors and assumes
response.TrackingCategories exists; remove the broad try/catch so real API
errors bubble up, ensure the returned shape is consistent by returning the
original response when present or an object with TrackingCategories: [] if
missing, and guard the length access with optional chaining (e.g.
(response?.TrackingCategories ?? []).length) while exporting a neutral summary
like "Fetched X tracking categories" so you don't mislead users when data is
absent.

import { axios } from "@pipedream/platform";
import { ConfigurationError } from "@pipedream/platform";
import { parseObject } from "../../common/util.mjs";
import xeroAccountingApi from "../../xero_accounting_api.app.mjs";

export default {
key: "xero_accounting_api-make-an-api-call",
name: "Make API Call",
description: "Makes an aribitrary call to Xero Accounting API.",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Typo: “aribitrary” → “arbitrary”; also prefer “HTTP” capitalization

This is user-facing text.

-  description: "Makes an aribitrary call to Xero Accounting API.",
+  description: "Makes an arbitrary call to the Xero Accounting API.",
📝 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
description: "Makes an aribitrary call to Xero Accounting API.",
description: "Makes an arbitrary call to the Xero Accounting API.",
🤖 Prompt for AI Agents
In components/xero_accounting_api/actions/make-an-api-call/make-an-api-call.mjs
around line 8, the description string contains a typo and wrong casing: change
"Makes an aribitrary call to Xero Accounting API." to "Makes an arbitrary HTTP
call to the Xero Accounting API." — fix the spelling of "arbitrary", capitalize
"HTTP", and add the definite article before "Xero Accounting API" for clarity in
user-facing text.

Comment on lines +95 to 106
accountReceivableTaxType: {
label: "Account Receivable Tax Type",
type: "string",
description: "Default tax type used for contact on AP invoices",
optional: true,
},
account_payable_type: {
accountPayableType: {
label: "Account Payable Type",
type: "string",
description: "Store certain address types for a contact - see address types",
optional: true,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct descriptions: AR vs AP tax types and incorrect copy for payable field

“Account Receivable Tax Type” currently says AP; “Account Payable Type” description refers to addresses. Fix the documentation to accurately reflect their meanings.

     accountReceivableTaxType: {
       label: "Account Receivable Tax Type",
       type: "string",
-      description: "Default tax type used for contact on AP invoices",
+      description: "Default tax type used for the contact on AR (accounts receivable) invoices",
       optional: true,
     },
     accountPayableType: {
       label: "Account Payable Type",
       type: "string",
-      description: "Store certain address types for a contact - see address types",
+      description: "Default tax type used for the contact on AP (accounts payable) invoices",
       optional: true,
     },
📝 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
accountReceivableTaxType: {
label: "Account Receivable Tax Type",
type: "string",
description: "Default tax type used for contact on AP invoices",
optional: true,
},
account_payable_type: {
accountPayableType: {
label: "Account Payable Type",
type: "string",
description: "Store certain address types for a contact - see address types",
optional: true,
},
accountReceivableTaxType: {
label: "Account Receivable Tax Type",
type: "string",
description: "Default tax type used for the contact on AR (accounts receivable) invoices",
optional: true,
},
accountPayableType: {
label: "Account Payable Type",
type: "string",
description: "Default tax type used for the contact on AP (accounts payable) invoices",
optional: true,
},
🤖 Prompt for AI Agents
In
components/xero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
around lines 95 to 106, the two field descriptions are wrong:
accountReceivableTaxType currently mentions AP and accountPayableType contains
an address-related sentence. Update accountReceivableTaxType description to read
that it is the "Default tax type used for contact on AR (accounts receivable)
invoices" and change accountPayableType label to "Account Payable Tax Type" and
its description to "Default tax type used for contact on AP (accounts payable)
invoices"; keep both fields optional and type string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

xero
2 participants