fix: update trigger function for RoutingFormResponseField (#21868)

* fix: update trigger function for RoutingFormResponseField

* Update packages/prisma/migrations/20250617123959_update_handle_routing_form_response_fields/migration.sql

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* add reusable function

* handle number type more flexibly

* handle multiselect value flexibly

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-06-17 20:47:43 +02:00
1 parent 4f0b35d9e2
commit 6a13d7c5c6
1 file changed
+207
@@ -0,0 +1,207 @@
-- Shared function that contains the core processing logic
CREATE OR REPLACE FUNCTION _process_routing_form_response_fields(response_id integer, response_data jsonb, form_id text)
RETURNS void AS $$
DECLARE
form_fields jsonb;
field_record jsonb;
response_field jsonb;
field_type text;
BEGIN
-- Validate response_data exists and is a valid JSON object
IF response_data IS NULL OR jsonb_typeof(response_data) != 'object' THEN
RAISE WARNING 'Invalid response data for id %. Type: %', response_id, COALESCE(jsonb_typeof(response_data), 'null');
RETURN;
END IF;
-- Get the fields from App_RoutingForms_Form
SELECT fields::jsonb INTO form_fields
FROM "App_RoutingForms_Form"
WHERE id = form_id;
-- Delete existing entries for this response
DELETE FROM "RoutingFormResponseField"
WHERE "responseId" = response_id;
-- Exit early if form_fields is NULL or not an array
IF form_fields IS NULL OR jsonb_typeof(form_fields) != 'array' THEN
RAISE WARNING 'form_fields is NULL or not an array for formId %, skipping processing', form_id;
RETURN;
END IF;
-- Iterate through each field in the response
FOR field_record IN SELECT * FROM jsonb_array_elements(form_fields)
LOOP
BEGIN
-- Validate field_record has required properties
IF field_record->>'id' IS NULL THEN
RAISE WARNING 'Field record is missing id property, skipping field';
CONTINUE;
END IF;
IF field_record->>'type' IS NULL THEN
RAISE WARNING 'Field record % is missing type property, skipping field', field_record->>'id';
CONTINUE;
END IF;
-- Get the response field object for this field
response_field := response_data->(field_record->>'id');
-- Skip if no response for this field
IF response_field IS NULL THEN
CONTINUE;
END IF;
-- Get field type
field_type := field_record->>'type';
-- Insert new record based on field type
IF field_type = 'multiselect' THEN
-- Handle array values for multiselect
-- Accept both array values and string values
IF response_field->>'value' IS NOT NULL THEN
BEGIN
IF jsonb_typeof(response_field->'value') = 'array' THEN
-- If it's already an array, use it directly
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueStringArray")
VALUES (
response_id,
field_record->>'id',
ARRAY(SELECT jsonb_array_elements_text(response_field->'value'))
);
ELSIF jsonb_typeof(response_field->'value') = 'string' THEN
-- If it's a string, convert it to a single-element array
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueStringArray")
VALUES (
response_id,
field_record->>'id',
ARRAY[response_field->>'value']
);
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to insert multiselect values for field %', field_record->>'id';
END;
END IF;
ELSIF field_type = 'number' THEN
-- Handle number values
-- Accept both JSON number values and string values that can be converted to numbers
IF response_field->>'value' IS NOT NULL THEN
BEGIN
IF jsonb_typeof(response_field->'value') = 'number' THEN
-- If it's already a JSON number, use it directly
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueNumber")
VALUES (
response_id,
field_record->>'id',
(response_field->>'value')::decimal
);
ELSIF jsonb_typeof(response_field->'value') = 'string' THEN
-- If it's a string, try to convert it to a number
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueNumber")
VALUES (
response_id,
field_record->>'id',
(response_field->>'value')::decimal
);
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to insert number value for field % (value: %)', field_record->>'id', response_field->>'value';
END;
END IF;
ELSIF field_type = 'select' THEN
-- Handle select values - can be either string or array
IF response_field->>'value' IS NOT NULL THEN
BEGIN
IF jsonb_typeof(response_field->'value') = 'array' THEN
-- If it's an array, take the first element
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueString")
VALUES (
response_id,
field_record->>'id',
(response_field->'value'->>0)
);
ELSIF jsonb_typeof(response_field->'value') = 'string' THEN
-- If it's a string, use it directly
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueString")
VALUES (
response_id,
field_record->>'id',
response_field->>'value'
);
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to insert select value for field %', field_record->>'id';
END;
END IF;
ELSE
-- Handle all other types as strings
-- Only insert if value is not null and is a valid string
IF response_field->>'value' IS NOT NULL AND jsonb_typeof(response_field->'value') = 'string' THEN
BEGIN
INSERT INTO "RoutingFormResponseField" ("responseId", "fieldId", "valueString")
VALUES (
response_id,
field_record->>'id',
response_field->>'value'
);
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to insert string value for field %', field_record->>'id';
END;
END IF;
END IF;
EXCEPTION WHEN OTHERS THEN
-- Log the error and continue processing other fields
RAISE WARNING 'Error processing field %', field_record->>'id';
CONTINUE;
END;
END LOOP;
EXCEPTION WHEN OTHERS THEN
-- Log any unhandled errors and continue
RAISE WARNING 'Unhandled error in _process_routing_form_response_fields for response %', response_id;
END;
$$ LANGUAGE plpgsql;
-- Updated trigger function that uses the shared core function
CREATE OR REPLACE FUNCTION handle_routing_form_response_fields()
RETURNS TRIGGER AS $$
DECLARE
response_data jsonb;
form_id text;
BEGIN
-- For INSERT trigger on RoutingFormResponseDenormalized, we need to get the response data from App_RoutingForms_FormResponse
-- For UPDATE trigger on App_RoutingForms_FormResponse, we can use NEW.response directly
IF TG_TABLE_NAME = 'RoutingFormResponseDenormalized' THEN
SELECT response, "formId" INTO response_data, form_id
FROM "App_RoutingForms_FormResponse"
WHERE id = NEW.id;
ELSE
response_data := NEW.response::jsonb;
form_id := NEW."formId";
END IF;
-- Call the shared core function
PERFORM _process_routing_form_response_fields(NEW.id, response_data, form_id);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- New function to reprocess routing form response fields for a specific response ID
CREATE OR REPLACE FUNCTION reprocess_routing_form_response_fields(response_id integer)
RETURNS void AS $$
DECLARE
response_data jsonb;
form_id text;
BEGIN
-- Get the response data and form ID from App_RoutingForms_FormResponse
SELECT response, "formId" INTO response_data, form_id
FROM "App_RoutingForms_FormResponse"
WHERE id = response_id;
-- Check if the response exists
IF response_data IS NULL THEN
RAISE EXCEPTION 'Response with id % not found', response_id;
END IF;
-- Call the shared core function
PERFORM _process_routing_form_response_fields(response_id, response_data, form_id);
END;
$$ LANGUAGE plpgsql;