tessera 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.overcommit.yml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/Rakefile +6 -0
- data/_config.yml +1 -0
- data/bin/console +6 -0
- data/bin/setup +8 -0
- data/lib/tessera.rb +55 -0
- data/lib/tessera/api/session.rb +27 -0
- data/lib/tessera/api/ticket_create.rb +36 -0
- data/lib/tessera/api/ticket_get.rb +37 -0
- data/lib/tessera/api/ticket_list.rb +37 -0
- data/lib/tessera/api/ticket_search.rb +35 -0
- data/lib/tessera/configuration.rb +46 -0
- data/lib/tessera/model/base.rb +28 -0
- data/lib/tessera/model/session.rb +11 -0
- data/lib/tessera/model/ticket.rb +58 -0
- data/lib/tessera/otrs/article.rb +21 -0
- data/lib/tessera/otrs/attachment.rb +13 -0
- data/lib/tessera/otrs/base.rb +11 -0
- data/lib/tessera/otrs/ticket.rb +18 -0
- data/lib/tessera/request.rb +57 -0
- data/lib/tessera/ticket.rb +76 -0
- data/lib/tessera/utils/errors.rb +25 -0
- data/lib/tessera/version.rb +3 -0
- data/templates/ticket.yml +72 -0
- data/tessera.gemspec +42 -0
- data/wercker.yml +7 -0
- metadata +220 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
module Tessera
|
6
|
+
module Api
|
7
|
+
class TicketCreate
|
8
|
+
def initialize(body_content)
|
9
|
+
@body = body.merge(body_content)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.call(body_content)
|
13
|
+
new(body_content).call
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
response = Tessera::Request.new(:POST, 'Ticket', @body).send
|
18
|
+
JSON.parse(response.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def session_id
|
22
|
+
session = Session.create
|
23
|
+
# TODO: solve token creation failure with error
|
24
|
+
session.session_id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def body
|
30
|
+
{
|
31
|
+
SessionID: session_id
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
module Tessera
|
6
|
+
module Api
|
7
|
+
class TicketGet
|
8
|
+
def initialize(ticket_id)
|
9
|
+
@ticket_id = ticket_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.call(ticket_id)
|
13
|
+
new(ticket_id).call
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
response = Tessera::Request.new(:GET, "Ticket/#{@ticket_id}", body).send
|
18
|
+
JSON.parse(response.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def session_id
|
22
|
+
session = Session.create
|
23
|
+
# TODO: solve token creation failure with error
|
24
|
+
session.session_id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def body
|
30
|
+
{
|
31
|
+
TicketID: @ticket_id,
|
32
|
+
SessionID: session_id
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
module Tessera
|
6
|
+
module Api
|
7
|
+
class TicketList
|
8
|
+
def self.call(ticket_ids)
|
9
|
+
new(ticket_ids).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(ticket_ids)
|
13
|
+
@ticket_ids = ticket_ids
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
response = Tessera::Request.new(:GET, 'TicketList', body).send
|
18
|
+
JSON.parse(response.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def session_id
|
22
|
+
session = Session.create
|
23
|
+
# TODO: solve token creation failure with error
|
24
|
+
session.session_id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def body
|
30
|
+
{
|
31
|
+
TicketID: @ticket_ids,
|
32
|
+
SessionID: session_id
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
##
|
2
|
+
# Class for searching through tickets
|
3
|
+
# as params hash any of these in official docs could be used:
|
4
|
+
# http://doc.otrs.com/doc/api/otrs/5.0/Perl/Kernel/GenericInterface/Operation/Ticket/TicketSearch.pm.html
|
5
|
+
#
|
6
|
+
module Tessera
|
7
|
+
module Api
|
8
|
+
class TicketSearch
|
9
|
+
def self.call(params = nil)
|
10
|
+
new(params).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(params)
|
14
|
+
@params = params
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
response = Tessera::Request.new(:GET, 'Ticket', body).send
|
19
|
+
JSON.parse(response.body)
|
20
|
+
end
|
21
|
+
|
22
|
+
def session_id
|
23
|
+
session = Session.create
|
24
|
+
# TODO: solve token creation failure with error
|
25
|
+
session.session_id
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def body
|
31
|
+
@params.to_h.merge(SessionID: session_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# This is configuration class that allows you to set your config like so:
|
4
|
+
#
|
5
|
+
# Tessera.configure do |config|
|
6
|
+
# config.username = 'jon'
|
7
|
+
# config.password = 'SuperSecret'
|
8
|
+
# config.base_url = 'https://otrs.com/otrs/nph-genericinterface.pl/Webservice/rest/'
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# or
|
12
|
+
#
|
13
|
+
# by simply assigning params by themself:
|
14
|
+
#
|
15
|
+
# config = Tessera::Configuration.new
|
16
|
+
# config.username = 'jon'
|
17
|
+
# config.password = 'SuperSecret'
|
18
|
+
# config.base_url = 'https://otrs.com/otrs/nph-genericinterface.pl/Webservice/rest/'
|
19
|
+
# Tessera.configuration = config
|
20
|
+
#
|
21
|
+
module Tessera
|
22
|
+
class Configuration
|
23
|
+
attr_writer :username, :password, :base_url
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@username = nil
|
27
|
+
@password = nil
|
28
|
+
@base_url = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def username
|
32
|
+
raise ConfigNotSet, 'username' unless @username
|
33
|
+
@username
|
34
|
+
end
|
35
|
+
|
36
|
+
def password
|
37
|
+
raise ConfigNotSet, 'password' unless @password
|
38
|
+
@password
|
39
|
+
end
|
40
|
+
|
41
|
+
def base_url
|
42
|
+
raise ConfigNotSet, 'base_url' unless @base_url
|
43
|
+
@base_url
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tessera
|
2
|
+
module Model
|
3
|
+
class Base
|
4
|
+
attr_accessor :response, :errors, :code
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@response = response
|
8
|
+
@code = @response.code
|
9
|
+
parse_errors if parsed_body
|
10
|
+
parse_attributes if parsed_body
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def parse_errors
|
16
|
+
@errors = parsed_body['Error']
|
17
|
+
end
|
18
|
+
|
19
|
+
def parsed_body
|
20
|
+
JSON.parse(@response.body) if @response.body
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_attributes
|
24
|
+
raise NotImplementedError, 'Model must implement attribute mapping'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Tessera
|
2
|
+
module Model
|
3
|
+
class Ticket < Base
|
4
|
+
attr_accessor :ticket_id, :lock_id, :real_till_time_not_used, :changed,
|
5
|
+
:customer_user_id, :queue_id, :escalation_solution_time, :owner_id,
|
6
|
+
:unlock_timeout, :priority_id, :escalation_time, :type, :queue,
|
7
|
+
:escalation_response_time, :escalation_update_time, :customer_id,
|
8
|
+
:age, :until_time, :responsible, :lock, :priority, :state_id,
|
9
|
+
:created_by, :state, :type_id, :ticket_number, :group_id, :created,
|
10
|
+
:service_id, :owner, :slaid, :state_type, :title, :responsible_id,
|
11
|
+
:create_tim_nix, :changed_by, :archive_flag
|
12
|
+
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
15
|
+
def initialize(params)
|
16
|
+
@ticket_id = params['TicketID']
|
17
|
+
@lock_id = params['LockID']
|
18
|
+
@real_till_time_not_used = params['RealTillTimeNotUsed']
|
19
|
+
@changed = params['Changed']
|
20
|
+
@customer_user_id = params['CustomerUserID']
|
21
|
+
@queue_id = params['QueueID']
|
22
|
+
@escalation_solution_time = params['EscalationSolutionTime']
|
23
|
+
@owner_id = params['OwnerID']
|
24
|
+
@unlock_timeout = params['UnlockTimeout']
|
25
|
+
@priority_id = params['PriorityID']
|
26
|
+
@escalation_time = params['EscalationTime']
|
27
|
+
@type = params['Type']
|
28
|
+
@queue = params['Queue']
|
29
|
+
@escalation_response_time = params['EscalationResponseTime']
|
30
|
+
@escalation_update_time = params['EscalationUpdateTime']
|
31
|
+
@customer_id = params['CustomerID']
|
32
|
+
@age = params['Age']
|
33
|
+
@until_time = params['UntilTime']
|
34
|
+
@responsible = params['Responsible']
|
35
|
+
@lock = params['Lock']
|
36
|
+
@priority = params['Priority']
|
37
|
+
@state_id = params['StateID']
|
38
|
+
@created_by = params['CreateBy']
|
39
|
+
@state = params['State']
|
40
|
+
@type_id = params['TypeID']
|
41
|
+
@ticket_number = params['TicketNumber']
|
42
|
+
@group_id = params['GroupID']
|
43
|
+
@created = params['Created']
|
44
|
+
@service_id = params['ServiceID']
|
45
|
+
@owner = params['Owner']
|
46
|
+
@slaid = params['SLAID']
|
47
|
+
@state_type = params['StateType']
|
48
|
+
@title = params['Title']
|
49
|
+
@responsible_id = params['ResponsibleID']
|
50
|
+
@create_tim_nix = params['CreateTimeUnix']
|
51
|
+
@changed_by = params['ChnageBy']
|
52
|
+
@archive_flag = params['ArchiveFlag']
|
53
|
+
end
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
55
|
+
# rubocop:enable Metrics/MethodLength
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tessera
|
2
|
+
module Otrs
|
3
|
+
class Article < Base
|
4
|
+
attr_accessor :From, :To, :Subject, :Body, :ContentType, :CommunicationChannel,
|
5
|
+
:ArticleSend
|
6
|
+
|
7
|
+
# rubocop:disable Metrics/ParameterLists
|
8
|
+
def initialize(from:, to: nil, subject:, body:, communication_channel: 'Email',
|
9
|
+
article_send: 0)
|
10
|
+
@From = from
|
11
|
+
@To = to
|
12
|
+
@Subject = subject
|
13
|
+
@Body = body
|
14
|
+
@ContentType = 'text/plain; charset=utf8'
|
15
|
+
@CommunicationChannel = communication_channel
|
16
|
+
@ArticleSend = article_send
|
17
|
+
end
|
18
|
+
# rubocop:enable Metrics/ParameterLists
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Tessera
|
2
|
+
module Otrs
|
3
|
+
class Attachment < Base
|
4
|
+
attr_accessor :Content, :ContentType, :Filename
|
5
|
+
|
6
|
+
def initialize(tempfile:)
|
7
|
+
@Content = Base64.encode64(tempfile.read)
|
8
|
+
@ContentType = tempfile.content_type
|
9
|
+
@Filename = tempfile.original_filename
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Tessera
|
2
|
+
module Otrs
|
3
|
+
class Ticket < Base
|
4
|
+
attr_accessor :Title, :QueueID, :State, :PriorityID, :CustomerUser, :CustomerID
|
5
|
+
|
6
|
+
# rubocop:disable Metrics/ParameterLists
|
7
|
+
def initialize(title:, queue:, state:, priority:, customer_user:, customer_id:)
|
8
|
+
@Title = title
|
9
|
+
@QueueID = queue
|
10
|
+
@State = state
|
11
|
+
@PriorityID = priority
|
12
|
+
@CustomerUser = customer_user
|
13
|
+
@CustomerID = customer_id
|
14
|
+
end
|
15
|
+
# rubocop:enable Metrics/ParameterLists
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# Request class
|
4
|
+
#
|
5
|
+
# Methods acepted:
|
6
|
+
# GET
|
7
|
+
# POST
|
8
|
+
# PATCH
|
9
|
+
#
|
10
|
+
# (basically it uses Net::HTTP, but other methods are not supported by API anyway...)
|
11
|
+
#
|
12
|
+
# Endpoint is based on you yaml setting
|
13
|
+
# You can find a template in templates directory here in repository
|
14
|
+
#
|
15
|
+
# Body gets assembled in (put name of class here) class :) (not here yet) TODO
|
16
|
+
#
|
17
|
+
# return value is Net::HTTPResponse which has to be parsed TODO (put name of class here)
|
18
|
+
#
|
19
|
+
module Tessera
|
20
|
+
class Request
|
21
|
+
def initialize(method, endpoint, body)
|
22
|
+
@method = method
|
23
|
+
@endpoint = endpoint
|
24
|
+
@body = body
|
25
|
+
end
|
26
|
+
|
27
|
+
def send
|
28
|
+
net_http_send
|
29
|
+
# Response.new(net_http_send)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def net_http_send
|
35
|
+
uri = URI("#{Tessera.configuration.base_url}#{@endpoint}")
|
36
|
+
|
37
|
+
request = net_http_class.new(uri)
|
38
|
+
headers.each { |k, v| request[k] = v }
|
39
|
+
request.body = @body.to_json
|
40
|
+
|
41
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
|
42
|
+
http.request(request)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def net_http_class
|
47
|
+
Kernel.const_get("Net::HTTP::#{@method.capitalize}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def headers
|
51
|
+
{
|
52
|
+
'Accept-Encoding' => 'application/json',
|
53
|
+
'Content-Type' => 'application/json'
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Tessera
|
2
|
+
class Ticket
|
3
|
+
class << self
|
4
|
+
# rubocop:disable Metrics/AbcSize
|
5
|
+
# rubocop:disable Metrics/MethodLength
|
6
|
+
def find(ticket_id)
|
7
|
+
ticket = if ticket_id.is_a? Array
|
8
|
+
::Tessera::Api::TicketList.call(ticket_id)
|
9
|
+
else
|
10
|
+
::Tessera::Api::TicketGet.call(ticket_id)
|
11
|
+
end
|
12
|
+
if ticket['Error']
|
13
|
+
raise TicketNotFound.new(ticket['Error']['ErrorMessage'],
|
14
|
+
ticket['Error']['ErrorCode'])
|
15
|
+
elsif ticket['Ticket'] && ticket['Ticket'].count > 1
|
16
|
+
ticket['Ticket'].map do |ticket_json|
|
17
|
+
::Tessera::Model::Ticket.new(ticket_json)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
::Tessera::Model::Ticket.new(ticket['Ticket'].first)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
# rubocop:enable Metrics/MethodLength
|
24
|
+
# rubocop:enable Metrics/AbcSize
|
25
|
+
|
26
|
+
def where(params)
|
27
|
+
result = Tessera::Api::TicketSearch.call(params)
|
28
|
+
ticket_ids = result['TicketID'] ? result['TicketID'] : []
|
29
|
+
new(ticket_ids)
|
30
|
+
end
|
31
|
+
|
32
|
+
# rubocop:disable Metrics/AbcSize
|
33
|
+
# rubocop:disable Metrics/MethodLength
|
34
|
+
def create(params)
|
35
|
+
ticket = Tessera::Otrs::Ticket.new(params[:ticket]).to_hash
|
36
|
+
article = Tessera::Otrs::Article.new(params[:article]).to_hash
|
37
|
+
attachment = if params[:attachment]
|
38
|
+
case params[:attachment].class
|
39
|
+
when Hash
|
40
|
+
Tessera::Otrs::Attachment.new(params[:attachment]).to_hash
|
41
|
+
when Array
|
42
|
+
params[:attachment].map do |a|
|
43
|
+
Tessera::Otrs::Attachment.new(a).to_hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
body = { Ticket: ticket }
|
48
|
+
body = body.merge(Article: article)
|
49
|
+
body = body.merge(Attachment: attachment) if attachment
|
50
|
+
|
51
|
+
Tessera::Api::TicketCreate.call(body)
|
52
|
+
end
|
53
|
+
# rubocop:enable Metrics/MethodLength
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(result)
|
58
|
+
@result = result
|
59
|
+
end
|
60
|
+
|
61
|
+
def count
|
62
|
+
@result.size
|
63
|
+
end
|
64
|
+
|
65
|
+
def ticket_ids
|
66
|
+
@result.map(&:to_i)
|
67
|
+
end
|
68
|
+
|
69
|
+
def tickets
|
70
|
+
return [] if @result.empty?
|
71
|
+
@tickets ||= @result.map do |id|
|
72
|
+
::Tessera::Ticket.find(id)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|