mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-04 18:28:54 -06:00
- Remove apps/web/lib/connector/hub-client.ts - Add @formbricks/hub dependency to apps/web - pipeline-handler: create Hub client from env (HUB_API_KEY, HUB_API_URL), call SDK create in batch with same results shape - transform: use FormbricksHub.FeedbackRecordCreateParams from SDK directly Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
4.7 KiB
TypeScript
136 lines
4.7 KiB
TypeScript
import "server-only";
|
|
import type FormbricksHub from "@formbricks/hub";
|
|
import { TConnectorFormbricksMapping, THubFieldType } from "@formbricks/types/connector";
|
|
import { TResponse } from "@formbricks/types/responses";
|
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
|
import { getTextContent } from "@formbricks/types/surveys/validation";
|
|
import { getLocalizedValue } from "@/lib/i18n/utils";
|
|
import { getElementsFromBlocks } from "@/lib/survey/utils";
|
|
|
|
type TResponseValue = string | number | string[] | Record<string, string> | undefined;
|
|
|
|
type TSurveyElement = ReturnType<typeof getElementsFromBlocks>[number];
|
|
|
|
const getHeadlineFromElement = (element: TSurveyElement | undefined): string => {
|
|
if (!element?.headline) return "Untitled";
|
|
const raw = getLocalizedValue(element.headline, "default");
|
|
return getTextContent(raw) || "Untitled";
|
|
};
|
|
|
|
function extractResponseValue(responseData: TResponse["data"], elementId: string): TResponseValue {
|
|
if (!responseData || typeof responseData !== "object") return undefined;
|
|
return (responseData as Record<string, TResponseValue>)[elementId];
|
|
}
|
|
|
|
const convertValueToHubFields = (
|
|
value: TResponseValue,
|
|
hubFieldType: THubFieldType
|
|
): Partial<
|
|
Pick<
|
|
FormbricksHub.FeedbackRecordCreateParams,
|
|
"value_text" | "value_number" | "value_boolean" | "value_date"
|
|
>
|
|
> => {
|
|
if (value === undefined || value === null) {
|
|
return {};
|
|
}
|
|
|
|
switch (hubFieldType) {
|
|
case "text":
|
|
if (typeof value === "string") return { value_text: value };
|
|
if (Array.isArray(value)) return { value_text: value.join(", ") };
|
|
if (typeof value === "object") return { value_text: JSON.stringify(value) };
|
|
return { value_text: String(value) };
|
|
|
|
case "number":
|
|
case "rating":
|
|
case "nps":
|
|
case "csat":
|
|
case "ces":
|
|
if (typeof value === "number") return { value_number: value };
|
|
if (typeof value === "string") {
|
|
const parsed = Number.parseFloat(value);
|
|
if (!Number.isNaN(parsed)) return { value_number: parsed };
|
|
}
|
|
return {};
|
|
|
|
case "boolean":
|
|
if (typeof value === "boolean") return { value_boolean: value };
|
|
if (typeof value === "string") {
|
|
return { value_boolean: value.toLowerCase() === "true" || value === "1" };
|
|
}
|
|
return {};
|
|
|
|
case "date":
|
|
if (typeof value === "string") {
|
|
const date = new Date(value);
|
|
if (!Number.isNaN(date.getTime())) return { value_date: date.toISOString() };
|
|
}
|
|
if (value instanceof Date) return { value_date: value.toISOString() };
|
|
return {};
|
|
|
|
case "categorical":
|
|
if (typeof value === "string") return { value_text: value };
|
|
if (Array.isArray(value)) return { value_text: value.join(", ") };
|
|
return { value_text: String(value) };
|
|
|
|
default:
|
|
return { value_text: typeof value === "string" ? value : String(value) };
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Transform a Formbricks survey response into Hub FeedbackRecord payloads.
|
|
* Called from the pipeline handler when a response is created/finished.
|
|
*/
|
|
export function transformResponseToFeedbackRecords(
|
|
response: TResponse,
|
|
survey: TSurvey,
|
|
mappings: TConnectorFormbricksMapping[],
|
|
tenantId?: string
|
|
): FormbricksHub.FeedbackRecordCreateParams[] {
|
|
const responseData = response.data;
|
|
if (!responseData) return [];
|
|
|
|
const surveyMappings = mappings.filter((m) => m.surveyId === survey.id);
|
|
const elements = getElementsFromBlocks(survey.blocks);
|
|
const elementMap = new Map(elements.map((el) => [el.id, el]));
|
|
const feedbackRecords: FormbricksHub.FeedbackRecordCreateParams[] = [];
|
|
|
|
for (const mapping of surveyMappings) {
|
|
const value = extractResponseValue(responseData, mapping.elementId);
|
|
if (value === undefined || value === null || value === "") continue;
|
|
|
|
const fieldLabel = mapping.customFieldLabel || getHeadlineFromElement(elementMap.get(mapping.elementId));
|
|
const valueFields = convertValueToHubFields(value, mapping.hubFieldType);
|
|
|
|
const feedbackRecord: FormbricksHub.FeedbackRecordCreateParams = {
|
|
collected_at:
|
|
response.createdAt instanceof Date ? response.createdAt.toISOString() : String(response.createdAt),
|
|
source_type: "formbricks",
|
|
field_id: mapping.elementId,
|
|
field_type: mapping.hubFieldType,
|
|
source_id: survey.id,
|
|
source_name: survey.name,
|
|
field_label: fieldLabel,
|
|
...valueFields,
|
|
};
|
|
|
|
if (response.language && response.language !== "default") {
|
|
feedbackRecord.language = response.language;
|
|
}
|
|
|
|
if (tenantId) {
|
|
feedbackRecord.tenant_id = tenantId;
|
|
}
|
|
|
|
if (response.contact?.userId) {
|
|
feedbackRecord.user_identifier = response.contact.userId;
|
|
}
|
|
|
|
feedbackRecords.push(feedbackRecord);
|
|
}
|
|
|
|
return feedbackRecords;
|
|
}
|