ticket_abstractor_client 1.29.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +199 -159
  3. data/lib/ticket_abstractor_client.rb +11 -39
  4. data/lib/ticket_abstractor_client/base/attachment.rb +52 -0
  5. data/lib/ticket_abstractor_client/base/client.rb +54 -0
  6. data/lib/ticket_abstractor_client/base/comment.rb +35 -0
  7. data/lib/ticket_abstractor_client/base/errors.rb +25 -0
  8. data/lib/ticket_abstractor_client/base/fields_filter.rb +15 -0
  9. data/lib/ticket_abstractor_client/base/response_handler.rb +28 -0
  10. data/lib/ticket_abstractor_client/base/ticket.rb +79 -0
  11. data/lib/ticket_abstractor_client/client_helper.rb +29 -0
  12. data/lib/ticket_abstractor_client/configuration.rb +61 -0
  13. data/lib/ticket_abstractor_client/jira/attachment.rb +42 -0
  14. data/lib/ticket_abstractor_client/jira/client.rb +83 -0
  15. data/lib/ticket_abstractor_client/jira/comment.rb +35 -0
  16. data/lib/ticket_abstractor_client/jira/errors.rb +25 -0
  17. data/lib/ticket_abstractor_client/jira/fields_meta.rb +36 -0
  18. data/lib/ticket_abstractor_client/jira/params_builder.rb +96 -0
  19. data/lib/ticket_abstractor_client/jira/ticket.rb +131 -0
  20. data/lib/ticket_abstractor_client/service_now/attachment.rb +41 -0
  21. data/lib/ticket_abstractor_client/service_now/client.rb +78 -0
  22. data/lib/ticket_abstractor_client/service_now/comment.rb +30 -0
  23. data/lib/ticket_abstractor_client/service_now/errors.rb +25 -0
  24. data/lib/ticket_abstractor_client/service_now/params_builder.rb +56 -0
  25. data/lib/ticket_abstractor_client/service_now/ticket.rb +120 -0
  26. data/lib/ticket_abstractor_client/version.rb +1 -1
  27. metadata +52 -9
  28. data/lib/ticket_abstractor_client/brouha_client.rb +0 -33
  29. data/lib/ticket_abstractor_client/client.rb +0 -31
  30. data/lib/ticket_abstractor_client/i_logger_client.rb +0 -5
  31. data/lib/ticket_abstractor_client/itrc_client.rb +0 -33
  32. data/lib/ticket_abstractor_client/jira_client.rb +0 -128
  33. data/lib/ticket_abstractor_client/service_now_client.rb +0 -61
  34. data/lib/ticket_abstractor_client/sev_one_client.rb +0 -21
@@ -1,39 +1,13 @@
1
1
  module TicketAbstractorClient
2
- def self.initialize_client(url, security_token = nil)
3
- Base.new url, security_token
4
- end
5
-
6
- class Base
7
- def initialize(url, security_token = nil)
8
- @url = url
9
- @security_token = security_token
10
- @clients = {}
11
- end
12
-
13
- def jira(endpoint = 'tp')
14
- client_key = "jira_#{endpoint}".to_sym
15
- @clients[client_key] ||= JiraClient.new(@url, @security_token, CGI.escape(endpoint.to_s))
16
- end
17
-
18
- def brouha
19
- @clients[:brouha] ||= BrouhaClient.new(@url, @security_token)
20
- end
21
-
22
- def itrc
23
- @clients[:itrc] ||= ItrcClient.new(@url, @security_token)
24
- end
2
+ class << self
3
+ attr_writer :configuration
25
4
 
26
- def ilogger
27
- @clients[:ilogger] ||= ILoggerClient.new(@url, @security_token)
5
+ def configuration
6
+ @configuration ||= Configuration.new
28
7
  end
29
8
 
30
- def sev_one
31
- @clients[:sev_one] ||= SevOneClient.new(@url, @security_token)
32
- end
33
-
34
- def service_now(endpoint = 'default')
35
- client_key = "service_now_#{endpoint}".to_sym
36
- @clients[client_key] ||= ServiceNowClient.new(@url, @security_token, CGI.escape(endpoint))
9
+ def configure
10
+ yield(configuration)
37
11
  end
38
12
  end
39
13
  end
@@ -41,11 +15,9 @@ end
41
15
  require 'cgi'
42
16
  require 'json'
43
17
  require 'rest_client'
18
+ require 'active_support/all'
19
+ require 'mime-types'
20
+
44
21
  require 'ticket_abstractor_client/version'
