twiglet 3.0.8 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|