Skip to content

Commit 278da77

Browse files
committed
Add POST /api/v0/conversation endpoint
This commit introduces a 'create' action in 'Api::V0::ConversationsController' that instantiates an anonymous conversation, delegates validation and persistence to 'Form::CreateQuestion' (thereby reusing our PII detection, length limits and pending‐question logic rather than reimplementing it), and returns either a 201 with the 'QuestionBlueprint' pending view or a 422 with ‘ValidationErrorBlueprint' JSON. We’ve scoped the existing ‘find_conversation' before_action to run on all actions except ‘create', avoiding a premature 404 on the initial POST, and added a private ‘question_params' method to explicitly require and permit the '{ question: { question: "..." } }' payload in line with our OpenAPI schema. Finally, we updated 'routes.rb' to expose the new POST '/api/v0/conversation' route and added comprehensive request specs covering authorization, success and error scenarios.
1 parent 1bcbd15 commit 278da77

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

app/controllers/api/v0/conversations_controller.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ class Api::V0::ConversationsController < ApplicationController
22
before_action { authorise_user!(SignonUser::Permissions::CONVERSATION_API) }
33
before_action :find_conversation, only: %i[show answer answer_feedback]
44

5+
def create
6+
conversation = Conversation.new
7+
8+
form = Form::CreateQuestion.new(conversation: conversation, user_question: question_params[:question])
9+
10+
if form.valid?
11+
question = form.submit
12+
render json: QuestionBlueprint.render(question, view: :pending), status: :created
13+
else
14+
render json: ValidationErrorBlueprint.render(message: "Could not create question", fields: form.errors.messages), status: :unprocessable_entity
15+
end
16+
end
17+
518
def show
619
answered_questions = @conversation.questions.joins(:answer)
720
pending_question = @conversation.questions.unanswered.last
@@ -49,4 +62,8 @@ def find_conversation
4962
def answer_feedback_params
5063
params.permit(:useful)
5164
end
65+
66+
def question_params
67+
params.require(:question).permit(:question)
68+
end
5269
end

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145

146146
namespace :api, format: false, defaults: { format: "json" } do
147147
namespace :v0 do
148+
post "/conversation", to: "conversations#create", as: :create_conversation
148149
get "/conversation/:conversation_id", to: "conversations#show", as: :show_conversation
149150
get "/conversation/:conversation_id/questions/:question_id/answer", to: "conversations#answer", as: :answer_question
150151
post "/conversation/:conversation_id/answers/:answer_id/feedback", to: "conversations#answer_feedback", as: :answer_feedback

spec/requests/api/v0/conversations_spec.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,53 @@
8989
end
9090
end
9191

92+
describe "POST :create" do
93+
it_behaves_like "responds with forbidden if user doesn't have API user permission",
94+
:api_v0_create_conversation_path,
95+
:post do
96+
let(:route_params) { [] }
97+
end
98+
99+
context "when the question is valid" do
100+
let(:payload) { { question: { question: "What is the capital of France?" } } }
101+
102+
it "creates a Conversation and a Question and returns 201" do
103+
expect {
104+
post api_v0_create_conversation_path, params: payload
105+
}.to change(Conversation, :count).by(1)
106+
.and change(Question, :count).by(1)
107+
108+
expect(response).to have_http_status(:created)
109+
end
110+
111+
it "returns the pending‐question JSON shaped by the pending view" do
112+
post api_v0_create_conversation_path, params: payload
113+
114+
q = Question.last
115+
expected = QuestionBlueprint.render_as_json(q, view: :pending)
116+
expect(JSON.parse(response.body)).to eq(expected)
117+
end
118+
end
119+
120+
context "when the question is invalid" do
121+
let(:payload) { { question: { question: "" } } }
122+
123+
it "returns 422 Unprocessable Entity" do
124+
post api_v0_create_conversation_path, params: payload
125+
expect(response).to have_http_status(:unprocessable_entity)
126+
end
127+
128+
it "returns the ValidationError JSON" do
129+
post api_v0_create_conversation_path, params: payload
130+
131+
expect(JSON.parse(response.body)).to eq({
132+
"message" => "Could not create question",
133+
"fields" => { "user_question" => [Form::CreateQuestion::USER_QUESTION_PRESENCE_ERROR_MESSAGE] }
134+
})
135+
end
136+
end
137+
end
138+
92139
describe "GET :answer" do
93140
context "when an answer has been generated for the question" do
94141
let!(:answer) { create(:answer, question:) }

0 commit comments

Comments
 (0)