Skip to content

Commit

Permalink
Allow any app to create support tickets
Browse files Browse the repository at this point in the history
We want to make it easy for any app to create a support ticket
and support-api feels like the best place for this.

The endpoint `/support-tickets` will be made available to apps via
`gds-api-adapters`, which allows them to pass Zendesk ticket
parameters to support-api.

The `gds_zendesk` gem provides a client to communicate with Zendesk
and it has a `ticket.create!` method, which is called if the parameters
provided are valid.

The only required standard ticket fields are `subject` and `description`
- for more information see https://support.zendesk.com/hc/en-us/articles/4408886739098-About-ticket-fields

This provides the basis of creating Zendesk tickets and will need
adjustments once we know what other information is required for
different types of support tickets.

We should keep the naming convention generic, as there is a high
possibility that we move away from Zendesk in the future.
  • Loading branch information
deborahchua committed May 24, 2024
1 parent 07a357f commit 36b6b7f
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
19 changes: 19 additions & 0 deletions app/controllers/support_tickets_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class SupportTicketsController < ApplicationController
def create
support_ticket = SupportTicket.new(attributes)

if support_ticket.valid?
GDS_ZENDESK_CLIENT.ticket.create!(support_ticket.attributes)

render json: { status: "success" }, status: :created
else
render json: { status: "error", errors: support_ticket.errors }, status: :unprocessable_entity
end
end

private

def attributes
params.slice(:subject, :tags, :user_agent, :description)
end
end
34 changes: 34 additions & 0 deletions app/models/support_ticket.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class SupportTicket
include ActiveModel::Validations

validates :subject, :description, presence: true

def initialize(attributes)
@subject = attributes.fetch(:subject, nil)
@tags = attributes.fetch(:tags, nil)
@user_agent = attributes.fetch(:user_agent, nil)
@description = attributes.fetch(:description, nil)
end

def attributes
{
"subject" => subject,
"tags" => tags,
"body" => ticket_body,
}
end

private

attr_reader :subject, :tags, :user_agent, :description

def ticket_body
<<-TICKET_BODY.strip_heredoc
[User agent]
#{user_agent}
[Details]
#{description}
TICKET_BODY
end
end
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
format: false,
param: :slug

resources "support-tickets",
only: :create,
controller: "support_tickets",
as: "support_ticket"

get "/feedback-by-day/:date", to: "feedback_by_day#index", format: false

get "/healthcheck/live", to: proc { [200, {}, %w[OK]] }
Expand Down
60 changes: 60 additions & 0 deletions spec/models/support_ticket_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require "rails_helper"

describe SupportTicket, "validations" do
it "validates presence of subject" do
support_ticket = described_class.new({})
support_ticket.valid?

expect(support_ticket.errors.messages.to_h).to include(subject: include("can't be blank"))
end

it "validates presence of description" do
support_ticket = described_class.new({})
support_ticket.valid?

expect(support_ticket.errors.messages.to_h).to include(description: include("can't be blank"))
end
end

describe SupportTicket, "#attributes" do
it "generates a hash of attributes to create a Zendesk ticket" do
support_ticket = described_class.new(
subject: "Feedback for app",
tags: %w[app_name],
user_agent: "Safari",
description: "Ticket details go here.",
)

expect(support_ticket.attributes).to eq(
"subject" => "Feedback for app",
"tags" => %w[app_name],
"body" => <<-TICKET_BODY.strip_heredoc,
[User agent]
Safari
[Details]
Ticket details go here.
TICKET_BODY
)
end

it "generates a hash of attributes where the body omits the optional user agent" do
support_ticket = described_class.new(
subject: "Feedback for app",
tags: %w[app_name],
description: "Ticket details go here.",
)

expect(support_ticket.attributes).to eq(
"subject" => "Feedback for app",
"tags" => %w[app_name],
"body" => <<-TICKET_BODY.strip_heredoc,
[User agent]
[Details]
Ticket details go here.
TICKET_BODY
)
end
end
68 changes: 68 additions & 0 deletions spec/requests/support_tickets_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require "rails_helper"

describe "Support Tickets" do
it "responds succesfully" do
stub_zendesk_ticket_creation

post "/support-tickets",
params: {
subject: "Feedback for app",
tags: %w[app_name],
user_agent: "Safari",
description: "Ticket details go here.",
}

expect(response.code).to eq("201")
expect(response_hash).to include("status" => "success")
end

it "sends the feedback to Zendesk" do
zendesk_request = expect_zendesk_to_receive_ticket(
"subject" => "Feedback for app",
"tags" => %w[app_name],
"body" => <<-TICKET_BODY.strip_heredoc,
[User agent]
Safari
[Details]
Ticket details go here.
TICKET_BODY
)

post "/support-tickets",
params: {
subject: "Feedback for app",
tags: %w[app_name],
user_agent: "Safari",
description: "Ticket details go here.",
}

expect(zendesk_request).to have_been_made
end

it "responds unsuccessfully if the feedback isn't valid" do
post "/support-tickets",
params: { subject: "Feedback for app" }

expect(response.code).to eq("422")
expect(response_hash).to include("status" => "error")
end

it "returns error if the subject field is empty" do
post "/support-tickets",
params: { description: "Ticket details go here." }

expect(response_hash).to include("errors" => include("subject" => include("can't be blank")))
end

it "returns error if the description field is empty" do
post "/support-tickets",
params: { subject: "Feedback for app" }

expect(response_hash).to include("errors" => include("description" => include("can't be blank")))
end

def response_hash
JSON.parse(response.body)
end
end

0 comments on commit 36b6b7f

Please sign in to comment.