Skip to content

feat(quickbooks): add generic query action#21

Draft
mchlggr wants to merge 1 commit into
mainfrom
codex/qb-nango-query-entities
Draft

feat(quickbooks): add generic query action#21
mchlggr wants to merge 1 commit into
mainfrom
codex/qb-nango-query-entities

Conversation

@mchlggr

@mchlggr mchlggr commented May 18, 2026

Copy link
Copy Markdown
Contributor

Adds a generic QuickBooks query-entities Nango action based on Nango's template. It runs SQL-like QBO queries, applies start/max pagination when omitted, and returns normalized records/count/paging metadata. Registered for both quickbooks and quickbooks-sandbox without duplicating implementation. Deployed to Nango dev for both providers; compile/typecheck passed.

@mchlggr mchlggr requested a review from a team May 18, 2026 13:51
@mchlggr mchlggr marked this pull request as draft May 18, 2026 13:51
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Walkthrough

This PR introduces a new query-entities action for QuickBooks integrations that enables SQL-like queries over supported entities with pagination. The implementation includes shared validation schemas and execution logic that constructs paginated requests, calls the QuickBooks /v3/company/{companyId}/query endpoint, parses and validates responses, and returns normalized pagination metadata. Both primary QuickBooks and QuickBooks Sandbox variants are defined using the shared logic and registered in the root action index.

Poem

🐰 A fluffy query hops through QB today,
With pagination clauses all in place to stay,
Shared schemas and sandbox friends side-by-side,
Extracting entities with validation as guide,
One action, two homes—what a hoppy delight! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a generic query action for QuickBooks integrations across multiple namespaces.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/qb-nango-query-entities

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 954912b1da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +105 to +106
const withStart = /\\bSTARTPOSITION\\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
return /\\bMAXRESULTS\\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Detect pagination clauses with real word-boundary regex

The pagination guard regexes are double-escaped (/\\b...\\b/), so they never match existing STARTPOSITION or MAXRESULTS tokens in user-provided queries. As a result, queries that already include either clause will have duplicates appended, which can produce invalid QuickBooks query strings or unexpected paging behavior for callers who pass explicit pagination.

Useful? React with 👍 / 👎.

Comment on lines +124 to +126
const parsed = responseSchema.parse(response.data);
const queryResponse = parsed.QueryResponse ?? { maxResults: 0, startPosition: 1, totalCount: 0 };
const records = extractRecords(queryResponse);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Surface QuickBooks Fault responses instead of returning empty data

When QuickBooks returns a Fault payload without QueryResponse (e.g., malformed query or permission error), this code parses successfully and falls back to an empty synthetic response, returning records: [] instead of raising an error. That silently converts real API failures into apparent successful empty results, which can hide production issues and mislead downstream automation.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nango.dev/quickbooks/actions/query-entities-shared.ts`:
- Around line 51-53: The responseSchema currently marks QueryResponse as
optional which masks malformed responses; change responseSchema to require
QueryResponse (remove .optional()) and remove any code that supplies a fallback
empty QueryResponse object when parsing/validating (ensure you use the strict
zod parsing on responseSchema and let validation fail), referencing the
responseSchema and queryResponseSchema symbols so the validator enforces a
present QueryResponse instead of defaulting to { maxResults: 0, startPosition:
1, totalCount: 0 }.
- Around line 105-106: The regexes used to detect existing pagination clauses
are incorrectly escaped (they contain literal backslashes), so change the
patterns used when testing query to use real word-boundary tokens rather than
backslash characters; update the two tests that reference STARTPOSITION and
MAXRESULTS (the expressions around withStart, query, startPosition, and
maxResults) to use /\bSTARTPOSITION\b/i and /\bMAXRESULTS\b/i (preserving the i
flag) so existing clauses are detected correctly and duplicates are not
appended.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9ef97deb-dae8-4b38-b890-6b0bf76e337e

📥 Commits

Reviewing files that changed from the base of the PR and between 1887063 and 954912b.

📒 Files selected for processing (4)
  • nango.dev/index.ts
  • nango.dev/quickbooks-sandbox/actions/query-entities.ts
  • nango.dev/quickbooks/actions/query-entities-shared.ts
  • nango.dev/quickbooks/actions/query-entities.ts
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-05-07T15:17:35.164Z
Learnt from: mchlggr
Repo: ariso-ai/link PR: 6
File: nango.dev/index.ts:17-23
Timestamp: 2026-05-07T15:17:35.164Z
Learning: In this repository, TypeScript ESM import specifiers intentionally use `.js` extensions (e.g., `import './path/to/file.js'`) even when the referenced source files are written in `.ts`. Treat `.js` extensions in TS/TSX import paths as correct for this project’s established convention—do not flag them as incorrect, since the project’s ESM TypeScript setup resolves the `.js`-suffixed specifiers to `.ts` sources.

Applied to files:

  • nango.dev/quickbooks/actions/query-entities.ts
  • nango.dev/quickbooks-sandbox/actions/query-entities.ts
  • nango.dev/index.ts
  • nango.dev/quickbooks/actions/query-entities-shared.ts
🔇 Additional comments (3)
nango.dev/quickbooks/actions/query-entities.ts (1)

1-14: LGTM!

nango.dev/quickbooks-sandbox/actions/query-entities.ts (1)

1-18: LGTM!

nango.dev/index.ts (1)

31-32: LGTM!

Comment on lines +51 to +53
const responseSchema = z.object({
QueryResponse: queryResponseSchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In the QuickBooks Online Accounting API v3 query endpoint, does every successful response include a top-level QueryResponse object?

💡 Result:

Yes, every successful response from the QuickBooks Online Accounting API v3 query endpoint includes a top-level QueryResponse object [1]. When you execute a query using the /query endpoint, the API returns a JSON (or XML) payload where the root element is QueryResponse [1]. This object acts as a container for the requested data, which is nested within it under the name of the entity being queried (e.g., Customer, Invoice) [1]. If no records match the query criteria, the QueryResponse object will still be present, but the entity-specific collection within it may be empty or omitted [2][1].

Citations:


🏁 Script executed:

fd query-entities-shared.ts --type f

Repository: ariso-ai/link

Length of output: 111


🏁 Script executed:

cat -n nango.dev/quickbooks/actions/query-entities-shared.ts | head -150

Repository: ariso-ai/link

Length of output: 6186


Remove the optional flag and fallback to prevent masking malformed API responses.

QuickBooks Online Accounting API v3 always returns a QueryResponse object in successful responses. Making it optional and defaulting to an empty object { maxResults: 0, startPosition: 1, totalCount: 0 } silently masks unexpected or malformed payloads as a successful empty result instead of surfacing the error.

Proposed fix
 const responseSchema = z.object({
-    QueryResponse: queryResponseSchema.optional(),
+    QueryResponse: queryResponseSchema,
 });
@@
-    const queryResponse = parsed.QueryResponse ?? { maxResults: 0, startPosition: 1, totalCount: 0 };
+    const queryResponse = parsed.QueryResponse;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nango.dev/quickbooks/actions/query-entities-shared.ts` around lines 51 - 53,
The responseSchema currently marks QueryResponse as optional which masks
malformed responses; change responseSchema to require QueryResponse (remove
.optional()) and remove any code that supplies a fallback empty QueryResponse
object when parsing/validating (ensure you use the strict zod parsing on
responseSchema and let validation fail), referencing the responseSchema and
queryResponseSchema symbols so the validator enforces a present QueryResponse
instead of defaulting to { maxResults: 0, startPosition: 1, totalCount: 0 }.

