helpyio/helpy

View on GitHub
app/controllers/api/v1/topics.rb

Summary

Maintainability
B
6 hrs
Test Coverage
module API
  module V1
    class Topics < Grape::API

      before do
        authenticate!
        restrict_to_role %w(admin agent)
      end

      include API::V1::Defaults
      include Grape::Kaminari

      # throttle max: 200, per: 1.minute

      # PRIVATE TICKET ENDPOINTS
      resource :tickets, desc: "Create and Manage private discussions" do

        # LIST BY STATUS
        desc "List all PRIVATE tickets by status", {
          entity: Entity::Topic,
          notes: "List all open tickets (private topics)"
        }
        params do
          requires :status, type: String, desc: "Status group (New, Open, Pending, etc.)"
        end
        get "status/:status", root: :topics do
          if current_user.is_restricted?
            topics = Forum.find(1).topics.where(
              current_status: permitted_params[:status]
            ).all.tagged_with(current_user.team_list)
          else
            topics = Forum.find(1).topics.where(
              current_status: permitted_params[:status]
            )
          end
          present paginate(topics), with: Entity::Topic
        end

        # LIST BY USER
        desc "List all PRIVATE tickets for a user", {
          entity: Entity::Topic,
          notes: "List all tickets (private topics) for a given user"
        }
        params do
          requires :user_id, type: Integer, desc: "ID of the user"
        end
        get "user/:user_id", root: :topics do
          if current_user.is_restricted?
            topics = Forum.find(1).topics.where(user_id: permitted_params[:user_id]).all.tagged_with(current_user.team_list)
          else
            topics = Forum.find(1).topics.where(user_id: permitted_params[:user_id]).all
          end
          present paginate(topics), with: Entity::Topic
        end

        # SHOW ONE TICKET AND ITS THREAD
        desc "Show a single ticket", {
          entity: Entity::Topic,
          notes: "Show one ticket (private topic)"
        }
        params do
          requires :id, type: Integer, desc: "Ticket ID"
        end
        get ":id", root: :topics do
          if current_user.is_restricted?
            topic = Topic.includes(:posts).where(id: permitted_params[:id]).tagged_with(current_user.team_list)
          else
            topic = Topic.includes(:posts).find(permitted_params[:id])
          end
          if topic.present?
            present topic, with: Entity::Topic, posts: true, user: true
          else
            error!('Unauthorized. Insufficient access priviledges.', 401)
          end
        end

        # CREATE A NEW PRIVATE TICKET
        desc "Creates a new ticket." do
          detail "You must provide a reference to the ticket creator
          in one of two ways. Either with the `user_id` of an already instantiated user
          object, or by supplying both an email address `user_email` along with a user name `user_name`."
        end
        params do
          requires :name, type: String, desc: "The subject of the ticket"
          requires :body, type: String, desc: "The post body"
          optional :team_list, type: String, desc: "The group that this ticket is assigned to"
          optional :channel, type: String, desc: "The source channel the ticket was created from, Defaults to API if no value provided."
          optional :kind, type: String, desc: "he kind of topic this is, can be 'ticket','discussion','chat', etc."
          optional :user_id, type: Integer, desc: "the User ID. Required if `user_email` and `user_name` are not supplied."
          optional :user_email, type: String, desc: "The user who is creating a ticket. Can be either registered or non-registered. Required if `user_id` not supplied."
          optional :user_name, type: String, desc: "The user name for register a non-registered user. Required if `user_id` is not supplied."
          optional :tag_list, type: String, desc: "A list of tags to apply to this ticket"
          optional :cc, type: String, desc: "Comma separated list of emails to CC"
          optional :bcc, type: String, desc: "Comma separated list of emails to BCC"
        end

        post "", root: :topics do
          error!('Required field not present. user_id or user_email and user_name is missing', 403) if params[:user_id].blank? && params[:user_email].blank?

          user_id = params[:user_id] # initialize user_id with nil or params[:user_id]
          if params[:user_email].present?
            user = User.find_by("lower(email) = ?", params[:user_email].downcase)
            if user.nil?
              error!('User not registered. Insufficient access priviledges.', 401) if params[:user_name].blank?
              user = User.register params[:user_email], params[:user_name]
              error!("Ticket not created. User could not be registered: #{user.errors.full_messages.join('; ')}", 403) unless user.persisted?
            end
            user_id = user.id
          end

          ticket = Topic.create!(
            forum_id: 1,
            name: params[:name],
            user_id: user_id,
            current_status: 'new',
            private: true,
            team_list: params[:team_list],
            tag_list: params[:tag_list],
            channel: params[:channel].present? ? params[:channel] : "api",
            kind: params[:kind].present? ? params[:kind] : 'ticket',
          )
          ticket.posts.create!(
            body: params[:body],
            user_id: user_id,
            kind: 'first',
            cc: params[:cc],
            bcc: params[:bcc]
          )
          present ticket, with: Entity::Topic, posts: true, user: true
        end

        # ASSIGN TICKET
        desc "Assign ticket to an agent"
        params do
          requires :id, type: Integer, desc: "The ticket ID to update"
          requires :assigned_user_id, type: Integer, desc: "The assigned agent for this ticket"
        end

        post "assign/:id", root: :topics do
          if current_user.is_restricted?
            ticket = Topic.where(id: permitted_params[:id]).all.tagged_with(current_user.team_list).first
          else
            ticket = Topic.where(id: permitted_params[:id]).first
          end
          if ticket.present?
            previous_assigned_id = ticket.assigned_user_id? ? ticket.assigned_user_id : params[:assigned_user_id]
            assigned_user = User.find(params[:assigned_user_id])
            ticket.assign(previous_assigned_id, assigned_user.id)
            present ticket, with: Entity::Topic, posts: true, user: true
          else
            error!('Unauthorized. Insufficient access priviledges.', 401)
          end
        end

        # CHANGE TICKET STATUS
        desc "Change the status of a ticket"
        params do
          requires :id, type: Integer, desc: "The ticket ID to update"
          requires :status, type: String, desc: "The status of the topic (New, Open, Pending, Resolved)"
        end

        post "update_status/:id", root: :topics do
          if current_user.is_restricted?
            ticket = Topic.where(id: permitted_params[:id]).all.tagged_with(current_user.team_list).first
          else
            ticket = Topic.where(id: permitted_params[:id]).first
          end
          if ticket.present?
            case params[:status]
            when 'closed'
              ticket.close(current_user.id)
            when 'reopen'
              ticket.reopen(current_user.id)
            when 'trash'
              ticket.trash(current_user.id)
            else
              ticket.current_status = params[:status]
              ticket.save
            end
            present ticket, with: Entity::Topic, posts: true, user: true
          else
            error!('Unauthorized. Insufficient access priviledges.', 401)
          end
        end

        # UPDATE A TICKETS TAGS
        desc "Update tags for this ticket"
        params do
          requires :id, type: Integer, desc: "The ticket ID to update"
          requires :tag_list, type: String, desc: "A list of tags to apply to this ticket"
        end

        post "update_tags/:id", root: :topics do
          if current_user.is_restricted?
            ticket = Topic.where(id: permitted_params[:id]).all.tagged_with(current_user.team_list).first
          else
            ticket = Topic.where(id: permitted_params[:id]).first
          end
          if ticket.present?
            ticket.tag_list = params[:tag_list]
            ticket.save
            present ticket, with: Entity::Topic, posts: true, user: true
          else
            error!('Unauthorized. Insufficient access priviledges.', 401)
          end
        end

        # MOVE FORUMS, BETWEEN PRIVATE/PUBLIC
        desc "Move forums, set privacy"
        params do
          requires :id, type: Integer, desc: "The ticket ID to update"
          requires :forum_id, type: Integer, desc: "The forum this ticket is associated with"
        end

        post "update_forum/:id", root: :topics do
          if current_user.is_restricted?
            ticket = Topic.where(id: permitted_params[:id]).all.tagged_with(current_user.team_list).first
          else
            ticket = Topic.where(id: permitted_params[:id]).first
          end
          if ticket.present?
            is_private = (permitted_params[:forum_id] == 1) ? true : false
            ticket.private = is_private
            ticket.forum_id = params[:forum_id]
            ticket.save
            present ticket, with: Entity::Topic, posts: true, user: true
          else
            error!('Unauthorized. Insufficient access priviledges.', 401)
          end
        end

        # SPLIT A TOPIC
        desc "Create a new discussion from a ticket"
        params do
          requires :post_id, type: Integer, desc: "The post to split into new topic"
        end

        post "split/:post_id", root: :topics do
          post = Post.where(id: permitted_params[:post_id]).first

          # Check that the post_id exists, and that it is private.
          error!('Not Found.', 404) unless post.present?

          parent_topic = post.topic

          topic = Topic.new(
            name: I18n.t('new_discussion_topic_title', original_name: parent_topic.name, original_id: parent_topic.id, default: "Split from #{parent_topic.id}-#{parent_topic.name}"),
            user: post.user,
            forum_id: parent_topic.forum_id,
            private: parent_topic.private,
            channel: parent_topic.channel,
            kind: parent_topic.kind
          )

          if topic.save
            parent_topic.posts.create(
              body: I18n.t('new_discussion_post', topic_id: topic.id, default: "Discussion ##{topic.id} was created from this one"),
              user: current_user,
              kind: 'note',
            )

            topic.posts.create(
              body: post.body,
              user: post.user,
              kind: 'first',
              screenshots: post.screenshots,
              attachments: post.attachments,
            )
            topic
          else
            error!('Unknown Error!', 500)
          end
        end

        # MERGE TWO OR MORE TICKETS
        desc "Merge two or more tickets together."
        params do
          requires :'topic_ids', type: Array[Integer], desc: "The topics to merge. Provide 2 ID in the format topic_ids[]=123&topic_ids[]=124"
          requires :user_id, type: Integer, desc: "the User ID"
        end

        post "merge", root: :topics do
          @ticket = Topic.merge_topics(params[:topic_ids], params[:user_id])
          if @ticket.present?
            present @ticket, with: Entity::Topic, posts: true, user: true
          end
        end
      end

      # PUBLIC TOPIC ENDPOINTS
      resource :topics, desc: "Create and manage public discussions" do

        # SHOW ONE TOPIC AND ITS THREAD
        desc "Show a single ticket", {
          entity: Entity::Topic,
          notes: "Show one community topic"
        }
        params do
          requires :id, type: Integer, desc: "Topic ID"
        end
        get ":id", root: :topics do
          topic = Topic.includes(:posts).find(permitted_params[:id])#
          present topic, with: Entity::Topic, posts: true, user: true
        end


        # CREATE A NEW PUBLIC TOPIC
        desc "Create a new public topic"
        params do
          requires :name, type: String, desc: "The subject of the ticket"
          requires :body, type: String, desc: "The post body"
          requires :user_id, type: Integer, desc: "the User ID"
          requires :forum_id, type: Integer, desc: "The forum to add the topic to"
          optional :channel, type: String, desc: "The source channel the ticket was created from. Defaults to API."
          optional :kind, type: String, desc: "The kind of topic this is, can be 'ticket','discussion','chat', etc."
          optional :priority, type: String, desc: "Priority of the topic, can be 'low', 'normal', 'high' or 'very_high'", values: [ 'low', 'normal', 'high', 'very_high' ]
        end

        post "", root: :topics do
          topic = Topic.create!(
            forum_id: permitted_params[:forum_id],
            name: permitted_params[:name],
            user_id: permitted_params[:user_id],
            private: false,
            channel: params[:channel].present? ? params[:channel] : "api",
            kind: params[:kind].present? ? params[:kind] : 'discussion',
            priority: permitted_params[:priority] || 'normal'
          )
          topic.posts.create!(
            body: permitted_params[:body],
            user_id: permitted_params[:user_id],
            kind: 'first'
          )
          present topic, with: Entity::Topic, posts: true, user: true
        end

        # UPDATE SINGLE TOPIC (PRIVACY, STATUS, ASSIGNED, ETC)
        desc "Update the status, assigned user, etc of a community topic"
        params do
          requires :id, type: Integer, desc: "The topic ID to update"
          requires :forum_id, type: Integer, desc: "The forum this topic is associated with"
          optional :current_status, type: String, desc: "The status of the topic (New, Open, Pending, Resolved)"
          optional :private, type: Boolean, desc: "Whether or not the topic is marked private"
          optional :assigned_user_id, type: Integer, desc: "The assigned agent for this topic"
          optional :priority, type: String, desc: "Priority of the topic, can be 'low', 'normal', 'high' or 'very_high'", values: [ 'low', 'normal', 'high', 'very_high' ]
        end

        patch ":id", root: :topics do
          topic = Topic.where(id: permitted_params[:id]).first
          topic.update!(
            forum_id: permitted_params[:forum_id],
            current_status: permitted_params[:current_status],
            private: permitted_params[:private],
            assigned_user_id: permitted_params[:assigned_user_id],
            priority: permitted_params[:priority] || 'normal'
          )
          present topic, with: Entity::Topic, posts: true, user: true
        end

        # VOTE FOR A TOPIC
        desc "Vote for a topic", {
          entity: Entity::Topic,
          notes: "Vote for a given topic"
        }
        params do
          requires :id, type: Integer, desc: "The ID of the topic to vote for"
          #requires :user_id, type: Integer
        end
        patch ':id/vote', root: :topics do
          topic = Topic.where(id: permitted_params[:id]).first
          topic.votes.create!(
            user_id: current_user #|| permitted_params[:user_id]
          )
          present topic, with: Entity::Topic
        end

      end

    end
  end
end