twiglet 3.0.5 → 3.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44c51be445d04ae84c5289cbeaa7508b9d3f733ce7b8d5001c7925bd0ed44dd4
4
- data.tar.gz: 2f04af22bf6c01fd12c8863e3c9350791d0d6c1ffaaf920797f9430e8e9d1e74
3
+ metadata.gz: 5aa4993d40d0b1f658c6b5f8c79f364e4bc1fef111d094b48446bde0fe7a0701
4
+ data.tar.gz: 8c6dee85c7622b274d570eb400bb8ca065c70e311a11699a990bc5548a2479d7
5
5
  SHA512:
6
- metadata.gz: 602d459a46ecb262457ba2b4cd3cb3900c3cdc77b3663087717018911bb61ef921e0892b7a24749182264e1e1ee970b9c92c72663ef0ed587a408de3863f8140
7
- data.tar.gz: 342cc5eebdc1054be2ae04c434218eeb93902328bfbed79cf66df4d3445a6604206084d88aae4a5998656138e3edec4eb37421079c3d8a78a6a27f822f399860
6
+ metadata.gz: 863b7bb7f3848a2345ffe5c8639add2f77fa7849d4f29d873bb25eb97c3510a2edec4c78cc6a95b343c1ee305571ca43534f88b494d983e0f00b23e7dd5ae5d5
7
+ data.tar.gz: 5827e6d6eee5fb90ca06822f7d5df2ac757a00f5d281453f74c3f3f3efcfee603639003eb711ccb026738ff9cd35e54405fc2ee125eb44b13f413f69f365f4c7
@@ -0,0 +1,16 @@
1
+ name: "dobby action"
2
+ on:
3
+ issue_comment:
4
+ types: [created]
5
+ jobs:
6
+ pr_commented:
7
+ runs-on: ubuntu-20.04
8
+ if: startsWith(github.event.comment.body, '/dobby')
9
+
10
+ steps:
11
+ - name: bump version
12
+ uses: simplybusiness/dobby@v2.0.0
13
+ env:
14
+ DOBBY_APP_ID: ${{ secrets.DOBBY_APP_ID }}
15
+ DOBBY_PRIVATE_KEY: ${{ secrets.DOBBY_PRIVATE_KEY }}
16
+ VERSION_FILE_PATH: lib/twiglet/version.rb
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
+
5
+ gem 'json-schema'
6
+
4
7
  group :development, :test do
5
8
  gem 'minitest'
6
9
  gem 'rake'
data/README.md CHANGED
@@ -110,7 +110,7 @@ This writes:
110
110
  It may be that when making a series of logs that write information about a single event, you may want to avoid duplication by creating an event specific logger that includes the context:
111
111
 
112
112
  ```ruby
113
- request_log = logger.with({ event: { action: 'HTTP request'}, trace: { id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb' }})
113
+ request_logger = logger.with({ event: { action: 'HTTP request'}, trace: { id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb' }})
114
114
  ```
115
115
 
116
116
  This can be used like any other Logger instance:
@@ -140,6 +140,10 @@ To access the formatter:
140
140
  logger.formatter
