rails_surrogate_key_logging 0.0.1
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 +7 -0
- data/README.md +1 -0
- data/lib/rails_surrogate_key_logging.rb +3 -0
- data/lib/surrogate_key_logging/action_controller/log_subscriber.rb +16 -0
- data/lib/surrogate_key_logging/action_controller.rb +10 -0
- data/lib/surrogate_key_logging/action_dispatch/request.rb +15 -0
- data/lib/surrogate_key_logging/action_dispatch.rb +10 -0
- data/lib/surrogate_key_logging/active_record/attributes.rb +40 -0
- data/lib/surrogate_key_logging/active_record/log_subscriber.rb +73 -0
- data/lib/surrogate_key_logging/active_record.rb +11 -0
- data/lib/surrogate_key_logging/active_support.rb +36 -0
- data/lib/surrogate_key_logging/key_manager.rb +13 -0
- data/lib/surrogate_key_logging/middleware.rb +18 -0
- data/lib/surrogate_key_logging/railtie.rb +28 -0
- data/lib/surrogate_key_logging/version.rb +18 -0
- data/lib/surrogate_key_logging.rb +60 -0
- data/rails_surrogate_key_logging.gemspec +34 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d4508c5a982897fd625ce7ee7625cedd7a78691b42a6562f9e036e2577f54b91
|
4
|
+
data.tar.gz: 5d5b0395a7cb737fd5f1dc90f0dbbf74a493fcdb91ad8644a9e802584b649e9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3201bc7f7e84b36813141a3c9e24883d1fd3a416ec4f05d6e6345fd46d063ebf494bcf39ca333f5292eed7ca845e0604d80f4907064b3db78aac6a496a477a08
|
7
|
+
data.tar.gz: 105d8ed9be27bf5e3f633f230b5836668793c45960628749b09e71570279d7f754207ce4275542fd886129975a6e342da0ac221c32c7ba4b81d27ddc49e12d68
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# rails-surrogate-key-logging
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_controller/log_subscriber'
|
4
|
+
|
5
|
+
module SurrogateKeyLogging
|
6
|
+
module ActionController
|
7
|
+
class LogSubscriber < ::ActionController::LogSubscriber
|
8
|
+
|
9
|
+
def start_processing(event)
|
10
|
+
event.payload[:params] = SurrogateKeyLogging.filter_parameters event.payload[:params]
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SurrogateKeyLogging
|
4
|
+
module ActionDispatch
|
5
|
+
module Request
|
6
|
+
|
7
|
+
def filtered_query_string
|
8
|
+
super.gsub(::ActionDispatch::Request::PAIR_RE) do |_|
|
9
|
+
SurrogateKeyLogging.filter_parameters(::Regexp.last_match(1) => ::Regexp.last_match(2)).first.join('=')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module SurrogateKeyLogging
|
6
|
+
module ActiveRecord
|
7
|
+
module Attributes
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
surrogate_parent_names model_name.singular, model_name.plural
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def surrogate_parent_names(*names)
|
16
|
+
@surrogate_parent_names ||= []
|
17
|
+
names.each do |name|
|
18
|
+
@surrogate_parent_names << name.to_sym
|
19
|
+
surrogate_attributes.each do |attr|
|
20
|
+
SurrogateKeyLogging.add_param_to_filter(attr, name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@surrogate_parent_names
|
24
|
+
end
|
25
|
+
|
26
|
+
def surrogate_attributes(*attrs)
|
27
|
+
@surrogate_attributes ||= []
|
28
|
+
attrs.each do |attr|
|
29
|
+
@surrogate_attributes << attr.to_sym
|
30
|
+
surrogate_parent_names.each do |parent|
|
31
|
+
SurrogateKeyLogging.add_param_to_filter(attr, parent)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@surrogate_attributes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SurrogateKeyLogging
|
4
|
+
module ActiveRecord
|
5
|
+
class LogSubscriber < ::ActiveRecord::LogSubscriber
|
6
|
+
|
7
|
+
def sql(event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
8
|
+
self.class.runtime += event.duration
|
9
|
+
return unless logger.debug?
|
10
|
+
|
11
|
+
payload = event.payload
|
12
|
+
|
13
|
+
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
14
|
+
|
15
|
+
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
16
|
+
name = "CACHE #{name}" if payload[:cached]
|
17
|
+
sql = payload[:sql]
|
18
|
+
binds = nil
|
19
|
+
|
20
|
+
name_match = /([A-Za-z]+) (Load|Update|Cache)/.match(payload[:name])
|
21
|
+
model = if name_match && name_match[1] && ::ActiveRecord::Base.descendants.map(&:to_s).include?(name_match[1])
|
22
|
+
name_match[1].safe_constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
if payload[:binds]&.any?
|
26
|
+
casted_params = type_casted_binds(payload[:type_casted_binds])
|
27
|
+
|
28
|
+
binds = []
|
29
|
+
payload[:binds].each_with_index do |attr, i|
|
30
|
+
binds << render_bind(attr, casted_params[i], payload, model)
|
31
|
+
end
|
32
|
+
binds = binds.inspect
|
33
|
+
binds.prepend(' ')
|
34
|
+
end
|
35
|
+
|
36
|
+
name = colorize_payload_name(name, payload[:name])
|
37
|
+
sql = color(sql, sql_color(sql), true) if colorize_logging
|
38
|
+
|
39
|
+
debug " #{name} #{sql}#{binds}"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def basic_parameter_filter
|
45
|
+
@basic_parameter_filter ||= ::ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
|
46
|
+
end
|
47
|
+
|
48
|
+
def type_casted_binds(casted_binds)
|
49
|
+
casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_bind(attr, value, _payload, model) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
|
+
case attr
|
54
|
+
when ActiveModel::Attribute
|
55
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>" if attr.type.binary? && attr.value
|
56
|
+
when Array
|
57
|
+
attr = attr.first
|
58
|
+
else
|
59
|
+
attr = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_name = attr&.name
|
63
|
+
if model && attr_name && model.respond_to?(:surrogate_attributes) && model.surrogate_attributes.include?(attr_name.to_sym)
|
64
|
+
value = SurrogateKeyLogging.key_manager.call(attr_name, value, model.to_s.underscore)
|
65
|
+
elsif attr_name
|
66
|
+
value = basic_parameter_filter.filter(attr_name => value)[attr_name]
|
67
|
+
end
|
68
|
+
[attr_name, value]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/parameter_filter'
|
4
|
+
|
5
|
+
# Add ability for @mask to be a class/instance/lambda/proc
|
6
|
+
module ActiveSupport
|
7
|
+
module ParameterFilter
|
8
|
+
class CompiledFilter
|
9
|
+
|
10
|
+
def value_for_key(key, value, parents = [], original_params = nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
11
|
+
parents.push(key) if deep_regexps
|
12
|
+
if regexps.any? { |r| r.match?(key.to_s) }
|
13
|
+
value = @mask.respond_to?(:call) ? @mask.call(key, value, parents, original_params) : @mask
|
14
|
+
elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? { |r| r.match?(joined) } # rubocop:disable Lint/DuplicateBranch
|
15
|
+
value = @mask.respond_to?(:call) ? @mask.call(key, value, parents, original_params) : @mask
|
16
|
+
elsif value.is_a?(Hash)
|
17
|
+
value = call(value, parents, original_params)
|
18
|
+
elsif value.is_a?(Array)
|
19
|
+
# If we don't pop the current parent it will be duplicated as we
|
20
|
+
# process each array value.
|
21
|
+
parents.pop if deep_regexps
|
22
|
+
value = value.map { |v| value_for_key(key, v, parents, original_params) }
|
23
|
+
# Restore the parent stack after processing the array.
|
24
|
+
parents.push(key) if deep_regexps
|
25
|
+
elsif blocks.any?
|
26
|
+
key = key.dup if key.duplicable?
|
27
|
+
value = value.dup if value.duplicable?
|
28
|
+
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
|
29
|
+
end
|
30
|
+
parents.pop if deep_regexps
|
31
|
+
value
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SurrogateKeyLogging
|
4
|
+
class KeyManager
|
5
|
+
|
6
|
+
def call(key, value, _parents = [], _original_params = nil)
|
7
|
+
surrogate = "SK#{Digest::SHA512.hexdigest(value.to_s)}"
|
8
|
+
puts "surrogate for key: `#{key}`, value: `#{value}`, surrogate: `#{surrogate}`"
|
9
|
+
surrogate
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SurrogateKeyLogging
|
4
|
+
class Middleware
|
5
|
+
attr_reader :app
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
puts 'surrogate new'
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
SurrogateKeyLogging.reset
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails'
|
4
|
+
|
5
|
+
module SurrogateKeyLogging
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
railtie_name :surrogate_key_logging
|
8
|
+
|
9
|
+
initializer 'surrogate_key_logging.filter_parameters' do
|
10
|
+
puts 'surrogate_key_logging.filter_parameters'
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer 'surrogate_key_logging.logs' do
|
14
|
+
puts 'surrogate_key_logging.logs'
|
15
|
+
::ActiveRecord::LogSubscriber.detach_from(:active_record)
|
16
|
+
::SurrogateKeyLogging::ActiveRecord::LogSubscriber.attach_to(:active_record)
|
17
|
+
::ActiveSupport::LogSubscriber.detach_from(:action_controller)
|
18
|
+
::SurrogateKeyLogging::ActionController::LogSubscriber.attach_to(:action_controller)
|
19
|
+
::ActionDispatch::Request.include SurrogateKeyLogging::ActionDispatch::Request
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer 'surrogate_key_logging.middleware' do
|
23
|
+
puts 'surrogate_key_logging.middleware'
|
24
|
+
Rails.application.config.middleware.insert_before 0, Middleware
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
|
5
|
+
# Container Module
|
6
|
+
module SurrogateKeyLogging
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
autoload :Railtie
|
10
|
+
autoload :Version
|
11
|
+
autoload :ActionController
|
12
|
+
autoload :ActionDispatch
|
13
|
+
autoload :ActiveSupport
|
14
|
+
autoload :ActiveRecord
|
15
|
+
autoload :KeyManager
|
16
|
+
autoload :Middleware
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def surrogate_attributes(*attrs)
|
21
|
+
@surrogate_attributes ||= []
|
22
|
+
attrs.each do |attr|
|
23
|
+
@surrogate_attributes << attr.to_s
|
24
|
+
end
|
25
|
+
@surrogate_attributes
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset
|
29
|
+
@key_manager = @parameter_filter = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def key_manager
|
33
|
+
@key_manager ||= KeyManager.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def parameter_filter
|
37
|
+
@parameter_filter ||= ::ActiveSupport::ParameterFilter.new(surrogate_attributes, mask: key_manager)
|
38
|
+
end
|
39
|
+
|
40
|
+
def filter_parameters(params)
|
41
|
+
parameter_filter.filter params
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_param_to_filter(attr, parent = nil)
|
45
|
+
if parent.nil?
|
46
|
+
surrogate_attributes attr.to_s
|
47
|
+
else
|
48
|
+
surrogate_attributes(
|
49
|
+
"#{parent}.#{attr}",
|
50
|
+
"#{parent}[#{attr}]",
|
51
|
+
"[#{parent}][#{attr}]"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
require 'surrogate_key_logging/railtie'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
4
|
+
require 'surrogate_key_logging/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'rails_surrogate_key_logging'
|
8
|
+
s.version = SurrogateKeyLogging::VERSION
|
9
|
+
s.authors = ['Taylor Yelverton']
|
10
|
+
s.email = 'rubygems@yelvert.io'
|
11
|
+
s.homepage = 'https://github.com/ComplyMD/rails_surrogate_key_logging'
|
12
|
+
s.summary = ''
|
13
|
+
s.license = 'MIT'
|
14
|
+
s.description = ''
|
15
|
+
s.metadata = {
|
16
|
+
'bug_tracker_uri' => 'https://github.com/ComplyMD/rails_surrogate_key_logging/issues',
|
17
|
+
'changelog_uri' => 'https://github.com/ComplyMD/rails_surrogate_key_logging/commits/master',
|
18
|
+
'documentation_uri' => 'https://github.com/ComplyMD/rails_surrogate_key_logging/wiki',
|
19
|
+
'homepage_uri' => 'https://github.com/ComplyMD/rails_surrogate_key_logging',
|
20
|
+
'source_code_uri' => 'https://github.com/ComplyMD/rails_surrogate_key_logging',
|
21
|
+
'rubygems_mfa_required' => 'true',
|
22
|
+
}
|
23
|
+
|
24
|
+
s.files = Dir['lib/**/*', 'README.md', 'MIT-LICENSE', 'rails_surrogate_key_logging.gemspec']
|
25
|
+
|
26
|
+
s.require_paths = %w[ lib ]
|
27
|
+
|
28
|
+
s.required_ruby_version = '>= 2.7.0'
|
29
|
+
|
30
|
+
s.add_dependency('actionpack', '>= 6.0.0')
|
31
|
+
s.add_dependency('activerecord', '>= 6.0.0')
|
32
|
+
s.add_dependency('activesupport', '>= 6.0.0')
|
33
|
+
s.add_dependency('railties', '>= 6.0.0')
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_surrogate_key_logging
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Taylor Yelverton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 6.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 6.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 6.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: railties
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 6.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 6.0.0
|
69
|
+
description: ''
|
70
|
+
email: rubygems@yelvert.io
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- README.md
|
76
|
+
- lib/rails_surrogate_key_logging.rb
|
77
|
+
- lib/surrogate_key_logging.rb
|
78
|
+
- lib/surrogate_key_logging/action_controller.rb
|
79
|
+
- lib/surrogate_key_logging/action_controller/log_subscriber.rb
|
80
|
+
- lib/surrogate_key_logging/action_dispatch.rb
|
81
|
+
- lib/surrogate_key_logging/action_dispatch/request.rb
|
82
|
+
- lib/surrogate_key_logging/active_record.rb
|
83
|
+
- lib/surrogate_key_logging/active_record/attributes.rb
|
84
|
+
- lib/surrogate_key_logging/active_record/log_subscriber.rb
|
85
|
+
- lib/surrogate_key_logging/active_support.rb
|
86
|
+
- lib/surrogate_key_logging/key_manager.rb
|
87
|
+
- lib/surrogate_key_logging/middleware.rb
|
88
|
+
- lib/surrogate_key_logging/railtie.rb
|
89
|
+
- lib/surrogate_key_logging/version.rb
|
90
|
+
- rails_surrogate_key_logging.gemspec
|
91
|
+
homepage: https://github.com/ComplyMD/rails_surrogate_key_logging
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata:
|
95
|
+
bug_tracker_uri: https://github.com/ComplyMD/rails_surrogate_key_logging/issues
|
96
|
+
changelog_uri: https://github.com/ComplyMD/rails_surrogate_key_logging/commits/master
|
97
|
+
documentation_uri: https://github.com/ComplyMD/rails_surrogate_key_logging/wiki
|
98
|
+
homepage_uri: https://github.com/ComplyMD/rails_surrogate_key_logging
|
99
|
+
source_code_uri: https://github.com/ComplyMD/rails_surrogate_key_logging
|
100
|
+
rubygems_mfa_required: 'true'
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.7.0
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.1.4
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: ''
|
120
|
+
test_files: []
|