From 4d07d40a4fc80ea10adfba7fd89a95164619e069 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:07:56 +0000 Subject: [PATCH 1/3] Initial plan From cf3505545553045dcecbc274393276b95c2bcdc7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:16:01 +0000 Subject: [PATCH 2/3] Add QuestionSerializer to normalize question positions Co-authored-by: rileyseaburg <48305658+rileyseaburg@users.noreply.github.com> --- app/serializers/form_serializer.rb | 6 +++- app/serializers/full_form_serializer.rb | 6 +++- app/serializers/question_serializer.rb | 25 +++++++++++++++ .../api/v1/forms_controller_spec.rb | 31 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 app/serializers/question_serializer.rb diff --git a/app/serializers/form_serializer.rb b/app/serializers/form_serializer.rb index db1f0b998..97704d254 100644 --- a/app/serializers/form_serializer.rb +++ b/app/serializers/form_serializer.rb @@ -60,6 +60,10 @@ class FormSerializer < ActiveModel::Serializer :last_response_created_at, :tag_list - has_many :questions + has_many :questions, serializer: QuestionSerializer belongs_to :service + + def questions + object.ordered_questions + end end diff --git a/app/serializers/full_form_serializer.rb b/app/serializers/full_form_serializer.rb index 7f491a1c8..6b2cd5619 100644 --- a/app/serializers/full_form_serializer.rb +++ b/app/serializers/full_form_serializer.rb @@ -79,9 +79,13 @@ def links :response_count, :last_response_created_at - has_many :questions + has_many :questions, serializer: QuestionSerializer has_many :submissions + def questions + object.ordered_questions + end + def submissions object.submissions.order(:id).where('created_at BETWEEN ? AND ?', start_date, end_date).limit(size).offset(size * page) end diff --git a/app/serializers/question_serializer.rb b/app/serializers/question_serializer.rb new file mode 100644 index 000000000..bac490fb8 --- /dev/null +++ b/app/serializers/question_serializer.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class QuestionSerializer < ActiveModel::Serializer + attributes :id, + :form_id, + :text, + :question_type, + :answer_field, + :position, + :is_required, + :created_at, + :updated_at, + :form_section_id, + :character_limit, + :placeholder_text, + :help_text + + has_many :question_options + + def position + # Normalize position based on the order within the form's ordered questions + form_questions = object.form.ordered_questions + form_questions.index(object) + 1 + end +end \ No newline at end of file diff --git a/spec/controllers/api/v1/forms_controller_spec.rb b/spec/controllers/api/v1/forms_controller_spec.rb index 6ad2147fe..6120f8610 100644 --- a/spec/controllers/api/v1/forms_controller_spec.rb +++ b/spec/controllers/api/v1/forms_controller_spec.rb @@ -255,6 +255,37 @@ expect(response.status).to eq(400) end end + + context 'question positions' do + let!(:user) { FactoryBot.create(:user) } + let!(:organization) { FactoryBot.create(:organization) } + let!(:form) { FactoryBot.create(:form, organization: organization) } + let!(:user_role) { FactoryBot.create(:user_role, :form_manager, user:, form:) } + + before do + # Create questions with gaps in positions (simulating deleted questions) + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 1, answer_field: 'answer_01') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 3, answer_field: 'answer_02') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 5, answer_field: 'answer_03') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 6, answer_field: 'answer_04') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 7, answer_field: 'answer_05') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 16, answer_field: 'answer_06') + + user.update(api_key: TEST_API_KEY) + request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(ENV.fetch('API_HTTP_USERNAME'), ENV.fetch('API_HTTP_PASSWORD')) + get :show, format: :json, params: { id: form.short_uuid, 'API_KEY' => user.api_key } + @parsed_response = JSON.parse(response.body) + end + + it 'normalizes question positions to be sequential starting from 1' do + questions_data = @parsed_response['data']['relationships']['questions']['data'] + expect(questions_data.size).to eq(6) + + # Question positions should be normalized to 1, 2, 3, 4, 5, 6 + positions = questions_data.map { |q| q['position'] } + expect(positions).to eq([1, 2, 3, 4, 5, 6]) + end + end end end end From dbeb054d8dcabeafeef7aba4f05f03eef0b63b28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:19:22 +0000 Subject: [PATCH 3/3] Add safety check and improve test for question position normalization Co-authored-by: rileyseaburg <48305658+rileyseaburg@users.noreply.github.com> --- app/serializers/question_serializer.rb | 3 ++- spec/controllers/api/v1/forms_controller_spec.rb | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/serializers/question_serializer.rb b/app/serializers/question_serializer.rb index bac490fb8..00895035a 100644 --- a/app/serializers/question_serializer.rb +++ b/app/serializers/question_serializer.rb @@ -20,6 +20,7 @@ class QuestionSerializer < ActiveModel::Serializer def position # Normalize position based on the order within the form's ordered questions form_questions = object.form.ordered_questions - form_questions.index(object) + 1 + index = form_questions.index(object) + index ? index + 1 : 1 end end \ No newline at end of file diff --git a/spec/controllers/api/v1/forms_controller_spec.rb b/spec/controllers/api/v1/forms_controller_spec.rb index 6120f8610..98dc6a68c 100644 --- a/spec/controllers/api/v1/forms_controller_spec.rb +++ b/spec/controllers/api/v1/forms_controller_spec.rb @@ -264,12 +264,12 @@ before do # Create questions with gaps in positions (simulating deleted questions) - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 1, answer_field: 'answer_01') - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 3, answer_field: 'answer_02') - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 5, answer_field: 'answer_03') - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 6, answer_field: 'answer_04') - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 7, answer_field: 'answer_05') - FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 16, answer_field: 'answer_06') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 1, answer_field: 'answer_01', text: 'Question 1') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 3, answer_field: 'answer_02', text: 'Question 2') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 5, answer_field: 'answer_03', text: 'Question 3') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 6, answer_field: 'answer_04', text: 'Question 4') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 7, answer_field: 'answer_05', text: 'Question 5') + FactoryBot.create(:question, form: form, form_section: form.form_sections.first, position: 16, answer_field: 'answer_06', text: 'Question 6') user.update(api_key: TEST_API_KEY) request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(ENV.fetch('API_HTTP_USERNAME'), ENV.fetch('API_HTTP_PASSWORD'))