twiglet 3.0.8 → 3.2.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/.github/workflows/{version-update.yml → dobby-actions.yml} +5 -4
- data/Gemfile +2 -6
- data/lib/twiglet/formatter.rb +5 -2
- data/lib/twiglet/logger.rb +22 -13
- data/lib/twiglet/message.rb +0 -12
- data/lib/twiglet/validation_schema.json +10 -0
- data/lib/twiglet/validator.rb +21 -0
- data/lib/twiglet/version.rb +1 -1
- data/test/formatter_test.rb +2 -1
- data/test/logger_test.rb +85 -2
- data/test/message_test.rb +0 -18
- data/test/validator_test.rb +38 -0
- data/twiglet.gemspec +6 -0
- metadata +77 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6970a1b98fbd77737c3742429691e3c03c7966e894e2f6a523824df785941858
|
4
|
+
data.tar.gz: 3a878416f432a4c9e38b0299afb6204fd2202168646d028298d40ab2c6cc7746
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a2e0c5bd321f495f4cf2c5d69a8caaff889e352305c5a900c1c7415e8637d0127729c91bd030353c1dd3b56e9c5623880f46849d3ff3eac1d502c3df8752c54
|
7
|
+
data.tar.gz: 0076c02f4070de277e488278f9f858d529b0ab39d8f8c07bc59a9d583c40421f0231b769a7ffb6a6ad1b422ed17f22ee4cd2a7eb68e436dc3b2b6c70c0246c8e
|
@@ -1,4 +1,4 @@
|
|
1
|
-
name: "
|
1
|
+
name: "dobby action"
|
2
2
|
on:
|
3
3
|
issue_comment:
|
4
4
|
types: [created]
|
@@ -8,8 +8,9 @@ jobs:
|
|
8
8
|
if: startsWith(github.event.comment.body, '/dobby')
|
9
9
|
|
10
10
|
steps:
|
11
|
-
- name:
|
12
|
-
uses: simplybusiness/dobby@
|
11
|
+
- name: bump version
|
12
|
+
uses: simplybusiness/dobby@v2.0.0
|
13
13
|
env:
|
14
|
-
|
14
|
+
DOBBY_APP_ID: ${{ secrets.DOBBY_APP_ID }}
|
15
|
+
DOBBY_PRIVATE_KEY: ${{ secrets.DOBBY_PRIVATE_KEY }}
|
15
16
|
VERSION_FILE_PATH: lib/twiglet/version.rb
|
data/Gemfile
CHANGED
data/lib/twiglet/formatter.rb
CHANGED
@@ -7,18 +7,21 @@ module Twiglet
|
|
7
7
|
Hash.include HashExtensions
|
8
8
|
|
9
9
|
def initialize(service_name,
|
10
|
-
default_properties: {},
|
10
|
+
validator:, default_properties: {},
|
11
11
|
now: -> { Time.now.utc })
|
12
12
|
@service_name = service_name
|
13
13
|
@now = now
|
14
14
|
@default_properties = default_properties
|
15
|
+
@validator = validator
|
15
16
|
|
16
17
|
super()
|
17
18
|
end
|
18
19
|
|
19
20
|
def call(severity, _time, _progname, msg)
|
20
21
|
level = severity.downcase
|
21
|
-
|
22
|
+
message = Message.new(msg)
|
23
|
+
@validator.validate(message)
|
24
|
+
log(level: level, message: message)
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
data/lib/twiglet/logger.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'logger'
|
4
4
|
require 'time'
|
5
|
-
require 'json'
|
6
5
|
require_relative 'formatter'
|
7
6
|
require_relative '../hash_extensions'
|
8
7
|
require_relative 'message'
|
8
|
+
require_relative 'validator'
|
9
9
|
|
10
10
|
module Twiglet
|
11
11
|
class Logger < ::Logger
|
@@ -13,23 +13,35 @@ module Twiglet
|
|
13
13
|
|
14
14
|
def initialize(
|
15
15
|
service_name,
|
16
|
-
|
17
|
-
now: -> { Time.now.utc },
|
18
|
-
output: $stdout,
|
19
|
-
level: Logger::DEBUG
|
16
|
+
**args
|
20
17
|
)
|
21
18
|
@service_name = service_name
|
22
|
-
|
23
|
-
@
|
24
|
-
|
19
|
+
default_properties = args.delete(:default_properties) || {}
|
20
|
+
@args = args
|
21
|
+
|
22
|
+
now = args.fetch(:now, -> { Time.now.utc })
|
23
|
+
output = args.fetch(:output, $stdout)
|
24
|
+
level = args.fetch(:level, Logger::DEBUG)
|
25
|
+
validation_schema = args.fetch(:validation_schema, File.read("#{__dir__}/validation_schema.json"))
|
25
26
|
|
26
27
|
raise 'Service name is mandatory' \
|
27
28
|
unless service_name.is_a?(String) && !service_name.strip.empty?
|
28
29
|
|
29
|
-
|
30
|
+
@validator = Validator.new(validation_schema)
|
31
|
+
|
32
|
+
formatter = Twiglet::Formatter.new(
|
33
|
+
service_name,
|
34
|
+
default_properties: default_properties,
|
35
|
+
now: now,
|
36
|
+
validator: @validator
|
37
|
+
)
|
30
38
|
super(output, formatter: formatter, level: level)
|
31
39
|
end
|
32
40
|
|
41
|
+
def configure_validation_error_response(&block)
|
42
|
+
@validator.custom_error_handler = block
|
43
|
+
end
|
44
|
+
|
33
45
|
def error(message = nil, error = nil, &block)
|
34
46
|
if error
|
35
47
|
error_fields = {
|
@@ -48,10 +60,7 @@ module Twiglet
|
|
48
60
|
def with(default_properties)
|
49
61
|
Logger.new(
|
50
62
|
@service_name,
|
51
|
-
default_properties: default_properties
|
52
|
-
now: @now,
|
53
|
-
output: @output,
|
54
|
-
level: @level
|
63
|
+
**@args.merge(default_properties: default_properties)
|
55
64
|
)
|
56
65
|
end
|
57
66
|
|
data/lib/twiglet/message.rb
CHANGED
@@ -10,18 +10,6 @@ module Twiglet
|
|
10
10
|
else
|
11
11
|
super(msg)
|
12
12
|
end
|
13
|
-
|
14
|
-
validate!
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def validate!
|
20
|
-
raise 'Message must be initialized with a String or a non-empty Hash' if empty?
|
21
|
-
|
22
|
-
raise 'Log object must have a \'message\' property' unless self[:message]
|
23
|
-
|
24
|
-
raise 'The \'message\' property of the log object must not be empty' if self[:message].strip.empty?
|
25
13
|
end
|
26
14
|
end
|
27
15
|
end
|
@@ -0,0 +1,21 @@
|
|
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 = JSON.parse(schema)
|
12
|
+
@custom_error_handler = ->(e) { raise e }
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate(message)
|
16
|
+
JSON::Validator.validate!(@schema, message)
|
17
|
+
rescue JSON::Schema::ValidationError => e
|
18
|
+
custom_error_handler.call(e)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/twiglet/version.rb
CHANGED
data/test/formatter_test.rb
CHANGED
@@ -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({}.to_json))
|
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 = [
|
@@ -42,11 +43,17 @@ describe Twiglet::Logger do
|
|
42
43
|
|
43
44
|
describe 'JSON logging' do
|
44
45
|
it 'should throw an error with an empty message' do
|
45
|
-
assert_raises
|
46
|
+
assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
|
46
47
|
@logger.info({ message: '' })
|
47
48
|
end
|
48
49
|
end
|
49
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' })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
50
57
|
it 'should log mandatory attributes' do
|
51
58
|
@logger.error({ message: 'Out of pets exception' })
|
52
59
|
actual_log = read_json(@buffer)
|
@@ -239,7 +246,7 @@ describe Twiglet::Logger do
|
|
239
246
|
|
240
247
|
describe 'text logging' do
|
241
248
|
it 'should throw an error with an empty message' do
|
242
|
-
assert_raises
|
249
|
+
assert_raises JSON::Schema::ValidationError, "The property '#/message' was not of a minimum string length of 1" do
|
243
250
|
@logger.info('')
|
244
251
|
end
|
245
252
|
end
|
@@ -352,6 +359,82 @@ describe Twiglet::Logger do
|
|
352
359
|
end
|
353
360
|
end
|
354
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
|
+
|
383
|
+
describe 'validation schema' do
|
384
|
+
before do
|
385
|
+
validation_schema = <<-JSON
|
386
|
+
{
|
387
|
+
"type": "object",
|
388
|
+
"required": ["pet"],
|
389
|
+
"properties": {
|
390
|
+
"pet": {
|
391
|
+
"type": "object",
|
392
|
+
"required": ["name", "best_boy_or_girl?"],
|
393
|
+
"properties": {
|
394
|
+
"name": {
|
395
|
+
"type": "string",
|
396
|
+
"minLength": 1
|
397
|
+
},
|
398
|
+
"best_boy_or_girl?": {
|
399
|
+
"type": "boolean"
|
400
|
+
}
|
401
|
+
}
|
402
|
+
}
|
403
|
+
}
|
404
|
+
}
|
405
|
+
JSON
|
406
|
+
|
407
|
+
@logger = Twiglet::Logger.new(
|
408
|
+
'petshop',
|
409
|
+
now: @now,
|
410
|
+
output: @buffer,
|
411
|
+
validation_schema: validation_schema
|
412
|
+
)
|
413
|
+
end
|
414
|
+
|
415
|
+
it 'allows for the configuration of custom validation rules' do
|
416
|
+
@logger.debug(
|
417
|
+
{
|
418
|
+
pet: { name: 'Davis', best_boy_or_girl?: true, species: 'dog' }
|
419
|
+
}
|
420
|
+
)
|
421
|
+
log = read_json(@buffer)
|
422
|
+
|
423
|
+
assert_equal true, log[:pet][:best_boy_or_girl?]
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'raises when custom validation rules are broken' do
|
427
|
+
nonconformant = {
|
428
|
+
pet: { name: 'Davis' }
|
429
|
+
}
|
430
|
+
|
431
|
+
assert_raises JSON::Schema::ValidationError,
|
432
|
+
"The property '#/pet' did not contain a required property of 'best_boy_or_girl?'" do
|
433
|
+
@logger.debug(nonconformant)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
355
438
|
private
|
356
439
|
|
357
440
|
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
|
@@ -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.to_json)
|
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
|
data/twiglet.gemspec
CHANGED
@@ -21,4 +21,10 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.required_ruby_version = '>= 2.6'
|
22
22
|
|
23
23
|
gem.license = 'Copyright SimplyBusiness'
|
24
|
+
|
25
|
+
gem.add_runtime_dependency 'json-schema'
|
26
|
+
gem.add_development_dependency 'minitest'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
|
+
gem.add_development_dependency 'simplecov', '0.17.1'
|
29
|
+
gem.add_development_dependency 'simplycop'
|
24
30
|
end
|
metadata
CHANGED
@@ -1,15 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twiglet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.2.0
|
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-02-
|
12
|
-
dependencies:
|
11
|
+
date: 2021-02-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json-schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.17.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.17.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplycop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
13
83
|
description: Like a log, only smaller.
|
14
84
|
email:
|
15
85
|
- tech@simplybusiness.co.uk
|
@@ -19,10 +89,10 @@ extra_rdoc_files: []
|
|
19
89
|
files:
|
20
90
|
- ".github/CODEOWNERS"
|
21
91
|
- ".github/dependabot.yml"
|
92
|
+
- ".github/workflows/dobby-actions.yml"
|
22
93
|
- ".github/workflows/gem-publish.yml"
|
23
94
|
- ".github/workflows/ruby.yml"
|
24
95
|
- ".github/workflows/version-forget-me-not.yml"
|
25
|
-
- ".github/workflows/version-update.yml"
|
26
96
|
- ".gitignore"
|
27
97
|
- ".rubocop.yml"
|
28
98
|
- ".ruby-version"
|
@@ -42,12 +112,15 @@ files:
|
|
42
112
|
- lib/twiglet/formatter.rb
|
43
113
|
- lib/twiglet/logger.rb
|
44
114
|
- lib/twiglet/message.rb
|
115
|
+
- lib/twiglet/validation_schema.json
|
116
|
+
- lib/twiglet/validator.rb
|
45
117
|
- lib/twiglet/version.rb
|
46
118
|
- test/formatter_test.rb
|
47
119
|
- test/hash_extensions_test.rb
|
48
120
|
- test/logger_test.rb
|
49
121
|
- test/message_test.rb
|
50
122
|
- test/test_coverage.rb
|
123
|
+
- test/validator_test.rb
|
51
124
|
- twiglet.gemspec
|
52
125
|
homepage: https://github.com/simplybusiness/twiglet-ruby
|
53
126
|
licenses:
|