ticket_abstractor_client 1.29.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +199 -159
- data/lib/ticket_abstractor_client.rb +11 -39
- data/lib/ticket_abstractor_client/base/attachment.rb +52 -0
- data/lib/ticket_abstractor_client/base/client.rb +54 -0
- data/lib/ticket_abstractor_client/base/comment.rb +35 -0
- data/lib/ticket_abstractor_client/base/errors.rb +25 -0
- data/lib/ticket_abstractor_client/base/fields_filter.rb +15 -0
- data/lib/ticket_abstractor_client/base/response_handler.rb +28 -0
- data/lib/ticket_abstractor_client/base/ticket.rb +79 -0
- data/lib/ticket_abstractor_client/client_helper.rb +29 -0
- data/lib/ticket_abstractor_client/configuration.rb +61 -0
- data/lib/ticket_abstractor_client/jira/attachment.rb +42 -0
- data/lib/ticket_abstractor_client/jira/client.rb +83 -0
- data/lib/ticket_abstractor_client/jira/comment.rb +35 -0
- data/lib/ticket_abstractor_client/jira/errors.rb +25 -0
- data/lib/ticket_abstractor_client/jira/fields_meta.rb +36 -0
- data/lib/ticket_abstractor_client/jira/params_builder.rb +96 -0
- data/lib/ticket_abstractor_client/jira/ticket.rb +131 -0
- data/lib/ticket_abstractor_client/service_now/attachment.rb +41 -0
- data/lib/ticket_abstractor_client/service_now/client.rb +78 -0
- data/lib/ticket_abstractor_client/service_now/comment.rb +30 -0
- data/lib/ticket_abstractor_client/service_now/errors.rb +25 -0
- data/lib/ticket_abstractor_client/service_now/params_builder.rb +56 -0
- data/lib/ticket_abstractor_client/service_now/ticket.rb +120 -0
- data/lib/ticket_abstractor_client/version.rb +1 -1
- metadata +52 -9
- data/lib/ticket_abstractor_client/brouha_client.rb +0 -33
- data/lib/ticket_abstractor_client/client.rb +0 -31
- data/lib/ticket_abstractor_client/i_logger_client.rb +0 -5
- data/lib/ticket_abstractor_client/itrc_client.rb +0 -33
- data/lib/ticket_abstractor_client/jira_client.rb +0 -128
- data/lib/ticket_abstractor_client/service_now_client.rb +0 -61
- data/lib/ticket_abstractor_client/sev_one_client.rb +0 -21
@@ -0,0 +1,42 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
class Attachment < Base::Attachment
|
4
|
+
attr_accessor :ticket_id, :endpoint, :project
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def fetch_by_id_and_name(id, name, endpoint)
|
8
|
+
file_path = download_attachment(id, name, endpoint)
|
9
|
+
new(file_path: file_path, endpoint: endpoint)
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch_by_ticket_id(ticket_id, endpoint)
|
13
|
+
ticket = Client.new(endpoint).get_ticket_by_id(ticket_id: ticket_id, fields: :attachment)
|
14
|
+
|
15
|
+
ticket['fields']['attachment'].each_with_object([]) do |hash, attachments|
|
16
|
+
attachment = fetch_by_id_and_name(hash['id'], hash['filename'], endpoint)
|
17
|
+
attachment.ticket_id = ticket_id
|
18
|
+
attachments << attachment
|
19
|
+
end
|
20
|
+
end
|
21
|
+
alias_method :fetch, :fetch_by_ticket_id
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def download_attachment(id, name, endpoint)
|
26
|
+
file_content = Client.new(endpoint).get_attachment(id: id, name: name)
|
27
|
+
save_content(file_content, "#{id}_#{name}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(opts)
|
32
|
+
super(opts)
|
33
|
+
@ticket_id, @endpoint, @project = opts.values_at(:ticket_id, :endpoint, :project)
|
34
|
+
end
|
35
|
+
|
36
|
+
def sync!
|
37
|
+
response = Client.new(@endpoint).create_attachment(self)
|
38
|
+
@external_created_at = response.first['created']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
class Client < Base::Client
|
4
|
+
include Jira::ParamsBuilder
|
5
|
+
|
6
|
+
def initialize(endpoint = 'tp')
|
7
|
+
super()
|
8
|
+
@jira_endpoint = endpoint
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_attachment(attachment)
|
12
|
+
args, params = build_attachment_params(attachment)
|
13
|
+
post(__method__, args, params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_comment(comment)
|
17
|
+
post(__method__, build_comment_params(comment))
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_ticket(ticket)
|
21
|
+
post(__method__, build_ticket_params(ticket))
|
22
|
+
end
|
23
|
+
|
24
|
+
def endpoints
|
25
|
+
get(__method__)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_all_fields
|
29
|
+
get(__method__)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_attachment(opts)
|
33
|
+
get(__method__, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_comments(opts)
|
37
|
+
get(__method__, opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_fields_by_project(opts)
|
41
|
+
get(__method__, opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_ticket_by_id(opts)
|
45
|
+
get(__method__, opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_ticket_status(opts)
|
49
|
+
get(__method__, build_status_params(opts))
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_tickets_by_jql(opts)
|
53
|
+
get(__method__, opts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_tickets_statuses(opts)
|
57
|
+
get(__method__, opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_ticket(ticket)
|
61
|
+
post(__method__, build_ticket_params(ticket))
|
62
|
+
end
|
63
|
+
|
64
|
+
def transit_ticket(ticket)
|
65
|
+
post(__method__, build_transitions_params(ticket))
|
66
|
+
end
|
67
|
+
|
68
|
+
def transitions_list(ticket_id)
|
69
|
+
get(__method__, { ticket_id: ticket_id })
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def get(path, args = {}, params = {})
|
75
|
+
super("jira/#{path}", args, params.merge(jira_endpoint: @jira_endpoint))
|
76
|
+
end
|
77
|
+
|
78
|
+
def post(path, args = {}, params = {})
|
79
|
+
super("jira/#{path}", args, params.merge(jira_endpoint: @jira_endpoint))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
class Comment < Base::Comment
|
4
|
+
attr_accessor :ticket_id, :endpoint, :project, :author_display_name
|
5
|
+
|
6
|
+
def self.fetch(ticket_id, endpoint)
|
7
|
+
comments_list = Client.new(endpoint).get_comments(ticket_id: ticket_id)
|
8
|
+
comments_list['comments'].map do |raw_comment|
|
9
|
+
new(
|
10
|
+
author: raw_comment['author']['name'],
|
11
|
+
author_display_name: raw_comment['author']['displayName'],
|
12
|
+
body: raw_comment['body'],
|
13
|
+
external_created_at: raw_comment['created'],
|
14
|
+
ticket_id: ticket_id,
|
15
|
+
endpoint: endpoint
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(opts)
|
21
|
+
super(opts)
|
22
|
+
@author_display_name = opts[:author_display_name]
|
23
|
+
@ticket_id, @endpoint, @project = opts.values_at(:ticket_id, :endpoint, :project)
|
24
|
+
end
|
25
|
+
|
26
|
+
def sync!
|
27
|
+
created_comment_hash = Client.new(@endpoint).create_comment(self)
|
28
|
+
@author = created_comment_hash['author']['name']
|
29
|
+
@author_display_name = created_comment_hash['author']['displayName']
|
30
|
+
@external_created_at = created_comment_hash['created']
|
31
|
+
self.set_data_hash!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
module Errors
|
4
|
+
class BaseError < Base::Errors::BaseError; end
|
5
|
+
class NotImplementedError < BaseError; end
|
6
|
+
class ConfigurationError < Base::Errors::ConfigurationError; end
|
7
|
+
class UnexpectedError < Base::Errors::UnexpectedError; end
|
8
|
+
|
9
|
+
class AttachmentArgumentError < Base::Errors::AttachmentArgumentError; end
|
10
|
+
class CommentArgumentError < Base::Errors::CommentArgumentError; end
|
11
|
+
class ClientArgumentError < Base::Errors::ClientArgumentError; end
|
12
|
+
class TicketArgumentError < Base::Errors::TicketArgumentError; end
|
13
|
+
|
14
|
+
class AttachmentError < Base::Errors::AttachmentError; end
|
15
|
+
class CommentError < Base::Errors::CommentError; end
|
16
|
+
class TicketError < Base::Errors::TicketError; end
|
17
|
+
|
18
|
+
class ConnectionError < Base::Errors::ConnectionError; end
|
19
|
+
class NotFoundError < Base::Errors::NotFoundError; end
|
20
|
+
class ParseError < Base::Errors::ParseError; end
|
21
|
+
class RequsetFormatError < Base::Errors::RequsetFormatError; end
|
22
|
+
class AccessError < Base::Errors::AccessError; end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
class FieldsMeta < Hash
|
4
|
+
attr_accessor :endpoint
|
5
|
+
attr_reader :updated_at
|
6
|
+
|
7
|
+
def initialize(endpoint)
|
8
|
+
super()
|
9
|
+
@endpoint = endpoint
|
10
|
+
update!
|
11
|
+
end
|
12
|
+
|
13
|
+
def update!
|
14
|
+
get_meta_data
|
15
|
+
update(@meta_data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def expired?
|
19
|
+
expiration_period = TicketAbstractorClient.configuration.jira_meta_expiration_period
|
20
|
+
(Time.now.to_i - @updated_at) > expiration_period
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def get_meta_data
|
26
|
+
raw_meta_data = Client.new(@endpoint).get_all_fields
|
27
|
+
@meta_data = filter_meta_data(raw_meta_data)
|
28
|
+
@updated_at = Time.now.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter_meta_data(raw_meta_data)
|
32
|
+
raw_meta_data.each_with_object({}) { |field, meta| meta[field['id']] = field['schema'] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
module ParamsBuilder
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def build_attachment_params(attachment)
|
7
|
+
[
|
8
|
+
{ ticket_id: attachment.ticket_id },
|
9
|
+
{ attachments: [File.new(attachment.file_path, 'rb')] }
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_comment_params(comment)
|
14
|
+
{ ticket_id: comment.ticket_id, comment: { body: comment.body } }
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_ticket_params(ticket)
|
18
|
+
params_hash = { ticket_params: {} }
|
19
|
+
params_hash[:ticket_id] = ticket.ticket_id if ticket.ticket_id.present?
|
20
|
+
|
21
|
+
fields = ticket.fields.each_with_object({}) do |(field_name, field_value), fields|
|
22
|
+
value = jira_field_value(ticket.endpoint, field_name, field_value)
|
23
|
+
fields[field_name] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
params_hash[:ticket_params].merge!(fields: fields)
|
27
|
+
params_hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_transitions_params(ticket)
|
31
|
+
new_status = ticket.status
|
32
|
+
transition = ticket.available_statuses[new_status]
|
33
|
+
|
34
|
+
raise Errors::ClientArgumentError, "Status '#{new_status}' is incorrect or prohibited" if transition.blank?
|
35
|
+
|
36
|
+
{ ticket_id: ticket.ticket_id, transition_params: { transition: { id: transition } } }
|
37
|
+
end
|
38
|
+
|
39
|
+
def jira_field_value(endpoint, name, value)
|
40
|
+
field_meta = fields_meta(endpoint)[name]
|
41
|
+
return unless field_meta
|
42
|
+
|
43
|
+
if field_meta['system'] # system fields mapping
|
44
|
+
case field_meta['type']
|
45
|
+
when 'project'
|
46
|
+
{ key: value }
|
47
|
+
when 'string'
|
48
|
+
value
|
49
|
+
when 'datetime'
|
50
|
+
Time.parse(value).to_s(:jira_iso8601) rescue value
|
51
|
+
when 'date'
|
52
|
+
Date.parse(value).to_s rescue value
|
53
|
+
when 'number'
|
54
|
+
value.to_i
|
55
|
+
when 'array'
|
56
|
+
Array.wrap(value)
|
57
|
+
else # when 'user', 'status', 'issuetype', 'priority'
|
58
|
+
{ name: value }
|
59
|
+
end
|
60
|
+
|
61
|
+
elsif custom_type = field_meta['custom'] # custom fields mapping
|
62
|
+
case custom_type
|
63
|
+
when /(:cascadingselect|:radiobuttons|:select)\Z/
|
64
|
+
{ value: value }
|
65
|
+
when /:datepicker\Z/
|
66
|
+
Date.parse(value).to_s rescue value
|
67
|
+
when /:datetime\Z/
|
68
|
+
Time.parse(value).to_s(:jira_iso8601) rescue value
|
69
|
+
when /(:freetextfield|:textfield|:textarea|:url)\Z/
|
70
|
+
value
|
71
|
+
when /(:grouppicker|:singleversionpicker|:userpicker)\Z/
|
72
|
+
{ name: value }
|
73
|
+
when /(:numberfield|:float)\Z/
|
74
|
+
value.to_i
|
75
|
+
when /:projectpicker\Z/
|
76
|
+
{ key: value }
|
77
|
+
when /:labels\Z/
|
78
|
+
Array.wrap(value)
|
79
|
+
when /(:multigrouppicker|:multiuserpicker|:versionpicker)\Z/
|
80
|
+
Array.wrap(value).map { |v| { name: v } }
|
81
|
+
when /:multiselect\Z/
|
82
|
+
Array.wrap(value).map { |v| { value: v } }
|
83
|
+
else
|
84
|
+
raise Errors::ClientArgumentError, "Unknown customfield type #{custom_type}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def fields_meta(endpoint)
|
90
|
+
meta = TicketAbstractorClient.configuration.jira_fields_meta[endpoint.to_sym]
|
91
|
+
meta.update! if meta.expired?
|
92
|
+
meta
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module TicketAbstractorClient
|
2
|
+
module Jira
|
3
|
+
class Ticket < Base::Ticket
|
4
|
+
|
5
|
+
def self.fetch_by_id(opts)
|
6
|
+
opts[:fields] ||= []
|
7
|
+
opts[:fields] += %w(-attachment -comment)
|
8
|
+
fetch(:by_id, opts).first
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.fetch_tickets_by_jql(opts)
|
12
|
+
fetch(:by_jql, opts)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(opts)
|
16
|
+
super(opts)
|
17
|
+
@fields['project'] = @project if @project.present?
|
18
|
+
@status = @fields.delete('status')
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_attachment(attachment)
|
22
|
+
@attachments ||= []
|
23
|
+
@attachments << Attachment.new(attachment.merge!(ticket_id: @ticket_id, endpoint: @endpoint))
|
24
|
+
@changes[:new_attachments] += 1
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_comment(comment)
|
29
|
+
@comments ||= []
|
30
|
+
@comments << Comment.new(comment.merge!(ticket_id: @ticket_id, endpoint: @endpoint))
|
31
|
+
@changes[:new_comments] += 1
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def attachments
|
36
|
+
return @attachments if @attachments.present?
|
37
|
+
|
38
|
+
return [] if @ticket_id.blank? && @attachments.blank?
|
39
|
+
|
40
|
+
@attachments = Attachment.fetch(@ticket_id, @endpoint)
|
41
|
+
end
|
42
|
+
|
43
|
+
def available_statuses
|
44
|
+
raw_response = Client.new(@endpoint).transitions_list(@ticket_id)
|
45
|
+
raw_response['transitions'].each_with_object({}) do |transition, transitions_map|
|
46
|
+
transitions_map[transition['to']['name']] = transition['id']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def comments
|
51
|
+
return @comments if @comments.present?
|
52
|
+
|
53
|
+
return [] if @ticket_id.blank? && @comments.blank?
|
54
|
+
|
55
|
+
@comments = Comment.fetch(@ticket_id, @endpoint)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reload!
|
59
|
+
ticket = Ticket.fetch_by_id(ticket_id: @ticket_id, project: @project, endpoint: @endpoint)
|
60
|
+
@fields = ticket.fields
|
61
|
+
@status = ticket.status
|
62
|
+
end
|
63
|
+
|
64
|
+
def sync!
|
65
|
+
raise(Errors::TicketArgumentError, 'No changes to apply') unless self.any_changes?
|
66
|
+
|
67
|
+
return self.create_ticket if @changes[:create]
|
68
|
+
|
69
|
+
update_ticket if @changes[:update]
|
70
|
+
update_status if @changes[:new_status]
|
71
|
+
sync_comments unless @changes[:new_comments].zero?
|
72
|
+
sync_attachments unless @changes[:new_attachments].zero?
|
73
|
+
self.reload!
|
74
|
+
self.reset_changes!
|
75
|
+
end
|
76
|
+
|
77
|
+
def updated_at
|
78
|
+
@fields['updated']
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def self.fetch(selektor, opts)
|
84
|
+
methods_map = { by_id: :get_ticket_by_id, by_jql: :get_tickets_by_jql }
|
85
|
+
opts = opts.with_indifferent_access
|
86
|
+
endpoint = opts.delete(:endpoint)
|
87
|
+
raw_tickets = Client.new(endpoint).public_send(methods_map.fetch(selektor.to_sym), opts)
|
88
|
+
raw_tickets = raw_tickets['issues'] if raw_tickets.key?('issues')
|
89
|
+
|
90
|
+
Array.wrap(raw_tickets).map do |raw_ticket|
|
91
|
+
fields = filter_fields(raw_ticket['fields'])
|
92
|
+
ticket = new(ticket_id: raw_ticket['key'], endpoint: endpoint, fields: fields)
|
93
|
+
ticket.reset_changes!
|
94
|
+
ticket
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.filter_fields(fields)
|
99
|
+
fields.extract!('attachment', 'comment')
|
100
|
+
filter_fields_klass = TicketAbstractorClient.configuration.jira_fields_filter_class
|
101
|
+
|
102
|
+
return fields if filter_fields_klass.blank?
|
103
|
+
|
104
|
+
filter_fields_klass.new(fields).filter_fields
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_ticket
|
108
|
+
response = Client.new(@endpoint).create_ticket(self)
|
109
|
+
@ticket_id = response['key']
|
110
|
+
self.reload!
|
111
|
+
self.reset_changes!
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_status
|
115
|
+
Client.new(@endpoint).transit_ticket(self)
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_ticket
|
119
|
+
Client.new(@endpoint).update_ticket(self)
|
120
|
+
end
|
121
|
+
|
122
|
+
def sync_attachments
|
123
|
+
@attachments.last(@changes[:new_attachments]).map(&:sync!)
|
124
|
+
end
|
125
|
+
|
126
|
+
def sync_comments
|
127
|
+
@comments.last(@changes[:new_comments]).map(&:sync!)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|