feat: Add routing-forms record response endpoint with available slots#22239
feat: Add routing-forms record response endpoint with available slots#22239hariombalhara merged 14 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 2 Skipped Deployments
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
✅ No security or compliance issues detected. Reviewed everything up to 889dd32. Security Overview
Detected Code Changes
Reply to this PR with |
74a7400 to
306bf01
Compare
306bf01 to
1fac0f9
Compare
1fac0f9 to
6b93fd0
Compare
E2E results are ready! |
- Fix type mismatch where mockResponse was passed as identifierKeyedResponse - identifierKeyedResponse expects Record<string, string | string[]> structure - Updated test to pass correct data structure for type compatibility Co-Authored-By: hariom@cal.com <hariom@cal.com>
…g in routing forms responses controller - Change @query() to @Body() decorator for POST request data in controller - Update service method to accept parsed body data directly - Remove incorrect URLSearchParams parsing of request.body object - Fix getRoutingUrl method to use form response data parameter This resolves API v2 test failures by following proper NestJS patterns for POST request handling. Co-Authored-By: hariom@cal.com <hariom@cal.com>
00348e5 to
5f935ea
Compare
|
|
||
| export default async function routerGetCrmContactOwnerEmail({ | ||
| attributeRoutingConfig, | ||
| response, |
There was a problem hiding this comment.
Bug-1: The response contains id of the field as they key and not the identifier. So we need to pass the one with identifier as they key here.
The one with id of the field, doesn't have the identifier anywhere and thus can't be compared with email identifier
| for (const identifier of Object.keys(identifierKeyedResponse)) { | ||
| const fieldResponse = identifierKeyedResponse[identifier]; | ||
| if (identifier === "email") { | ||
| prospectEmail = fieldResponse instanceof Array ? fieldResponse[0] : fieldResponse; | ||
| break; | ||
| } |
There was a problem hiding this comment.
Logic changed as per the new response object which is identifier key based.
| } | ||
|
|
||
| if (!contactOwner) { | ||
| if (!contactOwner || (!contactOwner.email && !contactOwner.recordType)) { |
...odules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts
Outdated
Show resolved
Hide resolved
...odules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts
Outdated
Show resolved
Hide resolved
| form: serializableForm, | ||
| formFillerId: uuidv4(), | ||
| response: response, | ||
| identifierKeyedResponse: fieldsResponses, |
There was a problem hiding this comment.
we need to release platform libraries
packages/platform/types/slots/slots-2024-09-04/inputs/get-slots.input.ts
Outdated
Show resolved
Hide resolved
packages/platform/types/slots/slots-2024-09-04/inputs/get-slots.input.ts
Show resolved
Hide resolved
apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts
Outdated
Show resolved
Hide resolved
.../organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts
Outdated
Show resolved
Hide resolved
.../organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts
Outdated
Show resolved
Hide resolved
|
|
||
| return { | ||
| status: SUCCESS_STATUS, | ||
| data: result, |
There was a problem hiding this comment.
if we need to control and hide fields that are not within the DTO use plainToClass combined with @Expose decorator in output DTO class
...odules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts
Outdated
Show resolved
Hide resolved
| if (routedUrlData.props.errorMessage) { | ||
| return { | ||
| status: "error", | ||
| error: { code: "ROUTING_ERROR", message: routedUrlData.props.errorMessage }, | ||
| redirect: false, | ||
| }; |
There was a problem hiding this comment.
This fixes an existing case where in case of form field validation failure, we were still returning success with empty message.
packages/platform/types/slots/slots-2024-09-04/inputs/get-slots.input.ts
Show resolved
Hide resolved
packages/platform/types/slots/slots-2024-09-04/inputs/get-slots.input.ts
Show resolved
Hide resolved
de0bb41 to
c63a7d8
Compare
| action: chosenRoute.action, | ||
| }); | ||
| const contactOwnerQuery = | ||
| identifierKeyedResponse && fetchCrm |
There was a problem hiding this comment.
@joeauyeung By default having this variable as false, so that we intentionally not use the fixed logic for headless router or elsewhere except API v2's new endpoint.
This keeps the behaviour same for headless router as routerGetCrmContactOwnerEmail wasn't returning any contactOwner or other details
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Graphite Automations"Add consumer team as reviewer" took an action on this PR • (07/04/25)1 reviewer was added to this PR based on Keith Williams's automation. |
There was a problem hiding this comment.
cubic found 6 issues across 23 files. Review them in cubic.dev
React with 👍 or 👎 to teach cubic. Tag @cubic-dev-ai to give specific feedback.
apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots-input.service.ts
Show resolved
Hide resolved
...pi/v2/src/modules/organizations/routing-forms/outputs/create-routing-form-response.output.ts
Show resolved
Hide resolved
...pi/v2/src/modules/organizations/routing-forms/outputs/create-routing-form-response.output.ts
Show resolved
Hide resolved
…#22239) * feat: Add routing-forms record response endpoint with available slots * fix: resolve TypeScript error in handleResponse.test.ts - Fix type mismatch where mockResponse was passed as identifierKeyedResponse - identifierKeyedResponse expects Record<string, string | string[]> structure - Updated test to pass correct data structure for type compatibility Co-Authored-By: hariom@cal.com <hariom@cal.com> * fix: correct POST endpoint parameter handling and request body parsing in routing forms responses controller - Change @query() to @Body() decorator for POST request data in controller - Update service method to accept parsed body data directly - Remove incorrect URLSearchParams parsing of request.body object - Fix getRoutingUrl method to use form response data parameter This resolves API v2 test failures by following proper NestJS patterns for POST request handling. Co-Authored-By: hariom@cal.com <hariom@cal.com> * Pass teamMemberEmail as well * Devin fixes reverted * Keep all routing related props together in both endpoints * Remove newly added slots props from Slots documentation as they are used through internal fn call only * fix test * Pass skipContactOwner * Pass crmAppSlug and crmOwnerRecordGType and add more tests * handle external redirect case and form not found case * hide props * chore: bump platform libs --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: hariom@cal.com <hariom@cal.com> Co-authored-by: cal.com <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
…#22239) * feat: Add routing-forms record response endpoint with available slots * fix: resolve TypeScript error in handleResponse.test.ts - Fix type mismatch where mockResponse was passed as identifierKeyedResponse - identifierKeyedResponse expects Record<string, string | string[]> structure - Updated test to pass correct data structure for type compatibility Co-Authored-By: hariom@cal.com <hariom@cal.com> * fix: correct POST endpoint parameter handling and request body parsing in routing forms responses controller - Change @query() to @Body() decorator for POST request data in controller - Update service method to accept parsed body data directly - Remove incorrect URLSearchParams parsing of request.body object - Fix getRoutingUrl method to use form response data parameter This resolves API v2 test failures by following proper NestJS patterns for POST request handling. Co-Authored-By: hariom@cal.com <hariom@cal.com> * Pass teamMemberEmail as well * Devin fixes reverted * Keep all routing related props together in both endpoints * Remove newly added slots props from Slots documentation as they are used through internal fn call only * fix test * Pass skipContactOwner * Pass crmAppSlug and crmOwnerRecordGType and add more tests * handle external redirect case and form not found case * hide props * chore: bump platform libs --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: hariom@cal.com <hariom@cal.com> Co-authored-by: cal.com <morgan@cal.com> Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>


What does this PR do?
Added a new API endpoint to create a routing form response(POST v2/organizations/:orgId/routing-forms/:formId/responses) and returns available booking slots for the matched event type. The response from that endpoint could be utilised to do a booking for a particular timeslot and connect that with the routingForm response
How to connect a response with booking
Send POST request to
https://api.cal.com/v2/organizations/:orgId/routing-forms/:formId/responses?start=2025-06-30T18:30:00.000Z&end=2025-07-23T09:00:00Zwith JSON bodyIt will return a response like
The routing object and eventTypeId and any slot available in slots can be used to create a booking now through
https://api.cal.com/v2/bookings (documented here) endpoint. You can pass the routing object as is. Take a look at this loom to see it all working
Followup
A new endpoint to use the queued response identified by queuedResponseId
Video Demo (if applicable):
https://www.loom.com/share/db67e598548b42789b7072a2e9996057
Mandatory Tasks (DO NOT REMOVE)