sunstone 5.0.1.4 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +32 -20
- data/{Rakefile.rb → Rakefile} +0 -0
- data/ext/active_record/finder_methods.rb +1 -1
- data/ext/active_record/persistence.rb +15 -0
- data/ext/active_record/relation.rb +11 -24
- data/ext/active_record/statement_cache.rb +5 -10
- data/lib/active_record/connection_adapters/sunstone/database_statements.rb +38 -2
- data/lib/active_record/connection_adapters/sunstone_adapter.rb +3 -3
- data/lib/arel/visitors/sunstone.rb +1 -1
- data/lib/sunstone/connection.rb +2 -2
- data/lib/sunstone/version.rb +1 -1
- data/sunstone.gemspec +2 -3
- data/test/active_record/query_test.rb +12 -2
- data/test/test_helper.rb +0 -1
- metadata +8 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76178cd8049c5ecd168c637aa48c27118df5d9c4
|
4
|
+
data.tar.gz: 5e5f760949bb9cb27ccea262c5b4c7123ff9fd13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 185c9b5258d7c28a6f6224eb733483bab0e3a1ceeb92a61779c5923a1495a9a45b2fb27fb5fbce9733d5ce7a101d1ac753830c1e8fc59a738db54adc4e2b3248
|
7
|
+
data.tar.gz: e2e93bd51ba9a0d32d5f5e8acc6ed766a1f0ed1d9ef1b3d7df1fe5cd1ff37dc8772302c8e2d0823adcf0bd100911990583be25ad260efbeb5373dfa37b1af7c5
|
data/.travis.yml
CHANGED
@@ -1,43 +1,55 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
rvm:
|
4
|
-
- 2.3.1
|
5
|
-
- 2.4.0
|
6
|
-
|
7
|
-
env:
|
8
|
-
- RAILS_VERSION=v5.0.0
|
9
|
-
- RAILS_VERSION=v5.0.0.1
|
10
|
-
- RAILS_VERSION=v5.0.1
|
2
|
+
sudo: false
|
11
3
|
|
12
4
|
cache:
|
13
5
|
bundler: true
|
14
6
|
directories:
|
15
7
|
- /home/travis/.rvm/gems
|
16
8
|
|
9
|
+
rvm:
|
10
|
+
- 2.4.1
|
11
|
+
|
12
|
+
env:
|
13
|
+
matrix:
|
14
|
+
- RAILS_VERSION=v5.1.0 GEM=ar:mysql2
|
15
|
+
- RAILS_VERSION=v5.1.0 GEM=ar:sqlite3
|
16
|
+
- RAILS_VERSION=v5.1.0 GEM=ar:postgresql
|
17
|
+
- RAILS_VERSION=v5.1.1 GEM=ar:mysql2
|
18
|
+
- RAILS_VERSION=v5.1.1 GEM=ar:sqlite3
|
19
|
+
- RAILS_VERSION=v5.1.1 GEM=ar:postgresql
|
20
|
+
- RAILS_VERSION=v5.1.2 GEM=ar:mysql2
|
21
|
+
- RAILS_VERSION=v5.1.2 GEM=ar:sqlite3
|
22
|
+
- RAILS_VERSION=v5.1.2 GEM=ar:postgresql
|
23
|
+
- RAILS_VERSION=v5.1.3 GEM=ar:mysql2
|
24
|
+
- RAILS_VERSION=v5.1.3 GEM=ar:sqlite3
|
25
|
+
- RAILS_VERSION=v5.1.3 GEM=ar:postgresql
|
26
|
+
|
17
27
|
addons:
|
18
28
|
postgresql: "9.4"
|
19
29
|
|
20
30
|
before_install:
|
21
31
|
- unset BUNDLE_GEMFILE
|
32
|
+
- gem update --system
|
33
|
+
- gem update bundler
|
22
34
|
|
23
35
|
install:
|
24
|
-
- git clone https://github.com/rails/rails.git ~/build/rails
|
36
|
+
- git clone --branch $RAILS_VERSION https://github.com/rails/rails.git ~/build/rails
|
25
37
|
|
26
38
|
before_script:
|
39
|
+
- sed -i "s/t.warning = true/t.warning = false/g" Rakefile
|
27
40
|
- pushd ~/build/rails
|
28
|
-
- git
|
41
|
+
- git status
|
42
|
+
- sed -i "s/Gem.ruby, '-w'/Gem.ruby, '-w0'/" ~/build/rails/activerecord/Rakefile
|
43
|
+
- sed -i "s/t.warning = true/t.warning = false/g" ~/build/rails/activerecord/Rakefile
|
29
44
|
- sed -i "/require 'support\/connection'/a \$LOAD_PATH.unshift\(File.expand_path\('~\/build\/malomalo\/sunstone\/lib'\)\)\nrequire 'sunstone'" ~/build/rails/activerecord/test/cases/helper.rb
|
30
45
|
- cat ~/build/rails/Gemfile
|
31
|
-
-
|
46
|
+
- rm ~/build/rails/Gemfile.lock
|
47
|
+
- "sed -i \"/# Active Record./a gem 'sunstone', path: File.expand_path\\('~\\/build\\/malomalo\\/sunstone'\\)\" ~/build/rails/Gemfile"
|
32
48
|
- cat ~/build/rails/Gemfile
|
33
|
-
- bundle
|
34
|
-
- createdb activerecord_unittest
|
35
|
-
- createdb activerecord_unittest2
|
36
|
-
- mysql -e "create database IF NOT EXISTS activerecord_unittest DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci;"
|
37
|
-
- mysql -e "create database IF NOT EXISTS activerecord_unittest2 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci;"
|
38
|
-
- gem environment gempath
|
49
|
+
- bundle update --jobs=3 --retry=3
|
39
50
|
- popd
|
40
51
|
- bundle install --jobs=3 --retry=3
|
41
|
-
- gem environment gempath
|
42
52
|
|
43
|
-
script:
|
53
|
+
script:
|
54
|
+
- bundle exec rake test
|
55
|
+
- cd ~/build/rails && ci/travis.rb
|
data/{Rakefile.rb → Rakefile}
RENAMED
File without changes
|
@@ -75,6 +75,21 @@ module ActiveRecord
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
def _update_record(attribute_names = self.attribute_names)
|
79
|
+
attributes_values = arel_attributes_with_values_for_update(attribute_names)
|
80
|
+
if attributes_values.empty?
|
81
|
+
rows_affected = 0
|
82
|
+
@_trigger_update_callback = true
|
83
|
+
else
|
84
|
+
rows_affected = self.class.unscoped._update_record(attributes_values, id, id_in_database)
|
85
|
+
@_trigger_update_callback = (rows_affected.is_a?(ActiveRecord::Result) ? rows_affected.rows.size : rows_affected) > 0
|
86
|
+
end
|
87
|
+
|
88
|
+
yield(self) if block_given?
|
89
|
+
|
90
|
+
rows_affected
|
91
|
+
end
|
92
|
+
|
78
93
|
#!!!! TODO: I am duplicated from finder_methods.....
|
79
94
|
def construct(parent, relations, seen, model_cache)
|
80
95
|
relations.each do |key, attributes|
|
@@ -4,49 +4,36 @@ module ActiveRecord
|
|
4
4
|
def to_sql
|
5
5
|
@to_sql ||= begin
|
6
6
|
relation = self
|
7
|
-
|
8
|
-
visitor = if connection.visitor.is_a?(Arel::Visitors::Sunstone)
|
9
|
-
Arel::Visitors::ToSql.new(connection)
|
10
|
-
else
|
11
|
-
connection.visitor
|
12
|
-
end
|
13
|
-
|
7
|
+
|
14
8
|
if eager_loading?
|
15
9
|
find_with_associations { |rel| relation = rel }
|
16
10
|
end
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
collect.substitute_binds(binds).join
|
12
|
+
conn = klass.connection
|
13
|
+
conn.unprepared_statement {
|
14
|
+
conn.to_sql(relation.arel, relation.bound_attributes)
|
15
|
+
}
|
23
16
|
end
|
24
17
|
end
|
25
18
|
|
26
19
|
def to_sar
|
27
20
|
@to_sar ||= begin
|
28
21
|
relation = self
|
29
|
-
|
30
|
-
visitor = if connection.visitor.is_a?(Arel::Visitors::ToSql)
|
31
|
-
Arel::Visitors::Sunstone.new(connection)
|
32
|
-
else
|
33
|
-
connection.visitor
|
34
|
-
end
|
35
|
-
|
22
|
+
|
36
23
|
if eager_loading?
|
37
24
|
find_with_associations { |rel| relation = rel }
|
38
25
|
end
|
39
26
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
collect.compile binds
|
27
|
+
conn = klass.connection
|
28
|
+
conn.unprepared_statement {
|
29
|
+
conn.to_sar(relation.arel, relation.bound_attributes)
|
30
|
+
}
|
45
31
|
end
|
46
32
|
end
|
47
33
|
|
48
34
|
def _update_record(values, id, id_was) # :nodoc:
|
49
35
|
substitutes, binds = substitute_values values
|
36
|
+
|
50
37
|
scope = @klass.unscoped
|
51
38
|
|
52
39
|
if @klass.finder_needs_type_condition?
|
@@ -4,34 +4,29 @@ module ActiveRecord
|
|
4
4
|
|
5
5
|
def initialize(values, sunstone=false)
|
6
6
|
@values = values
|
7
|
-
if sunstone
|
8
|
-
|
7
|
+
@indexes = if sunstone
|
8
|
+
values.value.find_all { |thing|
|
9
9
|
Arel::Nodes::BindParam === thing
|
10
10
|
}
|
11
11
|
else
|
12
|
-
|
12
|
+
values.each_with_index.find_all { |thing,i|
|
13
13
|
Arel::Nodes::BindParam === thing
|
14
14
|
}.map(&:last)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def sql_for(binds, connection)
|
19
|
-
|
19
|
+
casted_binds = binds.map(&:value_for_database)
|
20
20
|
|
21
21
|
if connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
22
22
|
@values.compile(binds)
|
23
23
|
else
|
24
24
|
val = @values.dup
|
25
|
-
@indexes.each { |i| val[i] = connection.quote(
|
25
|
+
@indexes.each { |i| val[i] = connection.quote(casted_binds.shift) }
|
26
26
|
val.join
|
27
27
|
end
|
28
28
|
end
|
29
|
-
end
|
30
29
|
|
31
|
-
def self.partial_query(visitor, ast, collector)
|
32
|
-
collected = visitor.accept(ast, collector)
|
33
|
-
collected = collected.value if !visitor.is_a?(Arel::Visitors::Sunstone)
|
34
|
-
PartialQuery.new(collected, visitor.is_a?(Arel::Visitors::Sunstone))
|
35
30
|
end
|
36
31
|
|
37
32
|
end
|
@@ -1,8 +1,19 @@
|
|
1
|
+
require "arel/collectors/sql_string"
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module Sunstone
|
4
6
|
module DatabaseStatements
|
5
7
|
|
8
|
+
def to_sql(arel, binds = [])
|
9
|
+
if arel.respond_to?(:ast)
|
10
|
+
collected = Arel::Visitors::ToSql.new(self).accept(arel.ast, prepared_statements ? AbstractAdapter::SQLString.new : AbstractAdapter::BindCollector.new)
|
11
|
+
collected.compile(binds, self).freeze
|
12
|
+
else
|
13
|
+
arel.dup.freeze
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
6
17
|
# Converts an arel AST to a Sunstone API Request
|
7
18
|
def to_sar(arel, bvs = [])
|
8
19
|
if arel.respond_to?(:ast)
|
@@ -12,10 +23,22 @@ module ActiveRecord
|
|
12
23
|
arel
|
13
24
|
end
|
14
25
|
end
|
26
|
+
|
27
|
+
def cacheable_query(klass, arel) # :nodoc:
|
28
|
+
collected = visitor.accept(arel.ast, collector)
|
29
|
+
if prepared_statements
|
30
|
+
klass.query(collected.value)
|
31
|
+
else
|
32
|
+
if self.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
|
33
|
+
StatementCache::PartialQuery.new(collected, true)
|
34
|
+
else
|
35
|
+
StatementCache::PartialQuery.new(collected.value, false)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
15
39
|
|
16
40
|
# Returns an ActiveRecord::Result instance.
|
17
41
|
def select_all(arel, name = nil, binds = [], preparable: nil)
|
18
|
-
|
19
42
|
arel, binds = binds_from_relation arel, binds
|
20
43
|
select(arel, name, binds)
|
21
44
|
end
|
@@ -69,7 +92,7 @@ module ActiveRecord
|
|
69
92
|
else
|
70
93
|
send_request.call(arel)
|
71
94
|
end
|
72
|
-
|
95
|
+
|
73
96
|
if sars[0].instance_variable_defined?(:@sunstone_calculation) && sars[0].instance_variable_get(:@sunstone_calculation)
|
74
97
|
# this is a count, min, max.... yea i know..
|
75
98
|
ActiveRecord::Result.new(['all'], [result], {:all => type_map.lookup('integer')})
|
@@ -79,6 +102,19 @@ module ActiveRecord
|
|
79
102
|
ActiveRecord::Result.new(result.keys, [result])
|
80
103
|
end
|
81
104
|
end
|
105
|
+
|
106
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
107
|
+
value = exec_insert(arel, name, binds, pk, sequence_name)
|
108
|
+
id_value || last_inserted_id(value)
|
109
|
+
end
|
110
|
+
|
111
|
+
def update(arel, name = nil, binds = [])
|
112
|
+
exec_update(arel, name, binds)
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete(arel, name = nil, binds = [])
|
116
|
+
exec_delete(arel, name, binds)
|
117
|
+
end
|
82
118
|
|
83
119
|
def last_inserted_id(result)
|
84
120
|
row = result.rows.first
|
@@ -103,7 +103,7 @@ module ActiveRecord
|
|
103
103
|
|
104
104
|
# Executes the delete statement and returns the number of rows affected.
|
105
105
|
def delete(arel, name = nil, binds = [])
|
106
|
-
r = exec_delete(
|
106
|
+
r = exec_delete(arel, name, binds)
|
107
107
|
r.rows.first.to_i
|
108
108
|
end
|
109
109
|
|
@@ -173,8 +173,8 @@ module ActiveRecord
|
|
173
173
|
# If the next id was calculated in advance (as in Oracle), it should be
|
174
174
|
# passed in as +id_value+.
|
175
175
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
176
|
-
sql, binds, pk, sequence_name = sql_for_insert(
|
177
|
-
|
176
|
+
sql, binds, pk, sequence_name = sql_for_insert(arel, pk, id_value, sequence_name, binds)
|
177
|
+
exec_insert(sql, name, binds, pk, sequence_name)
|
178
178
|
end
|
179
179
|
alias create insert
|
180
180
|
|
data/lib/sunstone/connection.rb
CHANGED
@@ -128,7 +128,7 @@ module Sunstone
|
|
128
128
|
elsif Thread.current[:sunstone_request_sent]
|
129
129
|
log_mess = request.path.split('?', 2)
|
130
130
|
log_mess += Thread.current[:sunstone_request_sent].path.split('?', 2)
|
131
|
-
raise <<~MSG
|
131
|
+
raise ActiveRecord::StatementInvalid, <<~MSG
|
132
132
|
Cannot send multiple request in a transaction.
|
133
133
|
|
134
134
|
Trying to send:
|
@@ -139,7 +139,7 @@ module Sunstone
|
|
139
139
|
MSG
|
140
140
|
else
|
141
141
|
log_mess = request.path.split('?', 2)
|
142
|
-
raise <<~MSG
|
142
|
+
raise ActiveRecord::StatementInvalid, <<~MSG
|
143
143
|
Cannot send multiple request in a transaction.
|
144
144
|
|
145
145
|
Trying to send:
|
data/lib/sunstone/version.rb
CHANGED
data/sunstone.gemspec
CHANGED
@@ -29,11 +29,10 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_development_dependency 'rgeo'
|
30
30
|
s.add_development_dependency 'simplecov'
|
31
31
|
s.add_development_dependency 'byebug'
|
32
|
-
s.add_development_dependency 'activesupport', '~> 5.0
|
32
|
+
s.add_development_dependency 'activesupport', '~> 5.1.0'
|
33
33
|
|
34
34
|
# Runtime
|
35
35
|
s.add_runtime_dependency 'msgpack'
|
36
36
|
s.add_runtime_dependency 'cookie_store'
|
37
|
-
s.add_runtime_dependency '
|
38
|
-
s.add_runtime_dependency 'activerecord', '~> 5.0.1'
|
37
|
+
s.add_runtime_dependency 'activerecord', '~> 5.1.0'
|
39
38
|
end
|
@@ -28,6 +28,16 @@ class ActiveRecord::QueryTest < ActiveSupport::TestCase
|
|
28
28
|
|
29
29
|
assert_nil Ship.first
|
30
30
|
end
|
31
|
+
|
32
|
+
test '::first!' do
|
33
|
+
webmock(:get, "/ships", { limit: 1, order: [{id: :asc}] }).to_return({
|
34
|
+
body: [].to_json
|
35
|
+
})
|
36
|
+
|
37
|
+
assert_raises ActiveRecord::RecordNotFound do
|
38
|
+
Ship.first!
|
39
|
+
end
|
40
|
+
end
|
31
41
|
|
32
42
|
test '::last' do
|
33
43
|
webmock(:get, "/ships", { limit: 1, order: [{id: :desc}] }).to_return({
|
@@ -94,13 +104,13 @@ class ActiveRecord::QueryTest < ActiveSupport::TestCase
|
|
94
104
|
end
|
95
105
|
|
96
106
|
test '#to_sar' do
|
97
|
-
assert_equal "/ships?%81%A5where%81%A2id%
|
107
|
+
assert_equal "/ships?%81%A5where%81%A2id%0A", Ship.where(:id => 10).to_sar.path
|
98
108
|
end
|
99
109
|
|
100
110
|
test 'bind params get eaten when joining' do
|
101
111
|
uri = URI(Ship.joins(:ownerships).where({ ownerships: { id: 1 } }).to_sar.path)
|
102
112
|
query = MessagePack.unpack(CGI.unescape(uri.query))
|
103
|
-
assert_equal({"where"=>{"ownerships"=>{"id"=>{"eq"=>
|
113
|
+
assert_equal({"where"=>{"ownerships"=>{"id"=>{"eq"=>1}}}}, query)
|
104
114
|
end
|
105
115
|
|
106
116
|
end
|
data/test/test_helper.rb
CHANGED
@@ -68,7 +68,6 @@ class ActiveSupport::TestCase
|
|
68
68
|
query = deep_transform_query(query) if query
|
69
69
|
|
70
70
|
stub_request(method, /^#{ActiveRecord::Base.connection.instance_variable_get(:@connection).url}/).with do |req|
|
71
|
-
# puts "REQ\t#{unpack(req.uri.query.sub(/=true$/, ''))}" if req&.uri&.path == path && req.uri.query
|
72
71
|
if query
|
73
72
|
req&.uri&.path == path && req.uri.query && unpack(req.uri.query.sub(/=true$/, '')) == query
|
74
73
|
else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sunstone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -184,14 +184,14 @@ dependencies:
|
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 5.0
|
187
|
+
version: 5.1.0
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 5.0
|
194
|
+
version: 5.1.0
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: msgpack
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,34 +220,20 @@ dependencies:
|
|
220
220
|
- - ">="
|
221
221
|
- !ruby/object:Gem::Version
|
222
222
|
version: '0'
|
223
|
-
- !ruby/object:Gem::Dependency
|
224
|
-
name: arel
|
225
|
-
requirement: !ruby/object:Gem::Requirement
|
226
|
-
requirements:
|
227
|
-
- - "~>"
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: '7.0'
|
230
|
-
type: :runtime
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
requirements:
|
234
|
-
- - "~>"
|
235
|
-
- !ruby/object:Gem::Version
|
236
|
-
version: '7.0'
|
237
223
|
- !ruby/object:Gem::Dependency
|
238
224
|
name: activerecord
|
239
225
|
requirement: !ruby/object:Gem::Requirement
|
240
226
|
requirements:
|
241
227
|
- - "~>"
|
242
228
|
- !ruby/object:Gem::Version
|
243
|
-
version: 5.0
|
229
|
+
version: 5.1.0
|
244
230
|
type: :runtime
|
245
231
|
prerelease: false
|
246
232
|
version_requirements: !ruby/object:Gem::Requirement
|
247
233
|
requirements:
|
248
234
|
- - "~>"
|
249
235
|
- !ruby/object:Gem::Version
|
250
|
-
version: 5.0
|
236
|
+
version: 5.1.0
|
251
237
|
description: A library for interacting with REST APIs. Similar to ActiveResource
|
252
238
|
email:
|
253
239
|
- jonbracy@gmail.com
|
@@ -261,7 +247,7 @@ files:
|
|
261
247
|
- Gemfile
|
262
248
|
- LICENSE
|
263
249
|
- README.md
|
264
|
-
- Rakefile
|
250
|
+
- Rakefile
|
265
251
|
- TODO.md
|
266
252
|
- ext/active_record/associations.rb
|
267
253
|
- ext/active_record/associations/association.rb
|
@@ -337,7 +323,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
337
323
|
version: '0'
|
338
324
|
requirements: []
|
339
325
|
rubyforge_project:
|
340
|
-
rubygems_version: 2.6.
|
326
|
+
rubygems_version: 2.6.11
|
341
327
|
signing_key:
|
342
328
|
specification_version: 4
|
343
329
|
summary: A library for interacting with REST APIs
|