45
- require 'ticket_abstractor_client/client'
46
- require 'ticket_abstractor_client/jira_client'
47
- require 'ticket_abstractor_client/brouha_client'
48
- require 'ticket_abstractor_client/itrc_client'
49
- require 'ticket_abstractor_client/i_logger_client'
50
- require 'ticket_abstractor_client/sev_one_client'
51
- require 'ticket_abstractor_client/service_now_client'
22
+ require 'ticket_abstractor_client/configuration'
23
+ require 'ticket_abstractor_client/client_helper'
@@ -0,0 +1,52 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ class Attachment
4
+ attr_reader :file_path, :data_hash, :external_created_at
5
+
6
+ class << self
7
+ def fetch
8
+ method_not_implemented __method__
9
+ end
10
+
11
+ protected
12
+
13
+ def method_not_implemented(method_name)
14
+ raise Errors::NotImplementedError, "#{self}##{method_name} is not implemented"
15
+ end
16
+
17
+ def save_content(content, original_filename)
18
+ tmp_file = Tempfile.new(Time.now.to_s)
19
+ tmp_file.binmode
20
+ tmp_file.write(content)
21
+ tmp_file.close
22
+ set_original_name(tmp_file.path, original_filename)
23
+ end
24
+
25
+ def set_original_name(filepath, original_name)
26
+ tmp_basename = File.basename(filepath)
27
+ tmp_path = filepath.gsub(tmp_basename, '')
28
+ FileUtils.mv filepath, File.join(tmp_path, original_name)
29
+ File.join(tmp_path, original_name)
30
+ end
31
+ end
32
+
33
+ def initialize(opts)
34
+ @file_path = opts[:file_path]
35
+ @external_created_at = opts[:external_created_at].to_time.utc rescue Time.now.utc
36
+ @data_hash = opts[:data_hash]
37
+ set_data_hash! if @data_hash.blank?
38
+ end
39
+
40
+ def sync!
41
+ self.class.method_not_implemented __method__
42
+ end
43
+
44
+ def set_data_hash!
45
+ return if self.file_path.blank?
46
+ file = File.new(self.file_path, 'rb')
47
+ @data_hash = Digest::SHA1.hexdigest(file.read)
48
+ file.close
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ class Client
4
+ include ResponseHandler
5
+
6
+ def initialize
7
+ @base_url = TicketAbstractorClient.configuration.ticket_abstractor_url
8
+ @security_token = TicketAbstractorClient.configuration.security_token
9
+ @ssl_options = TicketAbstractorClient.configuration.ssl_options
10
+
11
+ raise Errors::ConfigurationError, 'TicketAbstractor url is not given' if @base_url.blank?
12
+ raise Errors::ConfigurationError, 'SecurityToken is not given' if @security_token.blank?
13
+ end
14
+
15
+ protected
16
+
17
+ def get(path, args = {}, params = {})
18
+ params.merge! args: args.to_json, security_token: @security_token
19
+ with_response_handling do
20
+ rest_client_error_handling do
21
+ response = build_request(path, 'get', params).execute
22
+ response.headers.key?(:file_request) ? { 'result' => response } : JSON.parse(response)
23
+ end
24
+ end
25
+ end
26
+
27
+ def post(path, args = {}, params = {})
28
+ params.merge! args: args.to_json, security_token: @security_token
29
+ with_response_handling do
30
+ rest_client_error_handling do
31
+ response = build_request(path, 'post', params).execute
32
+ JSON.parse response
33
+ end
34
+ end
35
+ end
36
+
37
+ def build_request(path, method, params)
38
+ request_params = @ssl_options.merge(url: "#{@base_url}/v2/#{path}", method: method)
39
+ request_params[:headers] = { params: params } if method.inquiry.get?
40
+ request_params[:payload] = params if method.inquiry.post?
41
+
42
+ RestClient::Request.new(request_params)
43
+ end
44
+
45
+ def rest_client_error_handling
46
+ yield
47
+ rescue Errno::ECONNREFUSED => exception
48
+ raise Errors::UnexpectedError, exception.message
49
+ rescue RestClient::Exception => exception
50
+ raise Errors::UnexpectedError, exception.response.body
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ class Comment
4
+ attr_reader :author, :body, :data_hash, :external_created_at
5
+
6
+ def self.fetch(*opts)
7
+ method_not_implemented __method__
8
+ end
9
+
10
+ def initialize(opts)
11
+ opts = opts.with_indifferent_access
12
+ @author = opts[:author]
13
+ @body = opts.fetch(:body, nil) || raise(Errors::CommentArgumentError, 'Body is not given')
14
+ @external_created_at = opts[:external_created_at].to_time.utc rescue Time.now.utc
15
+ @data_hash = opts[:data_hash]
16
+ set_data_hash! if @data_hash.blank?
17
+ end
18
+
19
+ def set_data_hash!
20
+ content = "#{@external_created_at}:#{@author}:#{@body}"
21
+ @data_hash = Digest::SHA1.hexdigest(content)
22
+ end
23
+
24
+ def sync!
25
+ self.class.method_not_implemented __method__
26
+ end
27
+
28
+ protected
29
+
30
+ def self.method_not_implemented(method_name)
31
+ raise Errors::NotImplementedError, "#{self}##{method_name} is not implemented"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ module Errors
4
+ class BaseError < StandardError; end
5
+ class NotImplementedError < BaseError; end
6
+ class ConfigurationError < BaseError; end
7
+ class UnexpectedError < BaseError; end
8
+
9
+ class AttachmentArgumentError < BaseError; end
10
+ class CommentArgumentError < BaseError; end
11
+ class ClientArgumentError < BaseError; end
12
+ class TicketArgumentError < BaseError; end
13
+
14
+ class AttachmentError < BaseError; end
15
+ class CommentError < BaseError; end
16
+ class TicketError < BaseError; end
17
+
18
+ class ConnectionError < BaseError; end
19
+ class NotFoundError < BaseError; end
20
+ class ParseError < BaseError; end
21
+ class RequsetFormatError < BaseError; end
22
+ class AccessError < BaseError; end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ class FieldsFilter
4
+ attr_accessor :raw_fields
5
+
6
+ def initialize(raw_fields)
7
+ @raw_fields = raw_fields
8
+ end
9
+
10
+ def filter_fields
11
+ raise Errors::NotImplementedError, "#{self}##{__method__} is not implemented"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ module ResponseHandler
4
+ extend ActiveSupport::Concern
5
+
6
+ def with_response_handling
7
+ response = yield
8
+
9
+ raise(error_by_context(response['context']), response['error'].to_s) if response.key?('error')
10
+
11
+ response['result']
12
+ end
13
+
14
+ private
15
+
16
+ def error_by_context(context)
17
+ modulo, klass = context.split('::')
18
+ error_class = "TicketAbstractorClient::#{modulo}::Errors::#{klass}".safe_constantize
19
+ error_class.presence || unexpected_error_klass
20
+ end
21
+
22
+ def unexpected_error_klass
23
+ modulo = self.class.name.deconstantize.split('::').last
24
+ "TicketAbstractorClient::#{modulo}::Errors::UnexpectedError".safe_constantize
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,79 @@
1
+ module TicketAbstractorClient
2
+ module Base
3
+ class Ticket
4
+
5
+ attr_reader :attachments, :comments, :changes, :status, :fields
6
+ attr_accessor :ticket_id, :endpoint, :project
7
+
8
+ def initialize(opts = {})
9
+ opts = opts.with_indifferent_access
10
+ @ticket_id = opts[:ticket_id]
11
+ @fields = opts.fetch(:fields, {}).with_indifferent_access
12
+ @endpoint = opts[:endpoint] || raise(Errors::TicketArgumentError, 'Endpoint is not given')
13
+ @project = opts[:project]
14
+ initialize_changes!
15
+ mark_changes!
16
+ end
17
+
18
+ def add_attachment(attachment)
19
+ self.class.not_implemented __method__
20
+ end
21
+
22
+ def add_comment(comment)
23
+ self.class.not_implemented __method__
24
+ end
25
+
26
+ def any_changes?
27
+ @changes.values.any? { |value| value.is_a?(Fixnum) ? !value.zero? : value.present? }
28
+ end
29
+
30
+ def reset_changes!
31
+ initialize_changes!
32
+ end
33
+
34
+ def status=(status)
35
+ return if @status == status
36
+ @changes[:new_status] = true
37
+ @status = status
38
+ end
39
+
40
+ def sync!
41
+ self.class.not_implemented __method__
42
+ end
43
+
44
+ def to_hash
45
+ {
46
+ ticket_id: @ticket_id,
47
+ status: @status,
48
+ external_updated_at: @external_updated_at,
49
+ fields: @fields,
50
+ comments: @comments.map(&:to_hash),
51
+ attachments: @attachments.map(&:to_hash)
52
+ }
53
+ end
54
+
55
+ def to_json
56
+ to_hash.to_json
57
+ end
58
+
59
+ def updated_at
60
+ self.class.not_implemented __method__
61
+ end
62
+
63
+ protected
64
+
65
+ def self.not_implemented(method_name)
66
+ raise Errors::NotImplementedError, "#{self}##{__method__} is not implemented"
67
+ end
68
+
69
+ def initialize_changes!
70
+ @changes = { create: false, update: false, new_status: false, new_attachments: 0, new_comments: 0 }
71
+ end
72
+
73
+ def mark_changes!
74
+ return @changes[:create] = true if @ticket_id.blank?
75
+ @changes[:update] = true if @fields.present?
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,29 @@
1
+ module TicketAbstractorClient
2
+ module ClientHelper
3
+ paths_to_load = %W(
4
+ base/*.rb
5
+ jira/params_builder.rb
6
+ jira/*.rb
7
+ service_now/params_builder.rb
8
+ service_now/*.rb
9
+ )
10
+ paths_to_load.each do |path|
11
+ location = File.expand_path(path, __dir__)
12
+ Dir[location].each { |f| require f }
13
+ end
14
+
15
+ Time::DATE_FORMATS[:jira_iso8601] = '%FT%T.%L%z'
16
+ Time::DATE_FORMATS[:service_now] = '%m-%d-%y %l:%M %p'
17
+ Time::DATE_FORMATS[:service_now_display_value] = '%m-%d-%y %-l:%M %p'
18
+ Time::DATE_FORMATS[:service_now_iso8601] = '%F %T'
19
+ Time::DATE_FORMATS[:unix_timestamp] = '%s'
20
+ Time::DATE_FORMATS[:unix_timestamp_with_ms] = '%s.%5N'
21
+
22
+ def get_jira_fields_meta
23
+ Jira::Client.new.endpoints.each_with_object({}) do |(endpoint, _), result|
24
+ result[endpoint.to_sym] = Jira::FieldsMeta.new(endpoint)
25
+ end
26
+ end
27
+ module_function :get_jira_fields_meta
28
+ end
29
+ end
@@ -0,0 +1,61 @@
1
+ module TicketAbstractorClient
2
+ class Configuration
3
+ attr_accessor :ticket_abstractor_url, :security_token, :snow_display_value, :jira_fields_meta,
4
+ :jira_meta_expiration_period, :ssl_options
5
+
6
+ DEFAULT_JIRA_META_EXPIRATION_PERIOD = 604800 # 1 week
7
+
8
+ SNOW_DISPLAY_VALUES_LIST = [
9
+ SNOW_DISPLAY_VALUE_ALL = :all,
10
+ SNOW_DISPLAY_VALUE_TRUE = true,
11
+ SNOW_DISPLAY_VALUE_FALSE = false
12
+ ].freeze
13
+
14
+ DEFAULT_SNOW_DISPLAY_VALUE = SNOW_DISPLAY_VALUE_FALSE
15
+
16
+ DEFAULT_SSL_OPTIONS = { verify_ssl: true }.freeze
17
+
18
+ def jira_fields_filter_class
19
+ @jira_fields_filter_class.presence
20
+ end
21
+
22
+ def jira_fields_filter_class=(filter_klass)
23
+ raise_configuration_error(filter_klass) unless filter_klass < Base::FieldsFilter
24
+
25
+ @jira_fields_filter_class = filter_klass
26
+ end
27
+
28
+ def jira_fields_meta
29
+ @jira_fields_meta ||= ClientHelper.get_jira_fields_meta
30
+ end
31
+
32
+ def jira_meta_expiration_period
33
+ @jira_meta_expiration_period ||= DEFAULT_JIRA_META_EXPIRATION_PERIOD
34
+ end
35
+
36
+ def snow_display_value
37
+ @snow_display_value ||= DEFAULT_SNOW_DISPLAY_VALUE
38
+ end
39
+
40
+ def snow_fields_filter_class
41
+ @snow_fields_filter_class.presence
42
+ end
43
+
44
+ def snow_fields_filter_class=(filter_klass)
45
+ raise_configuration_error(filter_klass) unless filter_klass < Base::FieldsFilter
46
+
47
+ @snow_fields_filter_class = filter_klass
48
+ end
49
+
50
+ def ssl_options
51
+ @ssl_options ||= DEFAULT_SSL_OPTIONS
52
+ end
53
+
54
+ private
55
+
56
+ def raise_configuration_error(filter_klass)
57
+ message = "Filter class '#{filter_klass.to_s}' isn't a subclass of Base::FieldsFilter"
58
+ raise Base::Errors::ConfigurationError, message
59
+ end
60
+ end
61
+ end