-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
base: master
Are you sure you want to change the base?
17984 xero #18071
Conversation
…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.
The latest updates on your projects. Learn more about Vercel for GitHub. 3 Skipped Deployments
|
WalkthroughBroad 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
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
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 criteriaCurrently, you only enforce mutual exclusivity when “Create a new contact if not found” is “No”, and there’s no guard when neither
name
noraccountNumber
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
oraccountNumber
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
checksthis.addressLine1
twice and never checksaddressLine2
. This can incorrectly skip validation when only line 2 is provided.- In the payload,
AddressLine2
is assignedthis.addressLine1
instead ofthis.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 fieldsThis 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/incorrectAll 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 payloadThe 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 payloadThe data payload has typos that correspond to the property name typos:
puchases_default_account_code
should use the corrected property name, andpuechases_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 updateCurrently 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 nameMinor 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 periodGrammar 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 actionsDelete 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 consistencyMost 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;
treats0
andfalse
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
withinformatLineItems
to de-duplicate parsing and add resilience to malformed JSON (use try/catch similar toparseObject
).components/xero_accounting_api/actions/get-tracking-category/get-tracking-category.mjs (1)
3-9
: Nit: Title casing for nameConsider “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 actionsThis 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" + createContactIfNotFoundAlso 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 forSentToContact
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
toexpectedPaymentDate
. 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 endpointThe 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
andinvoiceId
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 validationProps 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 validationGiven 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 valuesAvoid 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 descriptionsSpecify 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 categoryThe 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 conditionallyAvoid 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 descriptionAligns 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 IDXero often returns created entities inside an array (e.g.,
Employees[0].EmployeeID
). IfcreateEmployee
returns that shape,response.EmployeeID
will beundefined
.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 expectationDocstring 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 contextConsider 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 $summaryThe current implementation converts the buffer to base64 and back, and ignores the
syncDir
prop. Write the buffer directly and honorsyncDir
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 unusedLeverage
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
whencontactIdentifier
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 numericConsider using
type: "integer"
forpage
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” typoThe 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 propXero’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 providedMake 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 appThe 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 propUse integer for better validation and UI affordances.
- type: "string", + type: "integer",
61-62
: Avoid “undefined” in summary when no Manual Journal ID is providedMake 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 numericOptional: 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 IDThe 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 hereType 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 providedOn 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 incorrectIt 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 theheaders
parameterThe
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, + }; },
components/xero_accounting_api/actions/create-bank-transaction/create-bank-transaction.mjs
Show resolved
Hide resolved
components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs
Show resolved
Hide resolved
components/xero_accounting_api/actions/create-credit-note/create-credit-note.mjs
Outdated
Show resolved
Hide resolved
… 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.
There was a problem hiding this 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 stringThe 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:expectedPaymentData
→expectedPaymentDate
(+ 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 XeroXero’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$
togetContact
options loader to preserve request contextOmitting
$
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 → purchasesDefaultAccountCodeThis 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 → purchasesTrackingCategoriesSame 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 ACCPAYPurchase bills (ACCPAY) are returned under
Invoices
.PurchaseBillID
doesn’t exist; useInvoices[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 propThe 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 lineItemsThis should be an array, not a single object. Other actions use a
string[]
of JSON strings parsed at runtime viaformatLineItems
.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 fromdata
.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 constraininglineAmountType
to allowed values
To prevent invalid inputs, provide options (e.g., “Exclusive”, “Inclusive”, “NoTax”) or validate inrun
.
3-3
: Nit: align app prop naming with adjacent actions (“xeroAccountingApi”)
Other files often usexeroAccountingApi
as the prop name. This file usesxero
. 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 nameFor 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-onlyThese 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 sendAlso applies to: 218-219
66-76
: Minor: update email description and remove legacy characterIf 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 notype
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 usingany
weakens validation and UI. Recommendstring[]
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 lineAmountTypeAdding 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 descriptionMinor 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 fallbackAvoid 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.
📒 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 tocreateInvoice({ $, 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 shapingUses 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 itShort: 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.
...ctions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs
Show resolved
Hide resolved
...ctions/xero-accounting-create-or-update-contact/xero-accounting-create-or-update-contact.mjs
Show resolved
Hide resolved
...ero_accounting_api/actions/xero-accounting-update-contact/xero-accounting-update-contact.mjs
Show resolved
Hide resolved
There was a problem hiding this 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 createInvoiceUse "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 emailInvoiceUse "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 uploadFileUse "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 crashIf 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 identifierThis 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 identifierThis 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 identifierThis 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 identifierThis 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 URLsRight now absolute URLs work by overriding via
...opts
, which is implicit. Accept aurl
param explicitly and prefer it overpath
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 togetInvoiceById
. If the intent is to download a PDF, ensure callers passheaders: { Accept: "application/pdf" }
and appropriateresponseType
(e.g.,arraybuffer
). Otherwise, consider consolidating withgetInvoiceById
.
📜 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.
📒 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
There was a problem hiding this 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.
There was a problem hiding this 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 objectWithout 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 → purchasesDefaultAccountCodeSpelling 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 purchasesTrackingCategoriesXero 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 patternFollow 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 categoryThis 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 paramsWhen 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 paramsMirror 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 → purchasesDefaultAccountCodePublic 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 parsedXero 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 modifiedAfterXero 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 PagePage 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 phrasingMinor 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: AddincludeArchived
FlagThe Xero wrapper’s
getTrackingCategories(opts = {})
spreads all passed options (…opts
) into its HTTP request, so a top‐levelparams
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 conventionsMost 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 flowsThis 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 nameMinor polish to avoid UI oddities.
- name: "Create or update contact ", + name: "Create or Update Contact",
34-34
: Fix minor typos/punctuation in descriptionsSmall 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 supportedCurrently, 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 countAvoids 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-fatalReturning {} 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 copyfileBinary 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-TypeSome 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 filesConverting 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 errorsCatching 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-invoiceOnly 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 errorsCurrently 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 errorsMirror 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 summaryIf 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 fieldsIf 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 namingProp 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 contactIdWhen 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 descriptionReplace “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 ManualJournalsAvoids 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” propTighten 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” propSmall 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 defaultAdd 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 propsTidy 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.
📒 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 usesgetTrackingCategory
(seecomponents/xero_accounting_api/xero_accounting_api.app.mjs
), and there is nogetTrackingCategoryById
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 changesThis 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 tenantIdSwitching 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 thatcomponents/xero_accounting_api/xero_accounting_api.app.mjs
is reached byimport xeroAccountingApi from "../../xero_accounting_api.app.mjs";
inactions/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 correctThe 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 LGTMUsing 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 goodProps, 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 cleanThe 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 LGTMConsistent 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 goodMatches 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/flowThe 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 goodGuarding 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 goodRequiring 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 — LGTMImport path matches the new app wrapper used across related actions in this PR.
10-16
: PropDefinition for tenantId via app wrapper — LGTMConsistent with other migrated actions in the PR; reduces duplication and centralizes validation.
7-7
: No lingering old prop references inlist-manual-journals
action
Ranrg -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_id
→tenantId
,modified_after
→modifiedAfter
).
• Verify that existing workflows are pinned to the prior version to avoid unintended breaks.
43-53
: ✅ VerifiedlistManualJournals
wrapper signature and parameter casingI’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 yourparams
object withWhere
,order
, andpage
—directly to_makeRequest
- The call site in
actions/list-manual-journals/list-manual-journals.mjs
mirrors the existinglistInvoices
patternNo 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 goodClearer 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 singleWhere
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&
withAND
to form a valid Xero “where” expression.- When only
Name
orEmailAddress
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.
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.", | ||
}, |
There was a problem hiding this comment.
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:
- 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",
+ ],
+ },
- Parse lineItems when mapping:
- LineItems: this.lineItems,
+ LineItems: parseObject(this.lineItems),
- 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
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.", | ||
); | ||
} |
There was a problem hiding this comment.
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.
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.
const findPayload = removeNullEntries({ | ||
Name: name, | ||
EmailAddress: emailAddress, | ||
}); |
There was a problem hiding this comment.
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.
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.
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 {}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
if ( | ||
(!contactDetail || !contactDetail?.Contacts?.length) && | ||
createContactIfNotFound === "Yes" | ||
) { | ||
return await this.xeroAccountingApi.createOrUpdateContact({ | ||
$, | ||
tenantId, | ||
data: createPayload, | ||
}); | ||
} | ||
return response; | ||
return contactDetail; | ||
}, |
There was a problem hiding this comment.
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.
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 {}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
} catch (e) { | ||
$.export("$summary", "No manual journals found"); | ||
return {}; |
There was a problem hiding this comment.
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.
} 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.
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 {}; | ||
} | ||
}, |
There was a problem hiding this comment.
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.
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.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
Resolves #17984
Summary by CodeRabbit
New Features
Improvements
Changes
Maintenance