Comment on lines +105 to +106
const withStart = /\\bSTARTPOSITION\\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
return /\\bMAXRESULTS\\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the current file contains double-escaped word-boundary markers.
rg -nP '\/\\\\bSTARTPOSITION\\\\b\/i|\/\\\\bMAXRESULTS\\\\b\/i' nango.dev/quickbooks/actions/query-entities-shared.ts

Repository: ariso-ai/link

Length of output: 277


🏁 Script executed:

#!/bin/bash
# Read lines 105-106 directly from the file to see exact source content
sed -n '105,106p' nango.dev/quickbooks/actions/query-entities-shared.ts

Repository: ariso-ai/link

Length of output: 269


🏁 Script executed:

#!/bin/bash
# Read the exact content using Python to see the real characters
python3 << 'EOF'
with open('nango.dev/quickbooks/actions/query-entities-shared.ts', 'r') as f:
    lines = f.readlines()
    for i in range(104, 106):  # 0-indexed, so 104-105 for lines 105-106
        line = lines[i]
        print(f"Line {i+1}: {repr(line)}")
EOF

Repository: ariso-ai/link

Length of output: 305


🏁 Script executed:

#!/bin/bash
# Read the full appendPagination function context
sed -n '95,115p' nango.dev/quickbooks/actions/query-entities-shared.ts

Repository: ariso-ai/link

Length of output: 879


Fix regex word boundary escaping to properly detect existing pagination clauses.

The patterns /\\bSTARTPOSITION\\b/i and /\\bMAXRESULTS\\b/i match literal backslash characters instead of word boundaries, so clause detection fails and duplicate pagination parameters can be appended.

Proposed fix
-    const withStart = /\\bSTARTPOSITION\\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
-    return /\\bMAXRESULTS\\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;
+    const withStart = /\bSTARTPOSITION\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
+    return /\bMAXRESULTS\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;
📝 Committable suggestion

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

Suggested change
const withStart = /\\bSTARTPOSITION\\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
return /\\bMAXRESULTS\\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;
const withStart = /\bSTARTPOSITION\b/i.test(query) ? query : `${query} STARTPOSITION ${startPosition}`;
return /\bMAXRESULTS\b/i.test(withStart) ? withStart : `${withStart} MAXRESULTS ${maxResults}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nango.dev/quickbooks/actions/query-entities-shared.ts` around lines 105 -
106, The regexes used to detect existing pagination clauses are incorrectly
escaped (they contain literal backslashes), so change the patterns used when
testing query to use real word-boundary tokens rather than backslash characters;
update the two tests that reference STARTPOSITION and MAXRESULTS (the
expressions around withStart, query, startPosition, and maxResults) to use
/\bSTARTPOSITION\b/i and /\bMAXRESULTS\b/i (preserving the i flag) so existing
clauses are detected correctly and duplicates are not appended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant