gorg_service 5.3.1 → 6.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/gorg_service.gemspec +3 -1
- data/lib/gorg_service/consumer/errors.rb +29 -3
- data/lib/gorg_service/consumer/listener.rb +17 -14
- data/lib/gorg_service/consumer/message_handler/base.rb +34 -23
- data/lib/gorg_service/event_message.rb +13 -0
- data/lib/gorg_service/log_message.rb +46 -0
- data/lib/gorg_service/message.rb +223 -107
- data/lib/gorg_service/message/formatters.rb +313 -0
- data/lib/gorg_service/message/json_schema.rb +1 -87
- data/lib/gorg_service/reply_message.rb +41 -0
- data/lib/gorg_service/request_message.rb +13 -0
- data/lib/gorg_service/rspec/log_message_handler.rb +4 -4
- data/lib/gorg_service/version.rb +1 -1
- metadata +37 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4176b3c8a8689ae7474eaa5420f3b45c96fbc916
|
4
|
+
data.tar.gz: 0b48b75295d30fbeeabd9245cf22e9d97174a02f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fe317544a9bf0a1cb7b5583f0489ba734ff982f9d1142527bfc69147d2779ecde58224720138d816fa8eb4e1fce948f78f89d9672765ee67feb36d880bc0b7d
|
7
|
+
data.tar.gz: cec6262709a87326414100b87707f89382f9344be4345c7147bb8403a1040d5b9d9e7ad3615daac78a812026ccf09d4443ae168d9588df792dcf5efcdd721828
|
data/gorg_service.gemspec
CHANGED
@@ -32,8 +32,10 @@ Gem::Specification.new do |spec|
|
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 1.11"
|
34
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
+
spec.add_development_dependency "yard", "~> 0.8.7"
|
35
36
|
spec.add_development_dependency "rspec", "~> 3.0"
|
36
|
-
spec.add_development_dependency "codeclimate-test-reporter", "~>
|
37
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0"
|
38
|
+
spec.add_development_dependency "simplecov", "~> 0.13"
|
37
39
|
spec.add_development_dependency 'bogus', '~> 0.1.6'
|
38
40
|
spec.add_development_dependency 'byebug', '~> 9.0'
|
39
41
|
end
|
@@ -6,10 +6,14 @@ class GorgService
|
|
6
6
|
#Common behavior of failling errors
|
7
7
|
class FailError < StandardError
|
8
8
|
attr_reader :error_raised
|
9
|
+
attr_reader :error_name
|
10
|
+
attr_accessor :gorg_service_message
|
9
11
|
|
10
|
-
def initialize(message = nil, error_raised = nil)
|
12
|
+
def initialize(message = nil, error_raised = nil, gorg_service_message: nil, error_name: nil)
|
11
13
|
@message = message
|
12
14
|
@error_raised = error_raised
|
15
|
+
@gorg_service_message = gorg_service_message
|
16
|
+
@error_name = error_name
|
13
17
|
end
|
14
18
|
|
15
19
|
def message
|
@@ -19,19 +23,41 @@ class GorgService
|
|
19
23
|
def type
|
20
24
|
""
|
21
25
|
end
|
26
|
+
|
27
|
+
def to_log_message
|
28
|
+
@gorg_service_message.log_message(
|
29
|
+
level: self.log_level,
|
30
|
+
error_type: self.type,
|
31
|
+
error_name: @error_name
|
32
|
+
)
|
33
|
+
end
|
22
34
|
end
|
23
35
|
|
24
36
|
#Softfail error : This message should be processed again later
|
25
37
|
class SoftfailError < FailError
|
26
38
|
def type
|
27
|
-
"
|
39
|
+
"softfail"
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_level
|
43
|
+
3
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_log_message
|
47
|
+
r=super
|
48
|
+
r.next_try_in=GorgService.configuration.rabbitmq_deferred_time.to_i
|
49
|
+
r
|
28
50
|
end
|
29
51
|
end
|
30
52
|
|
31
53
|
#Hardfail error : This message is not processable and will never be
|
32
54
|
class HardfailError < FailError
|
33
55
|
def type
|
34
|
-
"
|
56
|
+
"hardfail"
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_level
|
60
|
+
4
|
35
61
|
end
|
36
62
|
end
|
37
63
|
end
|
@@ -64,35 +64,38 @@ class GorgService
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def process_softfail(e, message)
|
67
|
-
message
|
67
|
+
e.gorg_service_message ||= message
|
68
68
|
GorgService.logger.error "SOFTFAIL ERROR : #{e.message}"
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
process_logging(e)
|
70
|
+
message.softfail_count+=1
|
71
|
+
if message.softfail_count.to_i >= @max_attempts
|
72
|
+
GorgService.logger.info " DISCARD MESSAGE : too much soft errors (#{message.softfail_count})"
|
73
|
+
process_hardfail(HardfailError.new("Too Much SoftError : This message reached the limit of softerror (max: #{@max_attempts})", gorg_service_message: message, error_name: e.error_name), message)
|
72
74
|
else
|
73
75
|
send_to_deferred_queue(message)
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
79
|
def process_hardfail(e, message)
|
80
|
+
e.gorg_service_message ||= message
|
78
81
|
GorgService.logger.error "HARDFAIL ERROR : #{e.message}, #{e.error_raised&&e.error_raised.inspect}"
|
79
82
|
GorgService.logger.info " DISCARD MESSAGE"
|
80
|
-
|
81
|
-
message.log_error(e)
|
82
|
-
process_logging(message)
|
83
|
-
end
|
83
|
+
process_logging(e)
|
84
84
|
end
|
85
85
|
|
86
|
-
def process_logging(
|
86
|
+
def process_logging(error)
|
87
|
+
message=error.to_log_message
|
87
88
|
message.routing_key=@log_routing_key
|
88
89
|
GorgService::Producer.new.publish_message(message)
|
89
|
-
#RabbitmqProducer.new.send_raw(message.to_json, @log_routing_key, verbose: true) if @log_routing_key
|
90
90
|
end
|
91
91
|
|
92
|
-
def send_to_deferred_queue(
|
93
|
-
|
94
|
-
|
95
|
-
GorgService.
|
92
|
+
def send_to_deferred_queue(message)
|
93
|
+
|
94
|
+
if @env.delayed_queue_for message.routing_key
|
95
|
+
GorgService::Producer.new.publish_message(message, exchange: @env.delayed_in_exchange)
|
96
|
+
#
|
97
|
+
# @env.delayed_in_exchange.publish(msg.to_json, :routing_key => msg.routing_key)
|
98
|
+
GorgService.logger.info "DEFER MESSAGE : message sent to #{@env.delayed_in_exchange.name} with routing key #{message.routing_key}"
|
96
99
|
else
|
97
100
|
raise "DelayedQueueNotFound"
|
98
101
|
end
|
@@ -32,28 +32,29 @@ class GorgService
|
|
32
32
|
end
|
33
33
|
alias_method :msg, :message
|
34
34
|
|
35
|
-
def reply_with(
|
36
|
-
self.class.reply_to(message
|
35
|
+
def reply_with(*_args, **keyword_args)
|
36
|
+
self.class.reply_to(message,**keyword_args)
|
37
37
|
end
|
38
38
|
|
39
|
-
def raise_hardfail(
|
40
|
-
self.class.raise_hardfail(
|
39
|
+
def raise_hardfail(*args, **keyword_args)
|
40
|
+
self.class.raise_hardfail(*args, **(keyword_args.merge(message:message)))
|
41
41
|
end
|
42
42
|
|
43
|
-
def raise_softfail(
|
44
|
-
self.class.raise_softfail(
|
43
|
+
def raise_softfail(*args, **keyword_args)
|
44
|
+
self.class.raise_softfail(*args, **(keyword_args.merge(message:message)))
|
45
45
|
end
|
46
46
|
|
47
47
|
class << self
|
48
48
|
|
49
|
-
def reply_to(message,data)
|
49
|
+
def reply_to(message, data: {}, status_code: 200, error_type: nil, error_name: nil, next_try_in: nil)
|
50
50
|
if message.expect_reply?
|
51
51
|
|
52
|
-
reply=
|
53
|
-
event: message.reply_routing_key,
|
52
|
+
reply=message.reply_message(
|
54
53
|
data: data,
|
55
|
-
|
56
|
-
|
54
|
+
status_code: status_code,
|
55
|
+
error_type: error_type,
|
56
|
+
error_name: error_name,
|
57
|
+
next_try_in: next_try_in,
|
57
58
|
)
|
58
59
|
|
59
60
|
replier=GorgService::Producer.new
|
@@ -61,28 +62,38 @@ class GorgService
|
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
|
-
def raise_hardfail(error_message,message:nil, error: nil, data: nil)
|
65
|
+
def raise_hardfail(error_message,message:nil, error: nil, data: nil, status_code: 500, error_name: nil)
|
65
66
|
if message
|
66
67
|
reply_to(message,{
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
error_type: 'hardfail',
|
69
|
+
status_code: status_code,
|
70
|
+
error_name: error_name,
|
71
|
+
data:{
|
72
|
+
error_message: error_message,
|
73
|
+
debug_message: error&&error.inspect,
|
74
|
+
error_data: data
|
75
|
+
},
|
76
|
+
|
71
77
|
})
|
72
78
|
end
|
73
|
-
raise HardfailError.new(error_message, error)
|
79
|
+
raise HardfailError.new(error_message, error, gorg_service_message: message, error_name: error_name)
|
74
80
|
end
|
75
81
|
|
76
|
-
def raise_softfail(error_message,message:nil, error: nil, data: nil)
|
82
|
+
def raise_softfail(error_message,message:nil, error: nil, data: nil, status_code: 500, error_name: nil)
|
77
83
|
if message
|
78
84
|
reply_to(message,{
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
error_type: 'softfail',
|
86
|
+
next_try_in: GorgService.configuration.rabbitmq_deferred_time.to_i,
|
87
|
+
status_code: status_code,
|
88
|
+
error_name: error_name,
|
89
|
+
data:{
|
90
|
+
error_message: error_message,
|
91
|
+
debug_message: error&&error.inspect,
|
92
|
+
error_data: data
|
93
|
+
},
|
83
94
|
})
|
84
95
|
end
|
85
|
-
raise SoftfailError.new(error_message, error)
|
96
|
+
raise SoftfailError.new(error_message, error, gorg_service_message: message, error_name: error_name)
|
86
97
|
end
|
87
98
|
|
88
99
|
def handle_error(*errorClasses,&block)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class GorgService
|
2
|
+
class LogMessage<Message
|
3
|
+
# @return [String] Level of the log
|
4
|
+
# 0 => DEBUG
|
5
|
+
# 1 => INFO
|
6
|
+
# 2 => WARNING
|
7
|
+
# 3 => SOFTFAIL
|
8
|
+
# 4 => HARDFAIL
|
9
|
+
attr_accessor :level
|
10
|
+
|
11
|
+
# @return [String] Error type ('hardfail','softfail')
|
12
|
+
attr_accessor :error_type
|
13
|
+
|
14
|
+
# @return [Integer] Time until next attempts in milliseconds
|
15
|
+
attr_accessor :next_try_in
|
16
|
+
|
17
|
+
# @return [String] Name identifying the error
|
18
|
+
attr_accessor :error_name
|
19
|
+
|
20
|
+
def type
|
21
|
+
"log"
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param opts [Hash] Attributes of the message
|
25
|
+
# @option opts [String] :level See {#level}. Default : 1
|
26
|
+
# @option opts [String] :error_type See {#error_type}. Default : nil
|
27
|
+
# @option opts [Integer] :next_try_in See {#next_try_in}. Default : nil
|
28
|
+
# @option opts [String] :error_name See {#error_name}. Default : nil
|
29
|
+
# @see GorgService::Message#initialize
|
30
|
+
def initialize(opts={})
|
31
|
+
super
|
32
|
+
self.level= opts.fetch(:level,1)
|
33
|
+
self.error_type= opts.fetch(:error_type,nil)
|
34
|
+
self.next_try_in= opts.fetch(:next_try_in,nil)
|
35
|
+
self.error_name= opts.fetch(:error_name,nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate
|
39
|
+
self.validation_errors[:level]+=["is null"] unless self.level
|
40
|
+
self.validation_errors[:level]+=["is not in [0,1,2,3,4]"] unless (0..4).to_a.include?(self.level)
|
41
|
+
self.validation_errors[:error_type]+=["is not in (softfail hardfail)"] unless self.error_type && (%w(softfail hardfail).include? self.error_type)
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/lib/gorg_service/message.rb
CHANGED
@@ -8,11 +8,26 @@ require 'securerandom'
|
|
8
8
|
require "gorg_service/message/json_schema"
|
9
9
|
require "gorg_service/message/error_log"
|
10
10
|
|
11
|
+
require "gorg_service/reply_message"
|
12
|
+
require "gorg_service/event_message"
|
13
|
+
require "gorg_service/log_message"
|
14
|
+
require "gorg_service/request_message"
|
15
|
+
|
16
|
+
require "gorg_service/message/formatters"
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
# Message transferred through Bunny. Follow Gorg SOA specification v2.0 ans maintain support for v1.0
|
21
|
+
#
|
22
|
+
# @author Alexandre Narbonne
|
11
23
|
class GorgService
|
12
24
|
class Message
|
13
25
|
|
26
|
+
DEFAULT_SOA_VERSION="1.0"
|
27
|
+
|
14
28
|
class DataValidationError < StandardError
|
15
29
|
|
30
|
+
# @return [Hash] Mapping of invalid data
|
16
31
|
attr_reader :errors
|
17
32
|
|
18
33
|
def initialize(errors)
|
@@ -20,81 +35,165 @@ class GorgService
|
|
20
35
|
end
|
21
36
|
end
|
22
37
|
|
38
|
+
class MessageValidationError < StandardError
|
39
|
+
|
40
|
+
# @return [Hash] Mapping of invalid data
|
41
|
+
attr_reader :errors
|
42
|
+
|
43
|
+
def initialize(errors)
|
44
|
+
@errors=errors
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [String] UUID of the message
|
23
49
|
attr_accessor :id
|
24
|
-
|
25
|
-
|
50
|
+
|
51
|
+
# @return [String] Id of the app which generated the message
|
26
52
|
attr_accessor :sender_id
|
53
|
+
alias_method :sender, :sender_id
|
54
|
+
alias_method :sender=, :sender_id=
|
55
|
+
|
56
|
+
# @return [String] Content type of the payload
|
57
|
+
# @note The only content type officially supported by Gadz.org SOA is 'application/json'
|
27
58
|
attr_accessor :content_type
|
59
|
+
|
60
|
+
# @return [String] Content encoding of the payload
|
61
|
+
# @note The only content encoding officially supported by Gadz.org SOA is 'deflate'
|
28
62
|
attr_accessor :content_encoding
|
29
|
-
attr_accessor :headers
|
30
|
-
attr_accessor :type
|
31
63
|
|
32
|
-
|
64
|
+
# @return [String] The Gadz.org SOA specs version used to generate this message
|
65
|
+
# @note Incomming message without this information should be considered v1.0
|
66
|
+
attr_accessor :soa_version
|
67
|
+
|
68
|
+
# @return [String] Major version of the soa version
|
69
|
+
def soa_version_major
|
70
|
+
(self.soa_version||DEFAULT_SOA_VERSION).split('.')[0]
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [String] identifier of the library used to generate this message, for debug and compatibility purpose
|
74
|
+
attr_accessor :client_library
|
75
|
+
|
76
|
+
# @return [String] Identifier of the admin who triggered the generation of this message
|
77
|
+
# @note In case of autonomous actions, there is no admin_id
|
78
|
+
attr_accessor :admin_id
|
79
|
+
|
80
|
+
# @return [String] Routing of the message
|
33
81
|
attr_accessor :routing_key
|
34
|
-
|
82
|
+
|
83
|
+
# @return [Hash] Data of the payload as an hash
|
84
|
+
# @note for further compatibility, don't assume that data is an hash but check Content-Type
|
35
85
|
attr_accessor :data
|
36
|
-
|
86
|
+
|
87
|
+
# @return [DateTime] Message generation datetime
|
37
88
|
attr_accessor :creation_time
|
38
|
-
attr_accessor :sender
|
39
89
|
|
90
|
+
# @return [String] Name of the exchange used to receive reply
|
91
|
+
attr_accessor :reply_to
|
92
|
+
alias_method :reply_exchange, :reply_to
|
93
|
+
|
94
|
+
# @return [String] UUID of the message the this message refer to (reply or log)
|
95
|
+
attr_accessor :correlation_id
|
96
|
+
|
97
|
+
# @return [nil] Type of message (event,log,request,reply). To be overwritten by children classes
|
98
|
+
def type
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Hash] Additional headers
|
103
|
+
attr_accessor :headers
|
104
|
+
|
105
|
+
# @return [Integer] Number of softfails associated to this message
|
106
|
+
attr_accessor :softfail_count
|
107
|
+
|
108
|
+
# @deprecated In Gadz.org Soa v2.0 errors are not store in messages anymore
|
109
|
+
# @return [Array<Message::ErrorLog>] List of errors associated to this message
|
110
|
+
attr_accessor :errors
|
111
|
+
|
112
|
+
# @return [Hash] Mapping of attributes errors in regard of Gadz.org SOA v2
|
113
|
+
attr_accessor :validation_errors
|
114
|
+
def validation_errors
|
115
|
+
@validation_errors||= Hash.new([].freeze)
|
116
|
+
end
|
117
|
+
|
118
|
+
# @deprecated Use {#routing_key} instead. event is no longer a part of GorgSOA specs
|
119
|
+
# @return [String] the name of the event
|
120
|
+
attr_accessor :event
|
121
|
+
def event
|
122
|
+
warn "[DEPRECATION] Message.event is deprecated and will be removed soon. Use id instead (called from #{caller_locations(1,1)[0]})"
|
123
|
+
self.routing_key
|
124
|
+
end
|
125
|
+
def event=(value)
|
126
|
+
warn "[DEPRECATION] Message.event is deprecated and will be removed soon. Use id instead (called from #{caller_locations(1,1)[0]})"
|
127
|
+
self.routing_key=value
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
# @deprecated Use {#id} instead. event_id is no longer a part of GorgSOA specs
|
133
|
+
# @return [String] UUID of the message
|
134
|
+
attr_accessor :event_id
|
135
|
+
def event_id
|
136
|
+
warn "[DEPRECATION] Message.event_id is deprecated and will be removed soon. Use id instead (called from #{caller_locations(1,1)[0]})"
|
137
|
+
self.id
|
138
|
+
end
|
139
|
+
def event_id=(value)
|
140
|
+
warn "[DEPRECATION] Message.event_id is deprecated and will be removed soon. Use id instead (called from #{caller_locations(1,1)[0]})"
|
141
|
+
self.id=value
|
142
|
+
end
|
143
|
+
|
144
|
+
# @deprecated In Gadz.org Soa v2.0 errors are not store in messages anymore
|
145
|
+
# @return [Array<Message::ErrorLog>] List of errors associated to this message
|
40
146
|
def errors
|
41
147
|
@errors||=[]
|
42
148
|
end
|
43
149
|
|
44
|
-
|
150
|
+
# @param opts [Hash] Attributes of the message
|
151
|
+
# @option opts [String] :id See {#id}. Default : Random UUID4
|
152
|
+
# @option opts [Array<Message::ErrorLog>] :errors See {#errors}. Default : []
|
153
|
+
# @option opts [DateTime] :creation_time See {#creation_time}. Default : DateTime.now
|
154
|
+
# @option opts [String] :sender See {#sender}. Default : GorgService.configuration.application_id
|
155
|
+
# @option opts [String] :data See {#data}. Default : nil
|
156
|
+
# @option opts [String] :routing_key See {#routing_key}. Default : nil
|
157
|
+
# @option opts [String] :reply_to See {#reply_to}. Default : nil
|
158
|
+
# @option opts [String] :correlation_id See {#correlation_id}. Default : nil
|
159
|
+
# @option opts [String] :content_type See {#content_type}. Default : "application/json"
|
160
|
+
# @option opts [String] :content_encoding See {#content_encoding}. Default : "deflate"
|
161
|
+
# @option opts [String] :headers See {#headers}. Default : {}
|
45
162
|
def initialize(opts={})
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@content_encoding= opts.fetch(:content_encoding,"deflate")
|
62
|
-
@headers= opts.fetch(:headers,{})
|
63
|
-
@type= opts.fetch(:type,"event")
|
163
|
+
self.id= opts.fetch(:event_id,nil)||opts.fetch(:id,generate_id)
|
164
|
+
|
165
|
+
self.errors= opts.fetch(:errors,[])
|
166
|
+
self.creation_time= opts.fetch(:creation_time,DateTime.now)
|
167
|
+
self.sender= opts.fetch(:sender,application_id)
|
168
|
+
self.data= opts.fetch(:data,nil)
|
169
|
+
self.routing_key= opts.fetch(:routing_key,nil)||opts.fetch(:event,nil)
|
170
|
+
self.softfail_count= opts.fetch(:softfail_count,0)
|
171
|
+
|
172
|
+
self.reply_to= opts.fetch(:reply_to,nil)
|
173
|
+
self.correlation_id= opts.fetch(:correlation_id,nil)
|
174
|
+
self.content_type= opts.fetch(:content_type,"application/json")
|
175
|
+
self.content_encoding= opts.fetch(:content_encoding,"deflate")
|
176
|
+
self.headers= opts.fetch(:headers,{})
|
177
|
+
self.soa_version= opts.fetch(:soa_version, DEFAULT_SOA_VERSION)
|
64
178
|
end
|
65
179
|
|
180
|
+
# @deprecated Please use directly the rendering interface of the formatter
|
181
|
+
# @param formatter [Message::FormatterV1] The formatter to be used. Default : an instance of Message::FormatterV1
|
182
|
+
# @return [Hash] The un-serialized payload of the RabbitMq message
|
66
183
|
def body
|
67
|
-
|
68
|
-
event_uuid: @event_id,
|
69
|
-
event_name: @event,
|
70
|
-
event_sender_id: @sender,
|
71
|
-
event_creation_time: @creation_time,
|
72
|
-
data: @data,
|
73
|
-
}
|
74
|
-
if errors.any?
|
75
|
-
_body[:errors_count]=@errors.count
|
76
|
-
_body[:errors]=@errors.map{|e| e.to_h}
|
77
|
-
end
|
78
|
-
_body
|
184
|
+
Message::Formatter.formatter_for_version(self.soa_version).new(self).body
|
79
185
|
end
|
80
186
|
alias_method :to_h, :body
|
81
187
|
alias_method :payload, :body
|
82
188
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
app_id: sender_id,
|
92
|
-
type: type,
|
93
|
-
message_id: id,
|
94
|
-
}
|
95
|
-
end
|
96
|
-
|
97
|
-
# Generate RabbitMQ message body
|
189
|
+
# @deprecated Please use directly the rendering interface of the formatter
|
190
|
+
# @param formatter [Message::FormatterV1] The formatter to be used. Default : an instance of Message::FormatterV1
|
191
|
+
# @return [Hash] The properties of the RabbitMq message
|
192
|
+
def properties(formatter: Message::FormatterV1.new(self))
|
193
|
+
Message::Formatter.formatter_for_version(self.soa_version).new(self).properties
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [String] The serialized (JSON) value of the RabbitMQ payload
|
98
197
|
def to_json
|
99
198
|
self.to_h.to_json
|
100
199
|
end
|
@@ -109,22 +208,76 @@ class GorgService
|
|
109
208
|
errors<<e
|
110
209
|
end
|
111
210
|
|
112
|
-
|
113
|
-
reply_to
|
114
|
-
end
|
115
|
-
|
211
|
+
# @return [Boolean] Does this message expect a reply ?
|
116
212
|
def expect_reply?
|
117
213
|
!!reply_to
|
118
214
|
end
|
119
215
|
|
216
|
+
# @return [Stirng] Routing key to use to reply to this message
|
120
217
|
def reply_routing_key
|
121
|
-
|
218
|
+
routing_key.sub('request','reply')
|
219
|
+
end
|
220
|
+
|
221
|
+
# @return [GorgService::ReplyMessage] the response to send
|
222
|
+
|
223
|
+
def reply_message(opts)
|
224
|
+
args= {
|
225
|
+
routing_key: self.reply_routing_key,
|
226
|
+
correlation_id: self.id,
|
227
|
+
soa_version: self.soa_version
|
228
|
+
}.merge(opts)
|
229
|
+
|
230
|
+
GorgService::ReplyMessage.new(args)
|
231
|
+
end
|
232
|
+
|
233
|
+
def log_message(opts)
|
234
|
+
args= {
|
235
|
+
routing_key: self.reply_routing_key,
|
236
|
+
correlation_id: self.id,
|
237
|
+
soa_version: '2.0' #v1 messages generate v2 logs
|
238
|
+
}.merge(opts)
|
239
|
+
|
240
|
+
GorgService::LogMessage.new(args)
|
122
241
|
end
|
123
242
|
|
243
|
+
# Validate the message against rules specified in {https://confluence.gadz.org/display/INFRA/Messages the Gadz.org SOA Message Specification}
|
244
|
+
def validate
|
245
|
+
self.validation_errors= Hash.new([].freeze)
|
246
|
+
|
247
|
+
self.validation_errors[:content_type]+=["is not supported by Gadz.org SOA"] unless ['application/json'].include? self.content_type
|
248
|
+
self.validation_errors[:content_encoding]+=["is not supported by Gadz.org SOA"] unless ['deflate','gzip'].include? self.content_encoding
|
249
|
+
# "gzip" is not officially supported by Gadz.org but I don't feel comfortable blocking it :)
|
250
|
+
|
251
|
+
self.validation_errors[:id]+=["is null"] unless self.id
|
252
|
+
self.validation_errors[:id]+=["is not a UUID"] unless !self.id || /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.match(self.id)
|
253
|
+
|
254
|
+
self.validation_errors[:correlation_id]+=["is not a UUID"] unless !self.correlation_id || /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.match()
|
255
|
+
|
256
|
+
self.validation_errors[:creation_time]+=["is not DateTime"] unless self.creation_time.is_a? DateTime
|
257
|
+
|
258
|
+
self.validation_errors[:type]+=["is not in (event, log, request, reply)"] unless %w(event log request reply).include? self.type
|
259
|
+
|
260
|
+
self.validation_errors[:sender]+=["is null"] unless self.sender
|
261
|
+
|
262
|
+
self.validation_errors[:soa_version]+=["is null"] unless self.soa_version
|
263
|
+
|
264
|
+
return self.validation_errors.empty?
|
265
|
+
end
|
266
|
+
alias_method :valid?, :validate
|
267
|
+
|
268
|
+
# @see #validate
|
269
|
+
# @raise [MessageValidationError] Raise an exception containing the errors if the message is invalid
|
270
|
+
def validate!
|
271
|
+
raise MessageValidationError.new(self.errors) unless validate
|
272
|
+
end
|
273
|
+
|
274
|
+
# @param [String,Hash] A JSON Schema usable by {JSON::Validator}
|
275
|
+
# @return [Boolean] True if the message is valid against the provided JSON Schema
|
276
|
+
# @raise [DataValidationError] Validation exception containing errors ( See {DataValidationError#errors})
|
124
277
|
def validate_data_with(schema)
|
125
|
-
|
126
|
-
if
|
127
|
-
raise DataValidationError.new(
|
278
|
+
data_validation_errors=JSON::Validator.fully_validate(schema, self.data)
|
279
|
+
if data_validation_errors.any?
|
280
|
+
raise DataValidationError.new(data_validation_errors)
|
128
281
|
else
|
129
282
|
return true
|
130
283
|
end
|
@@ -134,54 +287,17 @@ class GorgService
|
|
134
287
|
|
135
288
|
### Class methods
|
136
289
|
|
290
|
+
# @param [Hash] delivery_info delivery_info provided by {Bunny}
|
291
|
+
# @param [Hash] properties properties provided by {Bunny}
|
292
|
+
# @param [Hash] body body provided by {Bunny}
|
293
|
+
# @param [Class] formatter_class The formatter to be used to parse the message. Default to {Message::FormatterV1}
|
294
|
+
# @return [GorgService::Message] A kind of GorgService::Message
|
137
295
|
def self.parse(delivery_info, properties, body)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
JSON::Validator.validate!(GorgService::Message::JSON_SCHEMA,json_body)
|
142
|
-
|
143
|
-
msg=self.new(
|
144
|
-
routing_key: delivery_info[:routing_key],
|
145
|
-
id: properties[:message_id],
|
146
|
-
reply_to: properties[:reply_to],
|
147
|
-
correlation_id: properties[:correlation_id],
|
148
|
-
sender_id: properties[:app_id],
|
149
|
-
content_type: properties[:content_type],
|
150
|
-
content_encoding: properties[:content_encoding],
|
151
|
-
headers: properties[:header],
|
152
|
-
type: properties[:type],
|
153
|
-
|
154
|
-
event_id: json_body["event_uuid"],
|
155
|
-
event: json_body["event_name"],
|
156
|
-
data: convert_keys_to_sym(json_body["data"]),
|
157
|
-
creation_time: json_body["event_creation_time"] && DateTime.parse(json_body["event_creation_time"]),
|
158
|
-
sender: json_body["event_sender_id"],
|
159
|
-
errors: json_body["errors"]&&json_body["errors"].map{|e| GorgService::Message::ErrorLog.parse(e)},
|
160
|
-
)
|
161
|
-
msg
|
162
|
-
rescue JSON::ParserError => e
|
163
|
-
raise GorgService::Consumer::HardfailError.new("Unprocessable message : Unable to parse JSON message body", e)
|
164
|
-
rescue JSON::Schema::ValidationError => e
|
165
|
-
raise GorgService::Consumer::HardfailError.new("Invalid JSON : This message does not respect Gadz.org JSON Schema",e)
|
166
|
-
end
|
296
|
+
formatter_class=Message::Formatter.formatter_for_version(properties.to_h[:headers].to_h["soa-version"]||"1")
|
297
|
+
formatter_class.parse(delivery_info, properties, body)
|
167
298
|
end
|
168
299
|
|
169
|
-
|
170
|
-
def self.convert_keys_to_sym input_hash
|
171
|
-
s2s =
|
172
|
-
lambda do |h|
|
173
|
-
Hash === h ?
|
174
|
-
Hash[
|
175
|
-
h.map do |k, v|
|
176
|
-
[k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]]
|
177
|
-
end
|
178
|
-
] : h
|
179
|
-
end
|
180
|
-
s2s[input_hash]
|
181
|
-
end
|
182
|
-
|
183
|
-
private
|
184
|
-
|
300
|
+
protected
|
185
301
|
# Generate new id
|
186
302
|
def generate_id
|
187
303
|
SecureRandom.uuid()
|
@@ -0,0 +1,313 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class GorgService::Message
|
5
|
+
class Formatter
|
6
|
+
|
7
|
+
def initialize(message)
|
8
|
+
@message=message
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
@message
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.formatter_for_version(version)
|
16
|
+
major_version=version.split('.')[0]
|
17
|
+
case major_version
|
18
|
+
when '1'
|
19
|
+
FormatterV1
|
20
|
+
when '2'
|
21
|
+
FormatterV2
|
22
|
+
else
|
23
|
+
raise "Unknown Gorg SOA version"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.convert_keys_to_sym input_hash
|
28
|
+
s2s =
|
29
|
+
lambda do |h|
|
30
|
+
Hash === h ?
|
31
|
+
Hash[
|
32
|
+
h.map do |k, v|
|
33
|
+
[k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]]
|
34
|
+
end
|
35
|
+
] : h
|
36
|
+
end
|
37
|
+
s2s[input_hash]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class FormatterV1 < Formatter
|
43
|
+
|
44
|
+
JSON_SCHEMA_V1 = JSON.parse('{
|
45
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
46
|
+
"type": "object",
|
47
|
+
"properties": {
|
48
|
+
"event_name": {
|
49
|
+
"type": "string",
|
50
|
+
"pattern": "^[_a-z]+((\\.)?[_a-z]+)*$",
|
51
|
+
"description": "Event type. Must match the routing key"
|
52
|
+
},
|
53
|
+
"event_uuid": {
|
54
|
+
"type": "string",
|
55
|
+
"description": "The unique identifier of this message as UUID",
|
56
|
+
"pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
57
|
+
},
|
58
|
+
"event_creation_time": {
|
59
|
+
"type": "string",
|
60
|
+
"description": "Creation time in UTC ISO 8601 format",
|
61
|
+
"pattern": "^([\\\+-]?\\\d{4}(?!\\\d{2}\\\b))((-?)((0[1-9]|1[0-2])(\\\3([12]\\\d|0[1-9]|3[01]))?|W([0-4]\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\d|[12]\\\d{2}|3([0-5]\\\d|6[1-6])))([T\\\s]((([01]\\\d|2[0-3])((:?)[0-5]\\\d)?|24\\\:?00)([\\\.,]\\\d+(?!:))?)?(\\\17[0-5]\\\d([\\\.,]\\\d+)?)?([zZ]|([\\\+-])([01]\\\d|2[0-3]):?([0-5]\\\d)?)?)?)?$"
|
62
|
+
},
|
63
|
+
"event_sender_id": {
|
64
|
+
"type": "string",
|
65
|
+
"description": "Producer that sent the original message"
|
66
|
+
},
|
67
|
+
"data": {
|
68
|
+
"type": "object",
|
69
|
+
"description": "Data used to process this message"
|
70
|
+
},
|
71
|
+
"errors_count": {
|
72
|
+
"type": "integer",
|
73
|
+
"description": "Helper for counting errors"
|
74
|
+
},
|
75
|
+
"errors": {
|
76
|
+
"type": "array",
|
77
|
+
"items": {
|
78
|
+
"type": "object",
|
79
|
+
"properties": {
|
80
|
+
"error_type": {
|
81
|
+
"enum": [ "debug", "info", "warning", "softerror", "harderror" ],
|
82
|
+
"description": "Type of error."
|
83
|
+
},
|
84
|
+
"error_sender": {
|
85
|
+
"type": "string",
|
86
|
+
"description": "Consummer that sent this error"
|
87
|
+
},
|
88
|
+
"error_code":{
|
89
|
+
"type":"string",
|
90
|
+
"description": "Optionnal error code from the consummer"
|
91
|
+
},
|
92
|
+
"error_uuid":{
|
93
|
+
"type":"string",
|
94
|
+
"description": "The unique identifier of this error as UUID",
|
95
|
+
"pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
96
|
+
},
|
97
|
+
"error_message":{
|
98
|
+
"type":"string",
|
99
|
+
"description": "Error explanation"
|
100
|
+
},
|
101
|
+
"timestamp": {
|
102
|
+
"type": "string",
|
103
|
+
"description": "Time of occuring error in UTC ISO 8601",
|
104
|
+
"pattern": "^([\\\+-]?\\\d{4}(?!\\\d{2}\\\b))((-?)((0[1-9]|1[0-2])(\\\3([12]\\\d|0[1-9]|3[01]))?|W([0-4]\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\d|[12]\\\d{2}|3([0-5]\\\d|6[1-6])))([T\\\s]((([01]\\\d|2[0-3])((:?)[0-5]\\\d)?|24\\\:?00)([\\\.,]\\\d+(?!:))?)?(\\\17[0-5]\\\d([\\\.,]\\\d+)?)?([zZ]|([\\\+-])([01]\\\d|2[0-3]):?([0-5]\\\d)?)?)?)?$"
|
105
|
+
},
|
106
|
+
"error_debug": {
|
107
|
+
"type": "object",
|
108
|
+
"description": "Complementary informations for debugging"
|
109
|
+
}
|
110
|
+
},
|
111
|
+
"additionalProperties": false,
|
112
|
+
"required": [
|
113
|
+
"error_type",
|
114
|
+
"error_sender",
|
115
|
+
"timestamp",
|
116
|
+
"error_uuid",
|
117
|
+
"error_message"
|
118
|
+
]
|
119
|
+
}
|
120
|
+
}
|
121
|
+
},
|
122
|
+
"additionalProperties": false,
|
123
|
+
"required": [
|
124
|
+
"event_name",
|
125
|
+
"event_uuid",
|
126
|
+
"event_creation_time",
|
127
|
+
"event_sender_id",
|
128
|
+
"data"
|
129
|
+
]
|
130
|
+
}')
|
131
|
+
|
132
|
+
DEFAULT_HEADERS={
|
133
|
+
"soa-version" => "1.0",
|
134
|
+
"client-library" => "GorgService #{GorgService::VERSION}",
|
135
|
+
}
|
136
|
+
|
137
|
+
def properties
|
138
|
+
{
|
139
|
+
routing_key: message.routing_key,
|
140
|
+
reply_to: message.reply_to,
|
141
|
+
correlation_id: message.correlation_id,
|
142
|
+
content_type: message.content_type,
|
143
|
+
content_encoding: message.content_encoding,
|
144
|
+
headers: DEFAULT_HEADERS.merge(message.headers.to_h).merge(
|
145
|
+
'softfail-count' => message.softfail_count,
|
146
|
+
),
|
147
|
+
app_id: message.sender_id,
|
148
|
+
type: message.type,
|
149
|
+
message_id: message.id,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def body
|
154
|
+
b={
|
155
|
+
event_uuid: message.id,
|
156
|
+
event_name: message.routing_key,
|
157
|
+
event_sender_id: message.sender,
|
158
|
+
event_creation_time: message.creation_time.iso8601,
|
159
|
+
data: message.data,
|
160
|
+
}
|
161
|
+
if message.errors.any?
|
162
|
+
b[:errors_count]=message.errors.count
|
163
|
+
b[:errors]=message.errors.map{|e| e.to_h}
|
164
|
+
end
|
165
|
+
b
|
166
|
+
end
|
167
|
+
|
168
|
+
def payload
|
169
|
+
body.to_json
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.parse(delivery_info, properties, body)
|
173
|
+
begin
|
174
|
+
json_body=JSON.parse(body)
|
175
|
+
JSON::Validator.validate!(JSON_SCHEMA_V1, json_body)
|
176
|
+
|
177
|
+
msg=GorgService::Message.new(
|
178
|
+
routing_key: delivery_info[:routing_key],
|
179
|
+
reply_to: properties[:reply_to],
|
180
|
+
correlation_id: properties[:correlation_id],
|
181
|
+
sender_id: properties[:app_id],
|
182
|
+
content_type: properties[:content_type],
|
183
|
+
content_encoding: properties[:content_encoding],
|
184
|
+
headers: properties[:headers],
|
185
|
+
type: properties[:type],
|
186
|
+
|
187
|
+
softfail_count: properties[:headers].to_h.delete('softfail-count'),
|
188
|
+
|
189
|
+
id: json_body["event_uuid"],
|
190
|
+
event_id: json_body["event_uuid"],
|
191
|
+
event: json_body["event_name"],
|
192
|
+
data: convert_keys_to_sym(json_body["data"]),
|
193
|
+
creation_time: json_body["event_creation_time"] && DateTime.parse(json_body["event_creation_time"]),
|
194
|
+
sender: json_body["event_sender_id"],
|
195
|
+
errors: json_body["errors"]&&json_body["errors"].map{|e| GorgService::Message::ErrorLog.parse(e)},
|
196
|
+
)
|
197
|
+
msg
|
198
|
+
rescue JSON::ParserError => e
|
199
|
+
raise GorgService::Consumer::HardfailError.new("Unprocessable message : Unable to parse JSON message body", e)
|
200
|
+
rescue JSON::Schema::ValidationError => e
|
201
|
+
raise GorgService::Consumer::HardfailError.new("Invalid JSON : This message does not respect Gadz.org JSON Schema",e,{})
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
class FormatterV2 < Formatter
|
208
|
+
|
209
|
+
DEFAULT_HEADERS={
|
210
|
+
"soa-version" => "2.0",
|
211
|
+
"client-library" => "GorgService #{GorgService::VERSION}",
|
212
|
+
}
|
213
|
+
|
214
|
+
EXTRA_HEADERS_FOR ={
|
215
|
+
GorgService::ReplyMessage => {
|
216
|
+
'status-code' => :status_code,
|
217
|
+
'error-type' => :error_type,
|
218
|
+
'next-try-in' => :next_try_in,
|
219
|
+
'error-name' => :error_name,
|
220
|
+
},
|
221
|
+
GorgService::LogMessage => {
|
222
|
+
'level' => :level,
|
223
|
+
'error-type' => :error_type,
|
224
|
+
'next-try-in' => :next_try_in,
|
225
|
+
'error-name' => :error_name,
|
226
|
+
},
|
227
|
+
|
228
|
+
}
|
229
|
+
|
230
|
+
def properties
|
231
|
+
|
232
|
+
headers=DEFAULT_HEADERS.merge(message.headers.to_h).merge(
|
233
|
+
'softfail-count' => message.softfail_count,
|
234
|
+
)
|
235
|
+
|
236
|
+
extra_headers=EXTRA_HEADERS_FOR[message.class].to_h.map do|key,method_name|
|
237
|
+
[key,message.public_send(method_name)]
|
238
|
+
end.to_h
|
239
|
+
|
240
|
+
|
241
|
+
headers.merge!(extra_headers)
|
242
|
+
|
243
|
+
{
|
244
|
+
routing_key: message.routing_key,
|
245
|
+
reply_to: message.reply_to,
|
246
|
+
correlation_id: message.correlation_id,
|
247
|
+
content_type: message.content_type,
|
248
|
+
content_encoding: message.content_encoding,
|
249
|
+
headers: headers,
|
250
|
+
app_id: message.sender_id,
|
251
|
+
type: message.type,
|
252
|
+
message_id: message.id,
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
def body
|
257
|
+
message.data
|
258
|
+
end
|
259
|
+
|
260
|
+
def payload
|
261
|
+
body.to_json
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.parse(delivery_info, properties, body)
|
265
|
+
begin
|
266
|
+
|
267
|
+
type=properties[:type]
|
268
|
+
unless type
|
269
|
+
type=delivery_info[:routing_key].split('.').first
|
270
|
+
end
|
271
|
+
|
272
|
+
type_map={event: GorgService::EventMessage, request: GorgService::RequestMessage, reply: GorgService::ReplyMessage, log: GorgService::LogMessage}
|
273
|
+
klass=type_map[type.to_s.to_sym]
|
274
|
+
|
275
|
+
raise "Unknown type" unless klass
|
276
|
+
|
277
|
+
headers=properties[:headers]||{}
|
278
|
+
|
279
|
+
args={
|
280
|
+
data: convert_keys_to_sym(JSON.parse(body)),
|
281
|
+
id:properties[:message_id],
|
282
|
+
|
283
|
+
creation_time: properties[:timestamp] && DateTime.parse(properties[:timestamp]),
|
284
|
+
sender:properties[:app_id],
|
285
|
+
routing_key: delivery_info[:routing_key],
|
286
|
+
|
287
|
+
reply_to: properties[:reply_to],
|
288
|
+
correlation_id: properties[:correlation_id],
|
289
|
+
content_type: properties[:content_type],
|
290
|
+
content_encoding:properties[:content_encoding],
|
291
|
+
soa_version: headers.delete('soa-version'),
|
292
|
+
|
293
|
+
softfail_count: headers.delete('softfail-count'),
|
294
|
+
|
295
|
+
headers: headers,
|
296
|
+
}
|
297
|
+
|
298
|
+
extra_args=EXTRA_HEADERS_FOR[klass].to_h.map do |key,method_name|
|
299
|
+
[method_name,headers.delete(key)]
|
300
|
+
end.to_h
|
301
|
+
|
302
|
+
args.merge!(extra_args)
|
303
|
+
|
304
|
+
klass.new(args)
|
305
|
+
rescue JSON::ParserError => e
|
306
|
+
raise GorgService::Consumer::HardfailError.new("Unprocessable message : Unable to parse JSON message body", e)
|
307
|
+
rescue JSON::Schema::ValidationError => e
|
308
|
+
raise GorgService::Consumer::HardfailError.new("Invalid JSON : This message does not respect Gadz.org JSON Schema",e)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
end
|
@@ -6,93 +6,7 @@ require 'json'
|
|
6
6
|
class GorgService
|
7
7
|
class Message
|
8
8
|
|
9
|
-
|
10
|
-
"$schema": "http://json-schema.org/draft-04/schema#",
|
11
|
-
"type": "object",
|
12
|
-
"properties": {
|
13
|
-
"event_name": {
|
14
|
-
"type": "string",
|
15
|
-
"pattern": "^[_a-z]+((\\.)?[_a-z]+)*$",
|
16
|
-
"description": "Event type. Must match the routing key"
|
17
|
-
},
|
18
|
-
"event_uuid": {
|
19
|
-
"type": "string",
|
20
|
-
"description": "The unique identifier of this message as UUID",
|
21
|
-
"pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
22
|
-
},
|
23
|
-
"event_creation_time": {
|
24
|
-
"type": "string",
|
25
|
-
"description": "Creation time in UTC ISO 8601 format",
|
26
|
-
"pattern": "^([\\\+-]?\\\d{4}(?!\\\d{2}\\\b))((-?)((0[1-9]|1[0-2])(\\\3([12]\\\d|0[1-9]|3[01]))?|W([0-4]\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\d|[12]\\\d{2}|3([0-5]\\\d|6[1-6])))([T\\\s]((([01]\\\d|2[0-3])((:?)[0-5]\\\d)?|24\\\:?00)([\\\.,]\\\d+(?!:))?)?(\\\17[0-5]\\\d([\\\.,]\\\d+)?)?([zZ]|([\\\+-])([01]\\\d|2[0-3]):?([0-5]\\\d)?)?)?)?$"
|
27
|
-
},
|
28
|
-
"event_sender_id": {
|
29
|
-
"type": "string",
|
30
|
-
"description": "Producer that sent the original message"
|
31
|
-
},
|
32
|
-
"data": {
|
33
|
-
"type": "object",
|
34
|
-
"description": "Data used to process this message"
|
35
|
-
},
|
36
|
-
"errors_count": {
|
37
|
-
"type": "integer",
|
38
|
-
"description": "Helper for counting errors"
|
39
|
-
},
|
40
|
-
"errors": {
|
41
|
-
"type": "array",
|
42
|
-
"items": {
|
43
|
-
"type": "object",
|
44
|
-
"properties": {
|
45
|
-
"error_type": {
|
46
|
-
"enum": [ "debug", "info", "warning", "softerror", "harderror" ],
|
47
|
-
"description": "Type of error."
|
48
|
-
},
|
49
|
-
"error_sender": {
|
50
|
-
"type": "string",
|
51
|
-
"description": "Consummer that sent this error"
|
52
|
-
},
|
53
|
-
"error_code":{
|
54
|
-
"type":"string",
|
55
|
-
"description": "Optionnal error code from the consummer"
|
56
|
-
},
|
57
|
-
"error_uuid":{
|
58
|
-
"type":"string",
|
59
|
-
"description": "The unique identifier of this error as UUID",
|
60
|
-
"pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
61
|
-
},
|
62
|
-
"error_message":{
|
63
|
-
"type":"string",
|
64
|
-
"description": "Error explanation"
|
65
|
-
},
|
66
|
-
"timestamp": {
|
67
|
-
"type": "string",
|
68
|
-
"description": "Time of occuring error in UTC ISO 8601",
|
69
|
-
"pattern": "^([\\\+-]?\\\d{4}(?!\\\d{2}\\\b))((-?)((0[1-9]|1[0-2])(\\\3([12]\\\d|0[1-9]|3[01]))?|W([0-4]\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\d|[12]\\\d{2}|3([0-5]\\\d|6[1-6])))([T\\\s]((([01]\\\d|2[0-3])((:?)[0-5]\\\d)?|24\\\:?00)([\\\.,]\\\d+(?!:))?)?(\\\17[0-5]\\\d([\\\.,]\\\d+)?)?([zZ]|([\\\+-])([01]\\\d|2[0-3]):?([0-5]\\\d)?)?)?)?$"
|
70
|
-
},
|
71
|
-
"error_debug": {
|
72
|
-
"type": "object",
|
73
|
-
"description": "Complementary informations for debugging"
|
74
|
-
}
|
75
|
-
},
|
76
|
-
"additionalProperties": false,
|
77
|
-
"required": [
|
78
|
-
"error_type",
|
79
|
-
"error_sender",
|
80
|
-
"timestamp",
|
81
|
-
"error_uuid",
|
82
|
-
"error_message"
|
83
|
-
]
|
84
|
-
}
|
85
|
-
}
|
86
|
-
},
|
87
|
-
"additionalProperties": false,
|
88
|
-
"required": [
|
89
|
-
"event_name",
|
90
|
-
"event_uuid",
|
91
|
-
"event_creation_time",
|
92
|
-
"event_sender_id",
|
93
|
-
"data"
|
94
|
-
]
|
95
|
-
}')
|
9
|
+
|
96
10
|
|
97
11
|
end
|
98
12
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class GorgService
|
2
|
+
class ReplyMessage<Message
|
3
|
+
|
4
|
+
# @return [String] Error code, HTTP like
|
5
|
+
attr_accessor :status_code
|
6
|
+
|
7
|
+
# @return [String] Error type ('hardfail','softfail')
|
8
|
+
attr_accessor :error_type
|
9
|
+
|
10
|
+
# @return [Integer] Time until next attempts in milliseconds
|
11
|
+
attr_accessor :next_try_in
|
12
|
+
|
13
|
+
# @return [String] Name identifying the error
|
14
|
+
attr_accessor :error_name
|
15
|
+
|
16
|
+
def type
|
17
|
+
"reply"
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param opts [Hash] Attributes of the message
|
21
|
+
# @option opts [String] :status_code See {#status_code}. Default : nil
|
22
|
+
# @option opts [String] :error_type See {#error_type}. Default : nil
|
23
|
+
# @option opts [Integer] :next_try_in See {#next_try_in}. Default : nil
|
24
|
+
# @option opts [String] :error_name See {#error_name}. Default : nil
|
25
|
+
# @see GorgService::Message#initialize
|
26
|
+
def initialize(opts={})
|
27
|
+
super
|
28
|
+
self.status_code= opts.fetch(:status_code,nil)
|
29
|
+
self.error_type= opts.fetch(:error_type,nil)
|
30
|
+
self.next_try_in= opts.fetch(:next_try_in,nil)
|
31
|
+
self.error_name= opts.fetch(:error_name,nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate
|
35
|
+
self.validation_errors[:status_code]+=["is null"] unless self.status_code
|
36
|
+
self.validation_errors[:error_type]+=["is not in (softfail hardfail)"] unless self.error_type && (%w(softfail hardfail).include? self.error_type)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -9,15 +9,15 @@ class LogMessageHandler < GorgService::Consumer::MessageHandler::Base
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.has_received_error?(type)
|
12
|
-
messages.any?{|m| m.errors.any?{|x| x.type==type}}
|
12
|
+
messages.any?{|m| m.errors.any?{|x| x.type==type}} || messages.any?{|m| m.type=='log'&&m.error_type=type}
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.has_received_hardfail?
|
16
|
-
self.has_received_error?("
|
16
|
+
self.has_received_error?("hardfail")
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.
|
20
|
-
self.has_received_error?("
|
19
|
+
def self.has_received_softfail?
|
20
|
+
self.has_received_error?("softfail")
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.has_received_a_message_with_routing_key?(routing_key)
|
data/lib/gorg_service/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorg_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandre Narbonne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -86,6 +86,20 @@ dependencies:
|
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '10.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: yard
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 0.8.7
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.8.7
|
89
103
|
- !ruby/object:Gem::Dependency
|
90
104
|
name: rspec
|
91
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,14 +120,28 @@ dependencies:
|
|
106
120
|
requirements:
|
107
121
|
- - "~>"
|
108
122
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
123
|
+
version: '1.0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '1.0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: simplecov
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0.13'
|
110
138
|
type: :development
|
111
139
|
prerelease: false
|
112
140
|
version_requirements: !ruby/object:Gem::Requirement
|
113
141
|
requirements:
|
114
142
|
- - "~>"
|
115
143
|
- !ruby/object:Gem::Version
|
116
|
-
version: 0.
|
144
|
+
version: '0.13'
|
117
145
|
- !ruby/object:Gem::Dependency
|
118
146
|
name: bogus
|
119
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,11 +201,16 @@ files:
|
|
173
201
|
- lib/gorg_service/consumer/message_handler/reply_handler.rb
|
174
202
|
- lib/gorg_service/consumer/message_handler/request_handler.rb
|
175
203
|
- lib/gorg_service/consumer/message_router.rb
|
204
|
+
- lib/gorg_service/event_message.rb
|
205
|
+
- lib/gorg_service/log_message.rb
|
176
206
|
- lib/gorg_service/message.rb
|
177
207
|
- lib/gorg_service/message/error_log.rb
|
208
|
+
- lib/gorg_service/message/formatters.rb
|
178
209
|
- lib/gorg_service/message/json_schema.rb
|
179
210
|
- lib/gorg_service/producer.rb
|
180
211
|
- lib/gorg_service/rabbitmq_env_builder.rb
|
212
|
+
- lib/gorg_service/reply_message.rb
|
213
|
+
- lib/gorg_service/request_message.rb
|
181
214
|
- lib/gorg_service/rspec/bunny_cleaner.rb
|
182
215
|
- lib/gorg_service/rspec/log_message_handler.rb
|
183
216
|
- lib/gorg_service/version.rb
|