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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aadbe159aee695cfd454e0f006ab5eab2b3c06d01ecaa80d3f6b1b69c0d21d5e
4
- data.tar.gz: 53907b03fb29af1fe41d8262619a6d5c84dab554b4e309d7336d969c11600a44
3
+ metadata.gz: 6970a1b98fbd77737c3742429691e3c03c7966e894e2f6a523824df785941858
4
+ data.tar.gz: 3a878416f432a4c9e38b0299afb6204fd2202168646d028298d40ab2c6cc7746
5
5
  SHA512:
6
- metadata.gz: d5e68a4b343c861c7236e4a301365d738221bbe5eb0b23426e3cba84fad2358046d1b868bef5248c1c3398f4c2cfd132445259b7db976937024f73663699c2d4
7
- data.tar.gz: 28b619fd981103d5baea53d07e032e52e21cf88e84b5229d1ca1ff61cedc284d6825ebf26fb977f060d3af4c34f276b8a69c733e66321952a726d452dc82164b
6
+ metadata.gz: 8a2e0c5bd321f495f4cf2c5d69a8caaff889e352305c5a900c1c7415e8637d0127729c91bd030353c1dd3b56e9c5623880f46849d3ff3eac1d502c3df8752c54
7
+ data.tar.gz: 0076c02f4070de277e488278f9f858d529b0ab39d8f8c07bc59a9d583c40421f0231b769a7ffb6a6ad1b422ed17f22ee4cd2a7eb68e436dc3b2b6c70c0246c8e
@@ -1,4 +1,4 @@
1
- name: "version update action"
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: 'bump version'
12
- uses: simplybusiness/dobby@v1.0.0
11
+ - name: bump version
12
+ uses: simplybusiness/dobby@v2.0.0
13
13
  env:
14
- ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
- group :development, :test do
5
- gem 'minitest'
6
- gem 'rake'
7
- gem 'simplecov', '0.17.1'
8
- gem 'simplycop'
9
- end
4
+
5
+ gemspec
@@ -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
- log(level: level, message: Message.new(msg))
22
+ message = Message.new(msg)
23
+ @validator.validate(message)
24
+ log(level: level, message: message)
22
25
  end
23
26
 
24
27
  private
@@ -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
- default_properties: {},
17
- now: -> { Time.now.utc },
18
- output: $stdout,
19
- level: Logger::DEBUG
16
+ **args
20
17
  )
21
18
  @service_name = service_name
22
- @now = now
23
- @output = output
24
- @level = level
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
- formatter = Twiglet::Formatter.new(service_name, default_properties: default_properties, now: now)
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
 
@@ -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,10 @@
1
+ {
2
+ "type": "object",
3
+ "required": ["message"],
4
+ "properties": {
5
+ "message": {
6
+ "type": "string",
7
+ "minLength": 1
8
+ }
9
+ }
10
+ }
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Twiglet
4
- VERSION = '3.0.8'
4
+ VERSION = '3.2.0'
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({}.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 RuntimeError do
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 RuntimeError do
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.8
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-11 00:00:00.000000000 Z
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: