Fix confirmations and notifications validation to accept objects#3
Conversation
The validateRestApiAccess function was calling getAuthHeaders() without the required URL parameter for OAuth 1.0a signature generation. This caused authentication failures when using HTTP connections (which fall back to OAuth instead of Basic Auth). Changes: - Get baseURL from httpClient.defaults.baseURL - Construct full URL for each endpoint - Pass proper parameters (method, url, params) to getAuthHeaders() This fixes the "REST API validation failed: undefined" error when connecting via HTTP/OAuth.
The Gravity Forms REST API v2 returns confirmations and notifications as objects keyed by ID, not arrays. The validation was incorrectly using validateArray()/`.array()` which rejected the correct format. Changed both validators to use validateObject()/`.object()` and added missing notifications validation.
WalkthroughRefactors confirmations/notifications from arrays to keyed objects across validation, schema, tests, and test data; updates OAuth signing to build and validate full endpoint URLs from Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/config/auth.js (1)
327-345:⚠️ Potential issue | 🟡 Minor
baseURLrequirement applies universally even for Basic Auth.
BasicAuthHandler.getAuthHeaders()accepts no arguments and ignoresfullUrl, so the mandatorybaseURLcheck at lines 323–325 adds a new runtime requirement forvalidateRestApiAccesseven in Basic Auth deployments. A customhttpClientthat lacksdefaults.baseURLwould cause this health-check to fail. Consider short-circuiting the check when the active auth handler isBasicAuthHandler.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/config/auth.js` around lines 327 - 345, The health-check adds a mandatory baseURL requirement even for BasicAuth deployments; update validateRestApiAccess to detect when the active auth handler is BasicAuthHandler (e.g., via authManager.activeHandler or checking authManager.getAuthHeaders implementation/constructor name) and skip the baseURL existence check in that case, and when calling authManager.getAuthHeaders use the signature appropriate for the handler (call getAuthHeaders('GET', fullUrl, ...) only for handlers that accept a full URL, otherwise call BasicAuthHandler.getAuthHeaders() with no args), keeping the httpClient.get call using endpoint.path and not assuming httpClient.defaults.baseURL exists.
🧹 Nitpick comments (1)
src/config/validators.js (1)
90-106:!Array.isArray(confirmations)guard is redundant after.object().The
.object()chain rule already rejects arrays before the custom callback runs, making this condition unreachable. Theconfirmations &&null/undefined guard is still needed, but the array check can be dropped.♻️ Proposed simplification
.custom((confirmations) => { - if (confirmations && typeof confirmations === 'object' && !Array.isArray(confirmations)) { + if (confirmations && typeof confirmations === 'object') { Object.entries(confirmations).forEach(([key, conf]) => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/config/validators.js` around lines 90 - 106, In the custom validator attached to field('confirmations') (the validate('confirmations').object().custom(...) block), remove the redundant Array.isArray(confirmations) check since .object() already rejects arrays; keep the null/undefined guard (confirmations && typeof confirmations === 'object') so the custom logic still skips when confirmations is missing, and otherwise iterate Object.entries(confirmations) to validate redirect entries as before (i.e., call validate(`confirmations.${key}.url`).required().string().url().validate(conf.url) when conf.type === 'redirect' and conf.url !== undefined).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/config/auth.js`:
- Around line 320-325: The code reads httpClient.defaults.baseURL directly which
can throw if httpClient.defaults is undefined and get swallowed by the outer
catch; change the lookup to use optional chaining (e.g., access
httpClient?.defaults?.baseURL) and keep the existing explicit guard that throws
'httpClient baseURL is not configured' when baseURL is falsy so the intended
error message is produced; update the reference in the same block where baseURL
is declared (the httpClient.defaults/baseURL access) to use optional chaining.
In `@src/config/validation.js`:
- Around line 311-317: validateObject currently rejects arrays, but
formData.confirmations is treated as an array in multiple tests and scripts;
update all places that set confirmations to use the keyed-object Gravity Forms
v2 shape instead of arrays. Replace array usage in
src/tests/integration.test.js, src/tests/validation.test.js, and
scripts/setup-test-data.js so confirmations are objects keyed like
"confirmation_1": { type: ..., url: ... } (so
validateObject(formData.confirmations, 'confirmations') and the subsequent loop
that calls validateURL(conf.url, `confirmations.${key}.url`) will work without
errors).
---
Outside diff comments:
In `@src/config/auth.js`:
- Around line 327-345: The health-check adds a mandatory baseURL requirement
even for BasicAuth deployments; update validateRestApiAccess to detect when the
active auth handler is BasicAuthHandler (e.g., via authManager.activeHandler or
checking authManager.getAuthHeaders implementation/constructor name) and skip
the baseURL existence check in that case, and when calling
authManager.getAuthHeaders use the signature appropriate for the handler (call
getAuthHeaders('GET', fullUrl, ...) only for handlers that accept a full URL,
otherwise call BasicAuthHandler.getAuthHeaders() with no args), keeping the
httpClient.get call using endpoint.path and not assuming
httpClient.defaults.baseURL exists.
---
Nitpick comments:
In `@src/config/validators.js`:
- Around line 90-106: In the custom validator attached to field('confirmations')
(the validate('confirmations').object().custom(...) block), remove the redundant
Array.isArray(confirmations) check since .object() already rejects arrays; keep
the null/undefined guard (confirmations && typeof confirmations === 'object') so
the custom logic still skips when confirmations is missing, and otherwise
iterate Object.entries(confirmations) to validate redirect entries as before
(i.e., call
validate(`confirmations.${key}.url`).required().string().url().validate(conf.url)
when conf.type === 'redirect' and conf.url !== undefined).
- Use optional chaining for httpClient.defaults.baseURL access to prevent TypeError when defaults is undefined (auth.js) - Remove redundant Array.isArray guard in validators.js since .object() already rejects arrays - Convert confirmations from array to keyed-object format in tests and setup scripts to match Gravity Forms API v2 structure
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/tests/validation.test.js (1)
240-258: Consider adding a type validation test fornotifications.The existing "Type Validation: Objects" tests cover
searchandmeta, but there's no explicit test asserting thatnotificationsrejects non-object values (e.g., passing a string or array). Sincenotificationsis a newly added validated field, a quick test would solidify coverage.💡 Suggested test addition (outside of current line range)
// Notifications should be object await TestAssert.throwsAsync( () => client.createForm({ title: 'Test', notifications: 'not-an-object' }), 'must be an object', 'Notifications parameter should be object' );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tests/validation.test.js` around lines 240 - 258, Add a type validation test asserting that the new notifications field rejects non-object values: in the existing "Type Validation: Objects" suite add a TestAssert.throwsAsync call that invokes client.createForm with notifications set to a non-object (e.g., a string or array) and expects the error message 'must be an object' and a descriptive assertion message like 'Notifications parameter should be object'; reference the client.createForm call and the notifications field when locating where to insert the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/tests/validation.test.js`:
- Around line 240-258: Add a type validation test asserting that the new
notifications field rejects non-object values: in the existing "Type Validation:
Objects" suite add a TestAssert.throwsAsync call that invokes client.createForm
with notifications set to a non-object (e.g., a string or array) and expects the
error message 'must be an object' and a descriptive assertion message like
'Notifications parameter should be object'; reference the client.createForm call
and the notifications field when locating where to insert the test.
|
@claude Verify each finding against the current code and only fix it if needed. Nitpick comments:
|
Add 1.0.4 and 1.0.5 changelog entries. 1.0.5 includes OAuth signature generation fix and confirmations/notifications validation fix (PR #3).
Summary
confirmationsandnotificationsvalidation to accept objects (keyed by ID) instead of arraysnotificationsvalidation in both legacy and new validator pathsProblem
The Gravity Forms REST API v2 returns
confirmationsandnotificationsas objects keyed by ID, not arrays. However, both validators incorrectly enforced array validation:validators.js: Used.array()on the confirmations fieldvalidation.js: CalledvalidateArray()on confirmationsThis caused all
gf_create_formandgf_update_formcalls with confirmations or notifications to fail with:Additionally,
notificationshad no validation at all in either validator, meaning they were silently stripped from the validated output.Steps to Reproduce
{ "title": "Test Form", "confirmations": { "abc123": { "id": "abc123", "name": "Default Confirmation", "type": "message", "message": "Thanks!" } } }Validation error: confirmations must be an arraySolution
validateArray()→validateObject()for confirmations invalidation.js.array()→.object()for confirmations invalidators.js.map(),.forEach()) toObject.entries()notificationsvalidation as.object()in both validatorsTest plan
gf_update_formwith confirmations object succeeds (previously failed)gf_update_formwith notifications object succeeds (previously stripped)Summary by CodeRabbit
New Features
Bug Fixes
Tests