lib/multiwoven/integrations/destination/zendesk/client.rb



# frozen_string_literal: true

module Multiwoven
  module Integrations
    module Destination
      module Zendesk
        include Multiwoven::Integrations::Core
        class Client < DestinationConnector
          prepend Multiwoven::Integrations::Core::RateLimiter
          def check_connection(connection_config)
            connection_config = connection_config.with_indifferent_access
            initialize_client(connection_config)
            authenticate_client
            success_status
          rescue StandardError => e
            handle_exception(e, {
                               context: "ZENDESK:CHECK_CONNECTION:EXCEPTION",
                               type: "error"
                             })
            failure_status(e)
          end

          def discover(_connection_config = nil)
            catalog = build_catalog(load_catalog)
            catalog.to_multiwoven_message
          rescue StandardError => e
            handle_exception(e, {
                               context: "ZENDESK:DISCOVER:EXCEPTION",
                               type: "error"
                             })
            failure_status(e)
          end

          def write(sync_config, records, action = "create")
            @sync_config = sync_config
            @action = sync_config.stream.action || action
            initialize_client(sync_config.destination.connection_specification)
            process_records(records, sync_config.stream)
          rescue StandardError => e
            handle_exception(e, {
                               context: "ZENDESK:WRITE:EXCEPTION",
                               type: "error",
                               sync_id: @sync_config.sync_id,
                               sync_run_id: @sync_config.sync_run_id
                             })
            failure_status(e)
          end

          private

          def initialize_client(connection_config)
            connection_config = connection_config.with_indifferent_access
            @client = ZendeskAPI::Client.new do |config|
              config.url = "#{connection_config[:subdomain]}.#{ZENDESK_URL_SUFFIX}"
              config.username = connection_config[:username]
              config.password = connection_config[:password]
            end
          end

          def authenticate_client
            @client.tickets.page(1).per_page(1).fetch
          rescue ZendeskAPI::Error => e
            raise StandardError, "Authentication failed: #{e.message}"
          end

          def process_records(records, stream)
            log_message_array = []
            write_success = 0
            write_failure = 0

            records.each do |record|
              zendesk_data = prepare_record_data(record, stream.name)
              plural_stream_name = pluralize_stream_name(stream.name.downcase)
              args = [plural_stream_name, @action, zendesk_data]

              if @action == "create"
                response = @client.send(plural_stream_name).create!(zendesk_data)
              else
                existing_record = @client.send(plural_stream_name).find(id: record[:id])
                response = existing_record.update!(zendesk_data)
              end

              write_success += 1
              log_message_array << log_request_response("info", args, response)
            rescue StandardError => e
              handle_exception(e, {
                                 context: "ZENDESK:WRITE:EXCEPTION",
                                 type: "error",
                                 sync_id: @sync_config.sync_id,
                                 sync_run_id: @sync_config.sync_run_id
                               })
              write_failure += 1
              log_message_array << log_request_response("error", args, e.message)
            end
            tracking_message(write_success, write_failure, log_message_array)
          end

          def pluralize_stream_name(name)
            { "ticket" => "tickets", "user" => "users" }.fetch(name, name)
          end

          def prepare_record_data(record, type)
            case type
            when "Tickets"
              {
                subject: record[:subject],
                comment: { body: record[:description] },
                priority: record[:priority],
                status: record[:status],
                requester_id: record[:requester_id],
                assignee_id: record[:assignee_id],
                tags: record[:tags]
              }
            when "Users"
              {
                name: record[:name],
                email: record[:email],
                role: record[:role]
              }
            else
              raise StandardError, "Unsupported record type: #{type}"
            end
          end

          def load_catalog
            read_json(CATALOG_SPEC_PATH)
          end
        end
      end
    end
  end
end