202 lines
6.4 KiB
Ruby
202 lines
6.4 KiB
Ruby
class HelpdeskSearchController < ApplicationController
|
|
unloadable
|
|
|
|
accept_api_auth :ticket_by_issue, :issues_by_contact, :messages_by_issue, :contact_timeline
|
|
|
|
before_filter :require_login
|
|
|
|
def usage
|
|
# Human/browser probes often stop at /helpdesk_search/issues. Return a
|
|
# machine-readable usage response instead of letting Rails raise a route
|
|
# exception that looks like an application failure in production.log.
|
|
render :json => {
|
|
:helpdesk_search => {
|
|
:ticket_by_issue => '/helpdesk_search/issues/:issue_id/ticket',
|
|
:ticket_by_issue_alias => '/helpdesk_search/issues/:issue_id',
|
|
:issues_by_contact => '/helpdesk_search/contacts/:contact_id/issues',
|
|
:messages_by_issue => '/helpdesk_search/issues/:issue_id/messages',
|
|
:contact_timeline => '/helpdesk_search/contacts/:contact_id/timeline'
|
|
}
|
|
}, :status => :bad_request
|
|
end
|
|
|
|
def ticket_by_issue
|
|
issue = Issue.find(params[:issue_id])
|
|
return unless authorize_helpdesk_project!(issue.project)
|
|
|
|
ticket = HelpdeskTicket.where(:issue_id => issue.id).first
|
|
unless ticket
|
|
render_404
|
|
return
|
|
end
|
|
|
|
render :json => {:helpdesk_ticket => serialize_ticket(ticket)}
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def issues_by_contact
|
|
contact = Contact.find(params[:contact_id])
|
|
tickets = HelpdeskTicket.
|
|
includes(:issue => [:project, :status, :tracker, :assigned_to]).
|
|
where(:contact_id => contact.id).
|
|
order("#{HelpdeskTicket.table_name}.ticket_date DESC").
|
|
limit(api_limit)
|
|
|
|
render :json => {
|
|
:contact_id => contact.id,
|
|
:issues => tickets.map { |ticket| serialize_ticket_issue(ticket) }.compact
|
|
}
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def messages_by_issue
|
|
issue = Issue.find(params[:issue_id])
|
|
return unless authorize_helpdesk_project!(issue.project)
|
|
|
|
messages = JournalMessage.
|
|
includes(:contact, :journal).
|
|
joins(:journal).
|
|
where(:journals => {:journalized_type => 'Issue', :journalized_id => issue.id}).
|
|
order("#{JournalMessage.table_name}.message_date ASC").
|
|
limit(api_limit)
|
|
|
|
render :json => {
|
|
:issue_id => issue.id,
|
|
:journal_messages => messages.map { |message| serialize_journal_message(message) }
|
|
}
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def contact_timeline
|
|
contact = Contact.find(params[:contact_id])
|
|
tickets = HelpdeskTicket.
|
|
includes(:issue => [:project, :status, :tracker]).
|
|
where(:contact_id => contact.id).
|
|
order("#{HelpdeskTicket.table_name}.ticket_date DESC").
|
|
limit(api_limit)
|
|
|
|
messages = JournalMessage.
|
|
includes(:contact, :journal).
|
|
where(:contact_id => contact.id).
|
|
order("#{JournalMessage.table_name}.message_date DESC").
|
|
limit(api_limit)
|
|
|
|
events = []
|
|
tickets.each do |ticket|
|
|
next unless visible_issue?(ticket.issue)
|
|
events << serialize_ticket(ticket).merge(:type => 'helpdesk_ticket', :date => iso8601(ticket.ticket_date))
|
|
end
|
|
|
|
messages.each do |message|
|
|
issue = message_issue(message)
|
|
next unless visible_issue?(issue)
|
|
events << serialize_journal_message(message).merge(:type => 'journal_message', :date => iso8601(message.message_date))
|
|
end
|
|
|
|
events.sort_by! { |event| event[:date].to_s }
|
|
events.reverse!
|
|
|
|
render :json => {
|
|
:contact_id => contact.id,
|
|
:timeline => events.first(api_limit)
|
|
}
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
private
|
|
|
|
def authorize_helpdesk_project!(project)
|
|
# Search endpoints expose customer/email context, so they reuse the native
|
|
# per-project helpdesk visibility permission instead of adding a new role.
|
|
return true if project && User.current.allowed_to?(:view_helpdesk_tickets, project)
|
|
deny_access
|
|
false
|
|
end
|
|
|
|
def visible_issue?(issue)
|
|
issue && issue.project && User.current.allowed_to?(:view_helpdesk_tickets, issue.project)
|
|
end
|
|
|
|
def api_limit
|
|
requested_limit = params[:limit].present? ? params[:limit].to_i : 100
|
|
[[requested_limit, 1].max, 200].min
|
|
end
|
|
|
|
def serialize_ticket_issue(ticket)
|
|
issue = ticket.issue
|
|
return nil unless visible_issue?(issue)
|
|
|
|
serialize_ticket(ticket).merge(
|
|
:issue => {
|
|
:id => issue.id,
|
|
:project_id => issue.project_id,
|
|
:tracker_id => issue.tracker_id,
|
|
:tracker_name => issue.tracker.try(:name),
|
|
:status_id => issue.status_id,
|
|
:status_name => issue.status.try(:name),
|
|
:assigned_to_id => issue.assigned_to_id,
|
|
:assigned_to_name => issue.assigned_to.try(:name),
|
|
:subject => issue.subject,
|
|
:created_on => iso8601(issue.created_on),
|
|
:updated_on => iso8601(issue.updated_on)
|
|
}
|
|
)
|
|
end
|
|
|
|
def serialize_ticket(ticket)
|
|
{
|
|
:id => ticket.id,
|
|
:issue_id => ticket.issue_id,
|
|
:contact_id => ticket.contact_id,
|
|
:message_id => ticket.message_id,
|
|
:source => ticket.source,
|
|
:is_incoming => ticket.is_incoming?,
|
|
:from_address => ticket.from_address,
|
|
:to_address => ticket.to_address,
|
|
:cc_address => ticket.cc_address,
|
|
:ticket_date => iso8601(ticket.ticket_date)
|
|
}
|
|
end
|
|
|
|
def serialize_journal_message(message)
|
|
journal = message.journal
|
|
issue = message_issue(message)
|
|
|
|
# Keep this API metadata-only. The external indexer can fetch journal.notes
|
|
# with its own read policy; this endpoint should not leak message bodies,
|
|
# private notes, attachments, or BCC addresses by accident.
|
|
{
|
|
:id => message.id,
|
|
:journal_id => message.journal_id,
|
|
:issue_id => issue.try(:id),
|
|
:project_id => issue.try(:project_id),
|
|
:contact_id => message.contact_id,
|
|
:message_id => message.message_id,
|
|
:source => message.source,
|
|
:is_incoming => message.is_incoming?,
|
|
:from_address => message.from_address,
|
|
:to_address => message.to_address,
|
|
:cc_address => message.cc_address,
|
|
:has_bcc_address => message.bcc_address.present?,
|
|
:message_date => iso8601(message.message_date),
|
|
:journal_user_id => journal.try(:user_id),
|
|
:journal_private_notes => journal.try(:private_notes?),
|
|
:journal_has_notes => journal.try(:notes).present?
|
|
}
|
|
end
|
|
|
|
def message_issue(message)
|
|
journal = message.journal
|
|
return nil unless journal
|
|
journal.try(:issue) || journal.try(:journalized)
|
|
end
|
|
|
|
def iso8601(value)
|
|
value.try(:utc).try(:iso8601)
|
|
end
|
|
end
|