Skip to content

Commit 1bcbd15

Browse files
authored
Merge pull request #175 from alphagov/2425-add-post-feedback-endpoint
Add Post answer feedback endpoint
2 parents b87946d + f1423de commit 1bcbd15

12 files changed

Lines changed: 164 additions & 26 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class ValidationErrorBlueprint < Blueprinter::Base
2+
field :errors
3+
field :message do
4+
"Unprocessable entity"
5+
end
6+
end

app/controllers/api/v0/conversations_controller.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class Api::V0::ConversationsController < ApplicationController
22
before_action { authorise_user!(SignonUser::Permissions::CONVERSATION_API) }
3-
before_action :find_conversation, only: %i[show answer]
3+
before_action :find_conversation, only: %i[show answer answer_feedback]
44

55
def show
66
answered_questions = @conversation.questions.joins(:answer)
@@ -23,11 +23,30 @@ def answer
2323
end
2424
end
2525

26+
def answer_feedback
27+
answer = @conversation.answers.includes(:feedback).find(params[:answer_id])
28+
feedback_form = Form::CreateAnswerFeedback.new(answer_feedback_params.merge(answer:))
29+
30+
if feedback_form.valid?
31+
feedback_form.submit
32+
33+
render json: {}, status: :created
34+
else
35+
render json: ValidationErrorBlueprint.render(
36+
errors: feedback_form.errors.messages,
37+
), status: :unprocessable_entity
38+
end
39+
end
40+
2641
private
2742

2843
def find_conversation
2944
@conversation = Conversation
3045
.includes(questions: { answer: %i[sources feedback] })
3146
.find(params[:conversation_id])
3247
end
48+
49+
def answer_feedback_params
50+
params.permit(:useful)
51+
end
3352
end

app/models/form/create_answer_feedback.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ def submit
1616
private
1717

1818
def no_feedback_present?
19-
errors.add(:answer_feedback, "Feedback already provided") if answer.feedback.present?
19+
errors.add(:base, "Feedback already provided for this answer") if answer.feedback.present?
2020
end
2121
end

config/initializers/committee.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
require "committee"
2+
require "api/request_validation_error"
3+
4+
Rails.application.config.middleware.use Committee::Middleware::RequestValidation,
5+
schema_path: "docs/api_openapi_specification.yml",
6+
coerce_date_times: true,
7+
prefix: "/api/v0",
8+
strict_reference_validation: true,
9+
error_class: Api::RequestValidationError
210

311
Rails.application.config.middleware.use Committee::Middleware::ResponseValidation,
412
schema_path: "docs/api_openapi_specification.yml",

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
namespace :v0 do
148148
get "/conversation/:conversation_id", to: "conversations#show", as: :show_conversation
149149
get "/conversation/:conversation_id/questions/:question_id/answer", to: "conversations#answer", as: :answer_question
150+
post "/conversation/:conversation_id/answers/:answer_id/feedback", to: "conversations#answer_feedback", as: :answer_feedback
150151
end
151152
end
152153

docs/api_openapi_specification.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,11 @@ components:
322322
type: object
323323
required:
324324
- message
325-
- fields
325+
- errors
326326
properties:
327327
message:
328328
type: string
329-
fields:
329+
errors:
330330
description: |
331331
an object structure of field name to an array of errors
332332
type: object
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Api
2+
class RequestValidationError < Committee::ValidationError
3+
def error_body
4+
GenericErrorBlueprint.render_as_hash(message:)
5+
end
6+
end
7+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
RSpec.describe ValidationErrorBlueprint do
2+
describe ".render_as_json" do
3+
it "renders the correct JSON based on the errors passed in" do
4+
errors = {
5+
user_question: ["Question must be 300 characters or less", "Personal data has been detected in your question. Please remove it and try asking again."],
6+
base: ["Previous question pending. Please wait for a response"],
7+
}
8+
9+
expect(described_class.render_as_json(errors:))
10+
.to eq({ message: "Unprocessable entity", errors: }.as_json)
11+
end
12+
end
13+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
RSpec.describe Api::RequestValidationError do
2+
describe "#render" do
3+
it "returns an array which uses the GenericErrorBlueprint for the error body" do
4+
request = double
5+
error_message = "Bad request"
6+
generic_error_blueprint = GenericErrorBlueprint.render(message: error_message)
7+
8+
error = described_class.new(400, :bad_request, error_message, request)
9+
expect(error.render).to eq([400, { "Content-Type" => "application/json" }, [generic_error_blueprint]])
10+
end
11+
end
12+
end

spec/models/form/create_answer_feedback_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
answer_with_feedback = build(:answer, :with_feedback)
1818
form = described_class.new(useful: true, answer: answer_with_feedback)
1919
expect(form).to be_invalid
20-
expect(form.errors[:answer_feedback]).to eq(["Feedback already provided"])
20+
expect(form.errors[:base]).to eq(["Feedback already provided for this answer"])
2121
end
2222
end
2323

0 commit comments

Comments
 (0)