sheets_v4 0.9.0 → 0.10.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 +4 -4
- data/.commitlintrc.yml +16 -0
- data/.husky/commit-msg +1 -0
- data/.markdownlint.yml +1 -1
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +5 -35
- data/.yardopts +3 -1
- data/CHANGELOG.md +35 -0
- data/LICENSE.txt +1 -1
- data/README.md +116 -44
- data/Rakefile +9 -10
- data/examples/README.md +230 -47
- data/examples/create_spreadsheet +11 -0
- data/examples/{set_background_color → set_background_color1} +21 -8
- data/examples/set_background_color2 +11 -21
- data/lib/sheets_v4/color.rb +2 -2
- data/lib/sheets_v4/convert_dates_and_times.rb +2 -2
- data/lib/sheets_v4/google_extensions/sheet.rb +0 -1
- data/lib/sheets_v4/google_extensions/sheets_service.rb +0 -1
- data/lib/sheets_v4/google_extensions/spreadsheet.rb +0 -1
- data/lib/sheets_v4/version.rb +1 -1
- data/lib/sheets_v4.rb +0 -36
- data/package.json +11 -0
- data/release-please-config.json +22 -0
- metadata +67 -25
- data/lib/sheets_v4/api_object_validation/load_schemas.rb +0 -155
- data/lib/sheets_v4/api_object_validation/resolve_schema_ref.rb +0 -72
- data/lib/sheets_v4/api_object_validation/traverse_object_tree.rb +0 -82
- data/lib/sheets_v4/api_object_validation/validate_api_object.rb +0 -87
- data/lib/sheets_v4/validate_api_objects/validate_api_object.rb +0 -87
- data/lib/sheets_v4/validate_api_objects.rb +0 -20
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sheets_v4
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Couball
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bundler-audit
|
@@ -30,56 +29,84 @@ dependencies:
|
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1
|
32
|
+
version: '2.1'
|
34
33
|
type: :development
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
37
|
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1
|
39
|
+
version: '2.1'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: discovery_v1
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.1'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.1'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: main_branch_shared_rubocop_config
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.1'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0.1'
|
41
68
|
- !ruby/object:Gem::Dependency
|
42
69
|
name: rake
|
43
70
|
requirement: !ruby/object:Gem::Requirement
|
44
71
|
requirements:
|
45
72
|
- - "~>"
|
46
73
|
- !ruby/object:Gem::Version
|
47
|
-
version: '13.
|
74
|
+
version: '13.2'
|
48
75
|
type: :development
|
49
76
|
prerelease: false
|
50
77
|
version_requirements: !ruby/object:Gem::Requirement
|
51
78
|
requirements:
|
52
79
|
- - "~>"
|
53
80
|
- !ruby/object:Gem::Version
|
54
|
-
version: '13.
|
81
|
+
version: '13.2'
|
55
82
|
- !ruby/object:Gem::Dependency
|
56
83
|
name: rspec
|
57
84
|
requirement: !ruby/object:Gem::Requirement
|
58
85
|
requirements:
|
59
86
|
- - "~>"
|
60
87
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
88
|
+
version: '3.13'
|
62
89
|
type: :development
|
63
90
|
prerelease: false
|
64
91
|
version_requirements: !ruby/object:Gem::Requirement
|
65
92
|
requirements:
|
66
93
|
- - "~>"
|
67
94
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
95
|
+
version: '3.13'
|
69
96
|
- !ruby/object:Gem::Dependency
|
70
97
|
name: rubocop
|
71
98
|
requirement: !ruby/object:Gem::Requirement
|
72
99
|
requirements:
|
73
100
|
- - "~>"
|
74
101
|
- !ruby/object:Gem::Version
|
75
|
-
version: '1.
|
102
|
+
version: '1.66'
|
76
103
|
type: :development
|
77
104
|
prerelease: false
|
78
105
|
version_requirements: !ruby/object:Gem::Requirement
|
79
106
|
requirements:
|
80
107
|
- - "~>"
|
81
108
|
- !ruby/object:Gem::Version
|
82
|
-
version: '1.
|
109
|
+
version: '1.66'
|
83
110
|
- !ruby/object:Gem::Dependency
|
84
111
|
name: simplecov
|
85
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +135,20 @@ dependencies:
|
|
108
135
|
- - "~>"
|
109
136
|
- !ruby/object:Gem::Version
|
110
137
|
version: '0.8'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: simplecov-rspec
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0.3'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0.3'
|
111
152
|
- !ruby/object:Gem::Dependency
|
112
153
|
name: redcarpet
|
113
154
|
requirement: !ruby/object:Gem::Requirement
|
@@ -240,14 +281,17 @@ dependencies:
|
|
240
281
|
- - "~>"
|
241
282
|
- !ruby/object:Gem::Version
|
242
283
|
version: '3.0'
|
243
|
-
description: Unofficial helpers for the Google Sheets V4 API
|
284
|
+
description: Unofficial helpers and extensions for the Google Sheets V4 API
|
244
285
|
email:
|
245
286
|
- jcouball@yahoo.com
|
246
287
|
executables: []
|
247
288
|
extensions: []
|
248
289
|
extra_rdoc_files: []
|
249
290
|
files:
|
291
|
+
- ".commitlintrc.yml"
|
292
|
+
- ".husky/commit-msg"
|
250
293
|
- ".markdownlint.yml"
|
294
|
+
- ".release-please-manifest.json"
|
251
295
|
- ".rspec"
|
252
296
|
- ".rubocop.yml"
|
253
297
|
- ".yardopts"
|
@@ -257,14 +301,11 @@ files:
|
|
257
301
|
- README.md
|
258
302
|
- Rakefile
|
259
303
|
- examples/README.md
|
260
|
-
- examples/
|
304
|
+
- examples/create_spreadsheet
|
305
|
+
- examples/set_background_color1
|
261
306
|
- examples/set_background_color2
|
262
307
|
- lib/sheets_v4.rb
|
263
308
|
- lib/sheets_v4/api_object_validation.rb
|
264
|
-
- lib/sheets_v4/api_object_validation/load_schemas.rb
|
265
|
-
- lib/sheets_v4/api_object_validation/resolve_schema_ref.rb
|
266
|
-
- lib/sheets_v4/api_object_validation/traverse_object_tree.rb
|
267
|
-
- lib/sheets_v4/api_object_validation/validate_api_object.rb
|
268
309
|
- lib/sheets_v4/color.rb
|
269
310
|
- lib/sheets_v4/convert_dates_and_times.rb
|
270
311
|
- lib/sheets_v4/create_credential.rb
|
@@ -272,9 +313,9 @@ files:
|
|
272
313
|
- lib/sheets_v4/google_extensions/sheet.rb
|
273
314
|
- lib/sheets_v4/google_extensions/sheets_service.rb
|
274
315
|
- lib/sheets_v4/google_extensions/spreadsheet.rb
|
275
|
-
- lib/sheets_v4/validate_api_objects.rb
|
276
|
-
- lib/sheets_v4/validate_api_objects/validate_api_object.rb
|
277
316
|
- lib/sheets_v4/version.rb
|
317
|
+
- package.json
|
318
|
+
- release-please-config.json
|
278
319
|
homepage: https://github.com/main-branch/sheets_v4
|
279
320
|
licenses:
|
280
321
|
- MIT
|
@@ -283,8 +324,8 @@ metadata:
|
|
283
324
|
rubygems_mfa_required: 'true'
|
284
325
|
homepage_uri: https://github.com/main-branch/sheets_v4
|
285
326
|
source_code_uri: https://github.com/main-branch/sheets_v4
|
286
|
-
|
287
|
-
|
327
|
+
documentation_uri: https://rubydoc.info/gems/sheets_v4/0.10.1
|
328
|
+
changelog_uri: https://rubydoc.info/gems/sheets_v4/0.10.1/file/CHANGELOG.md
|
288
329
|
rdoc_options: []
|
289
330
|
require_paths:
|
290
331
|
- lib
|
@@ -298,9 +339,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
298
339
|
- - ">="
|
299
340
|
- !ruby/object:Gem::Version
|
300
341
|
version: '0'
|
301
|
-
requirements:
|
302
|
-
|
303
|
-
|
342
|
+
requirements:
|
343
|
+
- 'Platform: Mac, Linux, or Windows'
|
344
|
+
- 'Ruby: MRI 3.1 or later, TruffleRuby 24 or later, or JRuby 9.4 or later'
|
345
|
+
rubygems_version: 3.6.7
|
304
346
|
specification_version: 4
|
305
|
-
summary: Unofficial helpers for the Google Sheets V4 API
|
347
|
+
summary: Unofficial helpers and extensions for the Google Sheets V4 API
|
306
348
|
test_files: []
|
@@ -1,155 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SheetsV4
|
4
|
-
module ApiObjectValidation
|
5
|
-
# Load the Google Discovery API description for the Sheets V4 API
|
6
|
-
#
|
7
|
-
# @example
|
8
|
-
# logger = Logger.new(STDOUT, :level => Logger::ERROR)
|
9
|
-
# schemas = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call
|
10
|
-
#
|
11
|
-
# @api private
|
12
|
-
#
|
13
|
-
class LoadSchemas
|
14
|
-
# Loads schemas for the Sheets V4 API object from the Google Discovery API
|
15
|
-
#
|
16
|
-
# By default, a nil logger is used. This means that nothing is logged.
|
17
|
-
#
|
18
|
-
# The schemas are only loaded once and cached.
|
19
|
-
#
|
20
|
-
# @example
|
21
|
-
# schema_loader = SheetsV4::ApiObjectValidation::LoadSchemas.new
|
22
|
-
#
|
23
|
-
# @param logger [Logger] the logger to use
|
24
|
-
#
|
25
|
-
def initialize(logger: Logger.new(nil))
|
26
|
-
@logger = logger
|
27
|
-
end
|
28
|
-
|
29
|
-
# The logger to use internally for logging errors
|
30
|
-
#
|
31
|
-
# @example
|
32
|
-
# logger = Logger.new(STDOUT, :level => Logger::INFO)
|
33
|
-
# schema_loader = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger)
|
34
|
-
# schema_loader.logger == logger # => true
|
35
|
-
#
|
36
|
-
# @return [Logger]
|
37
|
-
#
|
38
|
-
attr_reader :logger
|
39
|
-
|
40
|
-
# A hash of schemas keyed by the schema name loaded from the Google Discovery API
|
41
|
-
#
|
42
|
-
# @example
|
43
|
-
# SheetsV4.api_object_schemas #=> { 'PersonSchema' => { 'type' => 'object', ... } ... }
|
44
|
-
#
|
45
|
-
# @return [Hash<String, Object>] a hash of schemas keyed by schema name
|
46
|
-
#
|
47
|
-
def call
|
48
|
-
self.class.schema_load_semaphore.synchronize { @call ||= load_api_object_schemas }
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
# Validate
|
54
|
-
# A mutex used to synchronize access to the schemas so they are only loaded
|
55
|
-
# once.
|
56
|
-
#
|
57
|
-
@schema_load_semaphore = Thread::Mutex.new
|
58
|
-
|
59
|
-
class << self
|
60
|
-
# A mutex used to synchronize access to the schemas so they are only loaded once
|
61
|
-
#
|
62
|
-
# @return [Thread::Mutex]
|
63
|
-
#
|
64
|
-
# @api private
|
65
|
-
#
|
66
|
-
attr_reader :schema_load_semaphore
|
67
|
-
end
|
68
|
-
|
69
|
-
# Load the schemas from the Google Discovery API
|
70
|
-
#
|
71
|
-
# @return [Hash<String, Object>] a hash of schemas keyed by schema name
|
72
|
-
#
|
73
|
-
# @api private
|
74
|
-
#
|
75
|
-
def load_api_object_schemas
|
76
|
-
source = 'https://sheets.googleapis.com/$discovery/rest?version=v4'
|
77
|
-
http_response = Net::HTTP.get_response(URI.parse(source))
|
78
|
-
raise_error(http_response) if http_response.code != '200'
|
79
|
-
|
80
|
-
data = http_response.body
|
81
|
-
JSON.parse(data)['schemas'].tap { |schemas| post_process_schemas(schemas) }
|
82
|
-
end
|
83
|
-
|
84
|
-
# Log an error and raise a RuntimeError based on the HTTP response code
|
85
|
-
# @param http_response [Net::HTTPResponse] the HTTP response
|
86
|
-
# @return [void]
|
87
|
-
# @raise [RuntimeError]
|
88
|
-
# @api private
|
89
|
-
def raise_error(http_response)
|
90
|
-
message = "HTTP Error '#{http_response.code}' loading schemas from '#{http_response.uri}'"
|
91
|
-
logger.error(message)
|
92
|
-
raise message
|
93
|
-
end
|
94
|
-
|
95
|
-
REF_KEY = '$ref'
|
96
|
-
|
97
|
-
# A visitor for the schema object tree that fixes up the tree as it goes
|
98
|
-
# @return [void]
|
99
|
-
# @api private
|
100
|
-
def schema_visitor(path:, object:)
|
101
|
-
return unless object.is_a? Hash
|
102
|
-
|
103
|
-
convert_schema_names_to_snake_case(path, object)
|
104
|
-
convert_schema_ids_to_snake_case(path, object)
|
105
|
-
add_unevaluated_properties(path, object)
|
106
|
-
convert_property_names_to_snake_case(path, object)
|
107
|
-
convert_ref_values_to_snake_case(path, object)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Convert schema names to snake case
|
111
|
-
# @return [void]
|
112
|
-
# @api private
|
113
|
-
def convert_schema_names_to_snake_case(path, object)
|
114
|
-
object.transform_keys!(&:underscore) if path.empty?
|
115
|
-
end
|
116
|
-
|
117
|
-
# Convert schema IDs to snake case
|
118
|
-
# @return [void]
|
119
|
-
# @api private
|
120
|
-
def convert_schema_ids_to_snake_case(path, object)
|
121
|
-
object['id'] = object['id'].underscore if object.key?('id') && path.size == 1
|
122
|
-
end
|
123
|
-
|
124
|
-
# Add 'unevaluatedProperties: false' to all schemas
|
125
|
-
# @return [void]
|
126
|
-
# @api private
|
127
|
-
def add_unevaluated_properties(path, object)
|
128
|
-
object['unevaluatedProperties'] = false if path.size == 1
|
129
|
-
end
|
130
|
-
|
131
|
-
# Convert object property names to snake case
|
132
|
-
# @return [void]
|
133
|
-
# @api private
|
134
|
-
def convert_property_names_to_snake_case(path, object)
|
135
|
-
object.transform_keys!(&:underscore) if path[-1] == 'properties'
|
136
|
-
end
|
137
|
-
|
138
|
-
# Convert reference values to snake case
|
139
|
-
# @return [void]
|
140
|
-
# @api private
|
141
|
-
def convert_ref_values_to_snake_case(_path, object)
|
142
|
-
object[REF_KEY] = object[REF_KEY].underscore if object.key?(REF_KEY)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Traverse the schema object tree and apply the schema visitor to each node
|
146
|
-
# @return [void]
|
147
|
-
# @api private
|
148
|
-
def post_process_schemas(schemas)
|
149
|
-
SheetsV4::ApiObjectValidation::TraverseObjectTree.call(
|
150
|
-
object: schemas, visitor: ->(path:, object:) { schema_visitor(path:, object:) }
|
151
|
-
)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SheetsV4
|
4
|
-
module ApiObjectValidation
|
5
|
-
# Resolve a JSON schema reference to a Google Sheets API schema
|
6
|
-
#
|
7
|
-
# This class uses the Google Discovery API to get the schemas. Any schema reference
|
8
|
-
# in the form `{ "$ref": "schema_name" }` will be resolved by looking up the schema
|
9
|
-
# name in the Google Discovery API and returning the schema object (as a Hash).
|
10
|
-
#
|
11
|
-
# This means that `{ "$ref": "cell_data" }` is resolved by returning
|
12
|
-
# `SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call['cell_data']`.
|
13
|
-
#
|
14
|
-
# An RuntimeError is raised if `SheetsV4::ApiObjectValidation::LoadSchemas.new.call`
|
15
|
-
# does not have a key matching the schema name.
|
16
|
-
#
|
17
|
-
# @example
|
18
|
-
# logger = Logger.new(STDOUT, level: Logger::INFO)
|
19
|
-
# ref_resolver = SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
|
20
|
-
# people_schema = { 'type' => 'array', 'items' => { '$ref' => 'person' } }
|
21
|
-
# json_validator = JSONSchemer.schema(people_schema, ref_resolver:)
|
22
|
-
# people_json = [{ 'name' => { 'first' => 'John', 'last' => 'Doe' } }]
|
23
|
-
#
|
24
|
-
# # Trying to validate people_json using json_validator as follows:
|
25
|
-
#
|
26
|
-
# json_validator.validate(people_json)
|
27
|
-
#
|
28
|
-
# # will try to load the referenced schema for 'person'. json_validator will
|
29
|
-
# # do this by calling `ref_resolver.call(URI.parse('json-schemer://schema/person'))`
|
30
|
-
#
|
31
|
-
# @api private
|
32
|
-
#
|
33
|
-
class ResolveSchemaRef
|
34
|
-
# Create a new schema resolver
|
35
|
-
#
|
36
|
-
# @param logger [Logger] the logger to use
|
37
|
-
#
|
38
|
-
# @api private
|
39
|
-
#
|
40
|
-
def initialize(logger: Logger.new(nil))
|
41
|
-
@logger = logger
|
42
|
-
end
|
43
|
-
|
44
|
-
# The logger to use internally
|
45
|
-
#
|
46
|
-
# Currently, only debug messages are logged.
|
47
|
-
#
|
48
|
-
# @return [Logger]
|
49
|
-
#
|
50
|
-
# @api private
|
51
|
-
#
|
52
|
-
attr_reader :logger
|
53
|
-
|
54
|
-
# Resolve a JSON schema reference
|
55
|
-
#
|
56
|
-
# @param ref [URI] the reference to resolve usually in the form "json-schemer://schema/[name]"
|
57
|
-
#
|
58
|
-
# @return [Hash] the schema object as a hash
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
#
|
62
|
-
def call(ref)
|
63
|
-
schema_name = ref.path[1..]
|
64
|
-
logger.debug { "Reading schema #{schema_name}" }
|
65
|
-
schemas = SheetsV4::ApiObjectValidation::LoadSchemas.new(logger:).call
|
66
|
-
schemas[schema_name].tap do |schema_object|
|
67
|
-
raise "Schema for #{ref} not found" unless schema_object
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SheetsV4
|
4
|
-
module ApiObjectValidation
|
5
|
-
# Visit all objects in arbitrarily nested object tree of hashes and/or arrays
|
6
|
-
#
|
7
|
-
# @api public
|
8
|
-
#
|
9
|
-
class TraverseObjectTree
|
10
|
-
# Visit all objects in arbitrarily nested object tree of hashes and/or arrays
|
11
|
-
#
|
12
|
-
# For each object, the visitor is called with the path to the object and the object
|
13
|
-
# itself.
|
14
|
-
#
|
15
|
-
# In the examples below assume the elided code is the following:
|
16
|
-
#
|
17
|
-
# ```Ruby
|
18
|
-
# visitor = -> (path:, object:) { puts "path: #{path}, object: #{obj}" }
|
19
|
-
# SheetsV4::ApiObjectValidation::TraverseObjectTree.call(object:, visitor:)
|
20
|
-
# ```
|
21
|
-
#
|
22
|
-
# @example Given a simple object (not very exciting)
|
23
|
-
# object = 1
|
24
|
-
# ...
|
25
|
-
# #=> path: [], object: 1
|
26
|
-
#
|
27
|
-
# @example Given an array
|
28
|
-
# object = [1, 2, 3]
|
29
|
-
# ...
|
30
|
-
# #=> path: [], object: [1, 2, 3]
|
31
|
-
# #=> path: [0], object: 1
|
32
|
-
# #=> path: [1], object: 2
|
33
|
-
# #=> path: [2], object: 3
|
34
|
-
#
|
35
|
-
# @example Given a hash
|
36
|
-
# object = { name: 'James', age: 42 }
|
37
|
-
# ...
|
38
|
-
# #=> path: [], object: { name: 'James', age: 42 }
|
39
|
-
# #=> path: [:name], object: James
|
40
|
-
# #=> path: [:age], object: 42
|
41
|
-
#
|
42
|
-
# @example Given an array of hashes
|
43
|
-
# object = [{ name: 'James', age: 42 }, { name: 'Jane', age: 43 }]
|
44
|
-
# ...
|
45
|
-
# #=> path: [], object: [{ name: 'James', age: 42 }, { name: 'Jane', age: 43 }]
|
46
|
-
# #=> path: [0], object: { name: 'James', age: 42 }
|
47
|
-
# #=> path: [0, :name], object: James
|
48
|
-
# #=> path: [0, :age], object: 42
|
49
|
-
# #=> path: [1], object: { name: 'Jane', age: 43 }
|
50
|
-
# #=> path: [1, :name], object: Jane
|
51
|
-
# #=> path: [1, :age], object: 43
|
52
|
-
#
|
53
|
-
# @example Given a hash of hashes
|
54
|
-
# object = { person1: { name: 'James', age: 42 }, person2: { name: 'Jane', age: 43 } }
|
55
|
-
# ...
|
56
|
-
# #=> path: [], object: { person1: { name: 'James', age: 42 }, person2: { name: 'Jane', age: 43 } }
|
57
|
-
# #=> path: [:person1], object: { name: 'James', age: 42 }
|
58
|
-
# #=> path: [:person1, :name], object: James
|
59
|
-
# #=> path: [:person1, :age], object: 42
|
60
|
-
# #=> path: [:person2], object: { name: 'Jane', age: 43 }
|
61
|
-
# #=> path: [:person2, :name], object: Jane
|
62
|
-
# #=> path: [:person2, :age], object: 43
|
63
|
-
#
|
64
|
-
# @param path [Array] the path to the object
|
65
|
-
# @param object [Object] the object to visit
|
66
|
-
# @param visitor [#call] the visitor to call for each object
|
67
|
-
#
|
68
|
-
# @return [void]
|
69
|
-
#
|
70
|
-
# @api public
|
71
|
-
#
|
72
|
-
def self.call(object:, visitor:, path: [])
|
73
|
-
visitor&.call(path:, object:)
|
74
|
-
if object.is_a? Hash
|
75
|
-
object.each { |k, obj| call(path: (path + [k]), object: obj, visitor:) }
|
76
|
-
elsif object.is_a? Array
|
77
|
-
object.each_with_index { |obj, k| call(path: (path + [k]), object: obj, visitor:) }
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support'
|
4
|
-
require 'active_support/inflector'
|
5
|
-
require 'json_schemer'
|
6
|
-
|
7
|
-
module SheetsV4
|
8
|
-
module ApiObjectValidation
|
9
|
-
# Validate objects against a Google Sheets API request object schema
|
10
|
-
#
|
11
|
-
# @api public
|
12
|
-
#
|
13
|
-
class ValidateApiObject
|
14
|
-
# Create a new api object validator
|
15
|
-
#
|
16
|
-
# By default, a nil logger is used. This means that no messages are logged.
|
17
|
-
#
|
18
|
-
# @example
|
19
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
|
20
|
-
#
|
21
|
-
# @param logger [Logger] the logger to use
|
22
|
-
#
|
23
|
-
def initialize(logger: Logger.new(nil))
|
24
|
-
@logger = logger
|
25
|
-
end
|
26
|
-
|
27
|
-
# The logger to use internally
|
28
|
-
#
|
29
|
-
# Validation errors are logged at the error level. Other messages are logged
|
30
|
-
# at the debug level.
|
31
|
-
#
|
32
|
-
# @example
|
33
|
-
# logger = Logger.new(STDOUT, :level => Logger::INFO)
|
34
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger)
|
35
|
-
# validator.logger == logger # => true
|
36
|
-
# validator.logger.debug { "Debug message" }
|
37
|
-
#
|
38
|
-
# @return [Logger]
|
39
|
-
#
|
40
|
-
attr_reader :logger
|
41
|
-
|
42
|
-
# Validate the object using the JSON schema named schema_name
|
43
|
-
#
|
44
|
-
# @example
|
45
|
-
# schema_name = 'batch_update_spreadsheet_request'
|
46
|
-
# object = { 'requests' => [] }
|
47
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
|
48
|
-
# validator.call(schema_name:, object:)
|
49
|
-
#
|
50
|
-
# @param schema_name [String] the name of the schema to validate against
|
51
|
-
# @param object [Object] the object to validate
|
52
|
-
#
|
53
|
-
# @raise [RuntimeError] if the object does not conform to the schema
|
54
|
-
#
|
55
|
-
# @return [void]
|
56
|
-
#
|
57
|
-
def call(schema_name:, object:)
|
58
|
-
logger.debug { "Validating #{object} against #{schema_name}" }
|
59
|
-
|
60
|
-
schema = { '$ref' => schema_name }
|
61
|
-
schemer = JSONSchemer.schema(schema, ref_resolver:)
|
62
|
-
errors = schemer.validate(object)
|
63
|
-
raise_error!(schema_name, object, errors) if errors.any?
|
64
|
-
|
65
|
-
logger.debug { "Object #{object} conforms to #{schema_name}" }
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
# The resolver to use to resolve JSON schema references
|
71
|
-
# @return [ResolveSchemaRef]
|
72
|
-
# @api private
|
73
|
-
def ref_resolver = @ref_resolver ||= SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
|
74
|
-
|
75
|
-
# Raise an error when the object does not conform to the schema
|
76
|
-
# @return [void]
|
77
|
-
# @raise [RuntimeError]
|
78
|
-
# @api private
|
79
|
-
def raise_error!(schema_name, object, errors)
|
80
|
-
error = errors.first['error']
|
81
|
-
error_message = "Object #{object} does not conform to #{schema_name}: #{error}"
|
82
|
-
logger.error(error_message)
|
83
|
-
raise error_message
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support'
|
4
|
-
require 'active_support/inflector'
|
5
|
-
require 'json_schemer'
|
6
|
-
|
7
|
-
module SheetsV4
|
8
|
-
module ApiObjectValidation
|
9
|
-
# Validate objects against a Google Sheets API request object schema
|
10
|
-
#
|
11
|
-
# @api public
|
12
|
-
#
|
13
|
-
class ValidateApiObject
|
14
|
-
# Create a new validator
|
15
|
-
#
|
16
|
-
# By default, a nil logger is used. This means that no messages are logged.
|
17
|
-
#
|
18
|
-
# @example
|
19
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
|
20
|
-
#
|
21
|
-
# @param logger [Logger] the logger to use
|
22
|
-
#
|
23
|
-
def initialize(logger: Logger.new(nil))
|
24
|
-
@logger = logger
|
25
|
-
end
|
26
|
-
|
27
|
-
# The logger to use internally
|
28
|
-
#
|
29
|
-
# Validation errors are logged at the error level. Other messages are logged
|
30
|
-
# at the debug level.
|
31
|
-
#
|
32
|
-
# @example
|
33
|
-
# logger = Logger.new(STDOUT, :level => Logger::INFO)
|
34
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new(logger)
|
35
|
-
# validator.logger == logger # => true
|
36
|
-
# validator.logger.debug { "Debug message" }
|
37
|
-
#
|
38
|
-
# @return [Logger]
|
39
|
-
#
|
40
|
-
attr_reader :logger
|
41
|
-
|
42
|
-
# Validate the object using the JSON schema named schema_name
|
43
|
-
#
|
44
|
-
# @example
|
45
|
-
# schema_name = 'batch_update_spreadsheet_request'
|
46
|
-
# object = { 'requests' => [] }
|
47
|
-
# validator = SheetsV4::ApiObjectValidation::ValidateApiObject.new
|
48
|
-
# validator.call(schema_name:, object:)
|
49
|
-
#
|
50
|
-
# @param schema_name [String] the name of the schema to validate against
|
51
|
-
# @param object [Object] the object to validate
|
52
|
-
#
|
53
|
-
# @raise [RuntimeError] if the object does not conform to the schema
|
54
|
-
#
|
55
|
-
# @return [void]
|
56
|
-
#
|
57
|
-
def call(schema_name:, object:)
|
58
|
-
logger.debug { "Validating #{object} against #{schema_name}" }
|
59
|
-
|
60
|
-
schema = { '$ref' => schema_name }
|
61
|
-
schemer = JSONSchemer.schema(schema, ref_resolver:)
|
62
|
-
errors = schemer.validate(object)
|
63
|
-
raise_error!(schema_name, object, errors) if errors.any?
|
64
|
-
|
65
|
-
logger.debug { "Object #{object} conforms to #{schema_name}" }
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
# The resolver to use to resolve JSON schema references
|
71
|
-
# @return [ResolveSchemaRef]
|
72
|
-
# @api private
|
73
|
-
def ref_resolver = @ref_resolver ||= SheetsV4::ApiObjectValidation::ResolveSchemaRef.new(logger:)
|
74
|
-
|
75
|
-
# Raise an error when the object does not conform to the schema
|
76
|
-
# @return [void]
|
77
|
-
# @raise [RuntimeError]
|
78
|
-
# @api private
|
79
|
-
def raise_error!(schema_name, object, errors)
|
80
|
-
error = errors.first['error']
|
81
|
-
error_message = "Object #{object} does not conform to #{schema_name}: #{error}"
|
82
|
-
logger.error(error_message)
|
83
|
-
raise error_message
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|