141
141
  ```
142
142
 
143
+ ### HTTP Request Logging
144
+ Take a look at this sample [Rack application](examples/rack/example_rack_app.rb#L15) with an ECS compliant
145
+ [request logger](/examples/rack/request_logger.rb) as a template when configuring your own request logging middleware with Twiglet.
146
+
143
147
  ## Use of dotted keys (DEPRECATED)
144
148
 
145
149
  Writing nested json objects could be confusing. This library has a built-in feature to convert dotted keys into nested objects, so if you log like this:
data/example_app.rb CHANGED
@@ -7,28 +7,32 @@ PORT = 8080
7
7
  logger = Twiglet::Logger.new('petshop')
8
8
 
9
9
  # Start our petshop
10
- logger.info({
11
- event: {
12
- action: 'startup'
13
- },
14
- message: "Ready to go, listening on port #{PORT}",
15
- server: {
16
- port: PORT
10
+ logger.info(
11
+ {
12
+ event: {
13
+ action: 'startup'
14
+ },
15
+ message: "Ready to go, listening on port #{PORT}",
16
+ server: {
17
+ port: PORT
18
+ }
17
19
  }
18
- })
20
+ )
19
21
 
20
22
  # Use text logging
21
23
  logger.info("Ready to go, listening on port #{PORT}")
22
24
  #
23
25
  # We get a request
24
- request_logger = logger.with({
25
- event: {
26
- action: 'HTTP request'
27
- },
28
- trace: {
29
- id: '126bb6fa-28a2-470f-b013-eefbf9182b2d'
26
+ request_logger = logger.with(
27
+ {
28
+ event: {
29
+ action: 'HTTP request'
30
+ },
31
+ trace: {
32
+ id: '126bb6fa-28a2-470f-b013-eefbf9182b2d'
33
+ }
30
34
  }
31
- })
35
+ )
32
36
 
33
37
  # Oh noes!
34
38
  db_err = StandardError.new('Connection timed-out')
@@ -36,17 +40,19 @@ db_err = StandardError.new('Connection timed-out')
36
40
  request_logger.error({ message: 'DB connection failed.' }, db_err) if db_err
37
41
 
38
42
  # We return an error to the requester
39
- request_logger.info({
40
- message: 'Internal Server Error',
41
- http: {
42
- request: {
43
- method: 'get'
44
- },
45
- response: {
46
- status_code: 500
43
+ request_logger.info(
44
+ {
45
+ message: 'Internal Server Error',
46
+ http: {
47
+ request: {
48
+ method: 'get'
49
+ },
50
+ response: {
51
+ status_code: 500
52
+ }
47
53
  }
48
54
  }
49
- })
55
+ )
50
56
 
51
57
  # Logging with an empty message is an anti-pattern and is therefore forbidden
52
58
  # Both of the following lines would throw an error
@@ -15,10 +15,12 @@ describe RequestLogger do
15
15
  end
16
16
 
17
17
  it 'logs the request data' do
18
- request.get("/some/path?some_var=1", 'HTTP_ACCEPT' => 'application/json',
19
- 'REMOTE_ADDR' => '0.0.0.0',
20
- 'HTTP_VERSION' => 'HTTP/1.1',
21
- 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh)')
18
+ request.get(
19
+ "/some/path?some_var=1", 'HTTP_ACCEPT' => 'application/json',
20
+ 'REMOTE_ADDR' => '0.0.0.0',
21
+ 'HTTP_VERSION' => 'HTTP/1.1',
22
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh)'
23
+ )
22
24
  log = JSON.parse(output.string)
23
25
 
24
26
  expected_log = {
@@ -55,7 +57,7 @@ describe RequestLogger do
55
57
  end
56
58
 
57
59
  it 'does not log PII' do
58
- request.post("/user/info", input_data: {credit_card_no: '1234'})
60
+ request.post("/user/info", input_data: { credit_card_no: '1234' })
59
61
  log = output.string
60
62
  assert_includes log, "POST: /user/info"
61
63
  refute_includes log, 'credit_card_no'
@@ -1,24 +1,28 @@
1
1
  require 'logger'
2
2
  require_relative '../hash_extensions'
3
3
  require_relative 'message'
4
+ require_relative 'validator'
4
5
 
5
6
  module Twiglet
6
7
  class Formatter < ::Logger::Formatter
7
8
  Hash.include HashExtensions
8
9
 
9
10
  def initialize(service_name,
10
- default_properties: {},
11
+ validator:, default_properties: {},
11
12
  now: -> { Time.now.utc })
12
13
  @service_name = service_name
13
14
  @now = now
14
15
  @default_properties = default_properties
16
+ @validator = validator
15
17
 
16
18
  super()
17
19
  end
18
20
 
19
21
  def call(severity, _time, _progname, msg)
20
22
  level = severity.downcase
21
- log(level: level, message: Message.new(msg))
23
+ message = Message.new(msg)
24
+ @validator.validate(message)
25
+ log(level: level, message: message)
22
26
  end
23
27
 
24
28
  private
@@ -26,16 +26,27 @@ module Twiglet
26
26
  raise 'Service name is mandatory' \
27
27
  unless service_name.is_a?(String) && !service_name.strip.empty?
28
28
 
29
- formatter = Twiglet::Formatter.new(service_name, default_properties: default_properties, now: now)
30
- super(output, formatter: formatter, level: level)
29
+ @validator = Validator.from_file("#{__dir__}/validation_schema.json")
30
+
31
+ @formatter = Twiglet::Formatter.new(
32
+ service_name,
33
+ default_properties: default_properties,
34
+ now: now,
35
+ validator: @validator
36
+ )
37
+ super(output, formatter: @formatter, level: level)
38
+ end
39
+
40
+ def configure_validation_error_response(&block)
41
+ @validator.custom_error_handler = block
31
42
  end
32
43
 
33
44
  def error(message = nil, error = nil, &block)
34
45
  if error
35
46
  error_fields = {
36
- 'error': {
37
- 'type': error.class,
38
- 'message': error.message
47
+ error: {
48
+ type: error.class,
49
+ message: error.message
39
50
  }
40
51
  }
41
52
  add_stack_trace(error_fields, error)
@@ -46,11 +57,13 @@ module Twiglet
46
57
  end
47
58
 
48
59
  def with(default_properties)
49
- Logger.new(@service_name,
50
- default_properties: default_properties,
51
- now: @now,
52
- output: @output,
53
- level: @level)
60
+ Logger.new(
61
+ @service_name,
62
+ default_properties: default_properties,
63
+ now: @now,
64
+ output: @output,
65
+ level: @level
66
+ )
54
67
  end
55
68
 
56
69
  alias_method :warning, :warn
@@ -1,24 +1,15 @@
1
1
  module Twiglet
2
2
  class Message < Hash
3
3
  def initialize(msg)
4
+ super
4
5
  case msg
5
6
  when String
6
7
  self[:message] = msg
7
8
  when Hash
8
9
  replace(msg.transform_keys!(&:to_sym))
10
+ else
11
+ super(msg)
9
12
  end
10
-
11
- validate!
12
- end
13
-
14
- private
15
-
16
- def validate!
17
- raise 'Message must be initialized with a String or a non-empty Hash' if empty?
18
-
19
- raise 'Log object must have a \'message\' property' unless self[:message]
20
-
21
- raise 'The \'message\' property of the log object must not be empty' if self[:message].strip.empty?
22
13
  end
23
14
  end
24
15
  end
@@ -0,0 +1,10 @@
1
+ {
2
+ "type": "object",
3
+ "required": ["message"],
4
+ "properties": {
5
+ "message": {
6
+ "type": "string",
7
+ "minLength": 1
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json-schema'
4
+ require 'json'
5
+
6
+ module Twiglet
7
+ class Validator
8
+ attr_accessor :custom_error_handler
9
+
10
+ def initialize(schema)
11
+ @schema = schema
12
+ @custom_error_handler = ->(e) { raise e }
13
+ end
14
+
15
+ def self.from_file(file_path)
16
+ new(JSON.parse(File.read(file_path)))
17
+ end
18
+
19
+ def validate(message)
20
+ JSON::Validator.validate!(@schema, message)
21
+ rescue JSON::Schema::ValidationError => e
22
+ custom_error_handler.call(e)
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Twiglet
4
- VERSION = '3.0.5'
4
+ VERSION = '3.1.2'
5
5
  end
@@ -3,11 +3,12 @@
3
3
  require 'minitest/autorun'
4
4
  require 'json'
5
5
  require_relative '../lib/twiglet/formatter'
6
+ require_relative '../lib/twiglet/validator'
6
7
 
7
8
  describe Twiglet::Formatter do
8
9
  before do
9
10
  @now = -> { Time.utc(2020, 5, 11, 15, 1, 1) }
10
- @formatter = Twiglet::Formatter.new('petshop', now: @now)
11
+ @formatter = Twiglet::Formatter.new('petshop', now: @now, validator: Twiglet::Validator.new({}))
11
12
  end
12
13
 
13
14
  it 'initializes an instance of a Ruby Logger Formatter' do
data/test/logger_test.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minitest/autorun'
4
+ require 'minitest/mock'
4
5
  require_relative '../lib/twiglet/logger'
5
6
 
6
7
  LEVELS = [
@@ -18,9 +19,11 @@ describe Twiglet::Logger do
18
19
  before do
19
20
  @now = -> { Time.utc(2020, 5, 11, 15, 1, 1) }
20
21
  @buffer = StringIO.new
21
- @logger = Twiglet::Logger.new('petshop',
22
- now: @now,
23
- output: @buffer)
22
+ @logger = Twiglet::Logger.new(
23
+ 'petshop',
24
+ now: @now,
25
+ output: @buffer
26
+ )
24
27
  end
25
28
 
26
29
  it 'should throw an error with an empty service name' do
@@ -30,21 +33,29 @@ describe Twiglet::Logger do
30
33
  end
31
34
 
32
35
  it 'conforms to the standard Ruby Logger API' do
33
- [:debug, :debug?, :info, :info?, :warn, :warn?, :fatal, :fatal?, :error, :error?,
34
- :level, :level=, :sev_threshold=].each do |call|
36
+ [
37
+ :debug, :debug?, :info, :info?, :warn, :warn?, :fatal, :fatal?, :error, :error?,
38
+ :level, :level=, :sev_threshold=
39
+ ].each do |call|
35
40
  assert @logger.respond_to?(call), "Logger does not respond to #{call}"
36
41
  end
37
42
  end
38
43
 
39
44
  describe 'JSON logging' do
40
45
  it 'should throw an error with an empty message' do
41
- assert_raises RuntimeError do
42
- @logger.info({message: ''})
46
+ assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
47
+ @logger.info({ message: '' })
48
+ end
49
+ end
50
+
51
+ it 'should throw an error if message is missing' do
52
+ assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
53
+ @logger.info({ foo: 'bar' })
43
54
  end
44
55
  end
45
56
 
46
57
  it 'should log mandatory attributes' do
47
- @logger.error({message: 'Out of pets exception'})
58
+ @logger.error({ message: 'Out of pets exception' })
48
59
  actual_log = read_json(@buffer)
49
60
 
50
61
  expected_log = {
@@ -65,9 +76,13 @@ describe Twiglet::Logger do
65
76
  end
66
77
 
67
78
  it 'should log the provided message' do
68
- @logger.error({event:
69
- {action: 'exception'},
70
- message: 'Emergency! Emergency!'})
79
+ @logger.error(
80
+ {
81
+ event:
82
+ { action: 'exception' },
83
+ message: 'Emergency! Emergency!'
84
+ }
85
+ )
71
86
  log = read_json(@buffer)
72
87
 
73
88
  assert_equal 'exception', log[:event][:action]
@@ -82,17 +97,19 @@ describe Twiglet::Logger do
82
97
  service: {
83
98
  type: 'shop'
84
99
  },
85
- request: {method: 'get'},
86
- response: {status_code: 200}
100
+ request: { method: 'get' },
101
+ response: { status_code: 200 }
87
102
  }
88
103
 
89
104
  output = StringIO.new
90
- logger = Twiglet::Logger.new('petshop',
91
- now: @now,
92
- output: output,
93
- default_properties: extra_properties)
94
-
95
- logger.error({message: 'GET /cats'})
105
+ logger = Twiglet::Logger.new(
106
+ 'petshop',
107
+ now: @now,
108
+ output: output,
109
+ default_properties: extra_properties
110
+ )
111
+
112
+ logger.error({ message: 'GET /cats' })
96
113
  log = read_json output
97
114
 
98
115
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -104,17 +121,21 @@ describe Twiglet::Logger do
104
121
 
105
122
  it "should be able to add properties with '.with'" do
106
123
  # Let's add some context to this customer journey
107
- purchase_logger = @logger.with({
108
- trace: {id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb'},
109
- customer: {full_name: 'Freda Bloggs'},
110
- event: {action: 'pet purchase'}
111
- })
124
+ purchase_logger = @logger.with(
125
+ {
126
+ trace: { id: '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb' },
127
+ customer: { full_name: 'Freda Bloggs' },
128
+ event: { action: 'pet purchase' }
129
+ }
130
+ )
112
131
 
113
132
  # do stuff
114
- purchase_logger.info({
115
- message: 'customer bought a dog',
116
- pet: {name: 'Barker', species: 'dog', breed: 'Bitsa'}
117
- })
133
+ purchase_logger.info(
134
+ {
135
+ message: 'customer bought a dog',
136
+ pet: { name: 'Barker', species: 'dog', breed: 'Bitsa' }
137
+ }
138
+ )
118
139
 
119
140
  log = read_json @buffer
120
141
 
@@ -135,8 +156,8 @@ describe Twiglet::Logger do
135
156
  end
136
157
 
137
158
  it "should log multiple messages properly" do
138
- @logger.debug({message: 'hi'})
139
- @logger.info({message: 'there'})
159
+ @logger.debug({ message: 'hi' })
160
+ @logger.info({ message: 'there' })
140
161
 
141
162
  expected_output =
142
163
  '{"ecs":{"version":"1.5.0"},"@timestamp":"2020-05-11T15:01:01.000Z",'\
@@ -174,7 +195,7 @@ describe Twiglet::Logger do
174
195
 
175
196
  LEVELS.each do |attrs|
176
197
  it "should correctly log level when calling #{attrs[:method]}" do
177
- @logger.public_send(attrs[:method], {message: 'a log message'})
198
+ @logger.public_send(attrs[:method], { message: 'a log message' })
178
199
  actual_log = read_json(@buffer)
179
200
 
180
201
  assert_equal attrs[:level], actual_log[:log][:level]
@@ -188,7 +209,7 @@ describe Twiglet::Logger do
188
209
  begin
189
210
  1 / 0
190
211
  rescue StandardError => e
191
- @logger.error({message: 'Artificially raised exception'}, e)
212
+ @logger.error({ message: 'Artificially raised exception' }, e)
192
213
  end
193
214
 
194
215
  actual_log = read_json(@buffer)
@@ -201,7 +222,7 @@ describe Twiglet::Logger do
201
222
 
202
223
  it 'should log an error without backtrace' do
203
224
  e = StandardError.new('Connection timed-out')
204
- @logger.error({message: 'Artificially raised exception'}, e)
225
+ @logger.error({ message: 'Artificially raised exception' }, e)
205
226
 
206
227
  actual_log = read_json(@buffer)
207
228
 
@@ -225,7 +246,7 @@ describe Twiglet::Logger do
225
246
 
226
247
  describe 'text logging' do
227
248
  it 'should throw an error with an empty message' do
228
- assert_raises RuntimeError do
249
+ assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
229
250
  @logger.info('')
230
251
  end
231
252
  end
@@ -284,13 +305,15 @@ describe Twiglet::Logger do
284
305
 
285
306
  describe 'dotted keys' do
286
307
  it 'should be able to convert dotted keys to nested objects' do
287
- @logger.debug({
288
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
289
- message: 'customer bought a dog',
290
- "pet.name": 'Barker',
291
- "pet.species": 'dog',
292
- "pet.breed": 'Bitsa'
293
- })
308
+ @logger.debug(
309
+ {
310
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
311
+ message: 'customer bought a dog',
312
+ "pet.name": 'Barker',
313
+ "pet.species": 'dog',
314
+ "pet.breed": 'Bitsa'
315
+ }
316
+ )
294
317
  log = read_json(@buffer)
295
318
 
296
319
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -301,12 +324,14 @@ describe Twiglet::Logger do
301
324
  end
302
325
 
303
326
  it 'should be able to mix dotted keys and nested objects' do
304
- @logger.debug({
305
- "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
306
- message: 'customer bought a dog',
307
- pet: {name: 'Barker', breed: 'Bitsa'},
308
- "pet.species": 'dog'
309
- })
327
+ @logger.debug(
328
+ {
329
+ "trace.id": '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb',
330
+ message: 'customer bought a dog',
331
+ pet: { name: 'Barker', breed: 'Bitsa' },
332
+ "pet.species": 'dog'
333
+ }
334
+ )
310
335
  log = read_json(@buffer)
311
336
 
312
337
  assert_equal '1c8a5fb2-fecd-44d8-92a4-449eb2ce4dcb', log[:trace][:id]
@@ -334,6 +359,27 @@ describe Twiglet::Logger do
334
359
  end
335
360
  end
336
361
 
362
+ describe 'configuring error response' do
363
+ it 'blows up by default' do
364
+ assert_raises JSON::Schema::ValidationError,
365
+ "The property '#/message' of type boolean did not match the following type: string" do
366
+ @logger.debug(message: true)
367
+ end
368
+ end
369
+
370
+ it 'silently swallows errors when configured to do so' do
371
+ mock = Minitest::Mock.new
372
+
373
+ @logger.configure_validation_error_response do |_e|
374
+ mock.notify_error("Logging schema validation error")
375
+ end
376
+
377
+ mock.expect(:notify_error, nil, ["Logging schema validation error"])
378
+ nonconformant_log = { message: true }
379
+ @logger.debug(nonconformant_log)
380
+ end
381
+ end
382
+
337
383
  private
338
384
 
339
385
  def read_json(buffer)
data/test/message_test.rb CHANGED
@@ -2,24 +2,6 @@ require 'minitest/autorun'
2
2
  require_relative '../lib/twiglet/message'
3
3
 
4
4
  describe Twiglet::Message do
5
- it 'raises if message is empty' do
6
- assert_raises RuntimeError do
7
- Twiglet::Message.new(' ')
8
- end
9
- end
10
-
11
- it 'raises if message is not provided' do
12
- assert_raises RuntimeError do
13
- Twiglet::Message.new(foo: 'bar')
14
- end
15
- end
16
-
17
- it 'raises on unrecognized inputs' do
18
- assert_raises RuntimeError do
19
- Twiglet::Message.new(OpenStruct.new(message: 'hello'))
20
- end
21
- end
22
-
23
5
  it 'returns a message hash from a string' do
24
6
  assert_equal Twiglet::Message.new('hello, world'), { message: 'hello, world' }
25
7
  end
@@ -7,7 +7,11 @@ SimpleCov.start do
7
7
  if ENV['CI']
8
8
  formatter SimpleCov::Formatter::SimpleFormatter
9
9
  else
10
- formatter SimpleCov::Formatter::MultiFormatter.new([SimpleCov::Formatter::SimpleFormatter,
11
- SimpleCov::Formatter::HTMLFormatter])
10
+ formatter SimpleCov::Formatter::MultiFormatter.new(
11
+ [
12
+ SimpleCov::Formatter::SimpleFormatter,
13
+ SimpleCov::Formatter::HTMLFormatter
14
+ ]
15
+ )
12
16
  end
13
17
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../lib/twiglet/validator'
5
+
6
+ describe Twiglet::Validator do
7
+ let(:valid)
8
+
9
+ before do
10
+ schema = {
11
+ "type" => "object",
12
+ "required" => ["message"],
13
+ "properties" => {
14
+ "message" => {
15
+ "type" => "string"
16
+ }
17
+ }
18
+ }
19
+
20
+ @validator = Twiglet::Validator.new(schema)
21
+ end
22
+
23
+ it 'does not raise when validation passes' do
24
+ assert_equal(@validator.validate({ message: 'this is my message', foo: 'bar' }), true)
25
+ end
26
+
27
+ it 'raises when validation fails' do
28
+ assert_raises JSON::Schema::ValidationError do
29
+ @validator.validate({ message: true })
30
+ end
31
+ end
32
+
33
+ it 'is a no-op when validator is configured to swallow errors' do
34
+ @validator.custom_error_handler = ->(e) { puts e }
35
+
36
+ assert_nil(@validator.validate({ message: true }))
37
+ end
38
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twiglet
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simply Business
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-25 00:00:00.000000000 Z
11
+ date: 2021-02-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Like a log, only smaller.
14
14
  email:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".github/CODEOWNERS"
21
21
  - ".github/dependabot.yml"
22
+ - ".github/workflows/dobby-actions.yml"
22
23
  - ".github/workflows/gem-publish.yml"
23
24
  - ".github/workflows/ruby.yml"
24
25
  - ".github/workflows/version-forget-me-not.yml"
@@ -41,12 +42,15 @@ files:
41
42
  - lib/twiglet/formatter.rb
42
43
  - lib/twiglet/logger.rb
43
44
  - lib/twiglet/message.rb
45
+ - lib/twiglet/validation_schema.json
46
+ - lib/twiglet/validator.rb
44
47
  - lib/twiglet/version.rb
45
48
  - test/formatter_test.rb
46
49
  - test/hash_extensions_test.rb
47
50
  - test/logger_test.rb
48
51
  - test/message_test.rb
49
52
  - test/test_coverage.rb
53
+ - test/validator_test.rb
50
54
  - twiglet.gemspec
51
55
  homepage: https://github.com/simplybusiness/twiglet-ruby
52
56
  licenses:
@@ -67,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
71
  - !ruby/object:Gem::Version
68
72
  version: '0'
69
73
  requirements: []
70
- rubygems_version: 3.1.4
74
+ rubygems_version: 3.2.3
71
75
  signing_key:
72
76
  specification_version: 4
73
77
  summary: Twiglet