feat(connectors): HubSpot checks follow the design's rows, copy and states
Merged
EV
4d ago+1190+1190+0123456789012345678901234567890123456789-204-204-012345678901234567890123456789
Description
Backend PR for the connector checks redesign, stacked on #16199. HubSpot follows the design's rows, copy and states.
HubSpot
- Rows:
- The 9 checks are the design's 9 rows, with its names: "Sign in to HubSpot", "Read tickets", "Read companies", "Read deals", "Read contacts", "List linked records", "Sample notes", "List users" and "Read record viewers".
- Dependencies follow the design's run order, with one change. "List linked records" and "Sample notes" run after the sign-in, not after the four read checks. A dependency that finds nothing to test stops the checks after it. Many portals have no tickets, so "No tickets in HubSpot" would stop both checks, but they can test a record of another type. The two checks sample the first type that lists a record, and they leave out a denied type: its read check reports it.
- Copy:
- Every failure gives the design's line and description.
- A denial names the private app scope that HubSpot needs, e.g. "In HubSpot, add
crm.objects.deals.readto the private app's scopes." - Passing checks give what they found: "Signed in to account {portal_id}", "Found {n} tickets" (the count from the Search API), "Listed contacts linked to tickets", "Read a note linked to a ticket", "{n} users found", "{n} users can view a sample ticket". Singular and plural forms are correct ("Found 1 company").
- Honest passes. These checks passed while testing nothing:
- A type with no records is now Skipped ("No tickets in HubSpot").
- When no selected type has a record, the connector would index nothing. The first read check now fails ("No records in the selected types"). This blocks Create.
- No record in scope is now Skipped for "List linked records", "Sample notes" and "Read record viewers" ("No records in scope").
- A sample record without notes is now Incomplete ("No notes found to test").
- No listed user is now Incomplete ("No users found to test").
Fixes from the design's review
- Rate limits. After the rate limiter's 5 retries, a check raises
RateLimitedValidationError, which stops the run. Before, the limit was an unexpected error. - Sign-in. It now reports a missing token ("Account has no API token"), an unreachable HubSpot ("Cannot reach HubSpot") and a timeout ("Sign-in timed out"). On a form run, an unreachable HubSpot or a timeout fails sign-in. A 5xx or an answer that is not JSON is "Unexpected response from HubSpot" (Incomplete).
- Error Details.
HubSpotApiErroris aSourceApiError. The gateway adds the method, the URL and HubSpot's error body, for SDK and REST calls, so Error Details show them. - Connection errors. The gateway raises
HubSpotConnectionError(withtimed_out) for a connection failure or a timeout, from the SDK and from REST calls. The checks cannot importrequests(the SDK import fence). - Permission sync validation.
validate_hubspot_perm_syncaccepts a portal with no records. Before, the viewers check passed silently there; now it raisesNothingToCheckError, which would pause the connector.
Not in this PR:
validates_bindingon "Sign in to HubSpot". The design says sign-in unlocks the form, but HubSpot's config has no credential-bound fields, and the framework requires a binding class for a binding check (test_binding_checks_require_only_bound_fields). GitHub and Google Drive also leave it out.- The design's HubSpot text for the 10-minute hang guard ("The check stopped after 10 minutes without an answer from HubSpot."). The runner writes this row text for every source. A per-source text needs a framework change.
How Has This Been Tested?
- New unit tests (
test_hubspot_check_row_text.py): the sign-in lines, the form-run policy (unreachable and timeout), the rate-limit stop, the dependency stops, Error Details, the empty-type and empty-scope states, the samples' Skipped and Incomplete states, and that "List linked records" runs past an empty type. - New gateway tests: SDK and REST errors carry the request and body; a connection failure says whether it timed out; a search keeps its match count.
- Existing HubSpot check tests updated to the new states.
- Connector, server and EE unit tests: 5519 passed, 25 skipped, 1 failed. The failed test is a web connector test (
test_browser_lifecycle.py). It also fails without this change.tyand pre-commit are clean. - HubSpot has no daily live capability-check test, so there is nothing to update there.
Additional Options
- [Optional] Please cherry-pick this PR to the latest release version.
- [Optional] Override Linear Check
Generated by Claude Code
Summary by cubic
Rewrites the HubSpot capability checks to follow the checks design: nine rows with the design's names, run order, copy, and outcome states. Checks now report what they tested and what they found instead of passing silently.
- "List linked records" and "Sample notes" run after sign-in only, so an empty or denied type no longer stops them.
- Read checks report Search API counts with singular and plural forms; a full users page reports a "100+" lower bound.
- Empty types are Skipped, a record without notes or no listed user is Incomplete, and a portal with no records of any selected type fails the first read check to block Create.
- Rate limits stop the run after retries with a clear message; sign-in reports missing tokens, unreachable HubSpot, and timeouts, failing a form run on the latter two.
- HubSpot API errors now carry the request method, URL, and response body so Error Details show them; connection failures raise a dedicated
HubSpotConnectionError. - Permission sync validation accepts a portal with no records instead of raising.
- Row copy uses the shared
CheckRowTextmodel, and the check tests share a helpers module.
Summary by CodeRabbit
- Improvements
- HubSpot connection checks now distinguish common issues, including invalid credentials, missing permissions, rate limits, and connectivity problems.
- Permission checks provide clearer results when selected data is empty or inaccessible, and can continue checking other readable record types.
- User checks report the number of users found, while record-viewer checks report viewer counts when a sample is available.
- HubSpot requests handle timeouts and connection failures more consistently.