zilla 0.1.1 → 0.1.3

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: 3cfb7c21cb7e5af74461ab0c8caeb711368a7f8548e228fad039578dc3d1e42b
4
- data.tar.gz: d2f8730e425912ab9ba43cdfb19190e59b446d64c123d84dfc392a7fd2bf2e02
3
+ metadata.gz: 67835482c78ededdf96d80a81e748354240776a1b8d696e3f8ea61a2e9a07a00
4
+ data.tar.gz: 1bac88fb8c3db62b72163318331f770e7145b4b55b7792420bf89cf2132f2452
5
5
  SHA512:
6
- metadata.gz: aa3973517ead3d94119eeeefaa0549c47697f1f5ea4c1f5262c4bcfed55808dce4dcbcfac11b362f9b6465fd0aeee3eb3f0c3a1b4ab221e9a418628834c11ddd
7
- data.tar.gz: 856bcf5b69ece2888f4b1b157cada1307b6cad333970581cf43a449cf2fb50c6cd3e59d6b794c027a16d2b9771572c79560f635741b168fa97926d9c2d28480d
6
+ metadata.gz: 646c19b46599f6cfb5791493a5a410e7702a1a706ffa1a0b7e919c56a6b2fb88b07b5f2e2111700bc344533e6810a96023dd627dcb4f1cad27306b5071954592
7
+ data.tar.gz: 71e3293b3e08583dd862faaf95550fbb9c701c7ddfde921ad1bca13e573127191d7dcd55e0db558c4a5632798fd2152d9222aeec8f04cdf85bf88cafe024d57c
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format progress
2
2
  --color
3
3
  --require spec_helper
4
+ --order random
data/.rubocop.yml CHANGED
@@ -44,4 +44,4 @@ Style/ClassAndModuleChildren:
44
44
  EnforcedStyle: compact
45
45
 
46
46
  RSpec/NestedGroups:
47
- Max: 5
47
+ Max: 6
data/Gemfile CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in async-openapi.gemspec
6
5
  gemspec
7
6
 
8
7
  gem "rake", "~> 13.0"
@@ -13,7 +12,6 @@ gem "rubocop", "~> 1.21"
13
12
  gem "rubocop-performance"
14
13
  gem "rubocop-rspec"
15
14
 
16
- gem "async-rspec"
17
15
  gem "simplecov"
18
16
  gem "webmock"
19
17
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- zilla (0.1.1)
5
- async (~> 2.3.0)
6
- async-http-faraday (~> 0.11)
4
+ zilla (0.1.3)
7
5
  faraday (~> 2.7.0)
8
6
  json_schemer (~> 0.2.0)
9
7
  memery (~> 1.4.0)
@@ -16,34 +14,9 @@ GEM
16
14
  addressable (2.8.1)
17
15
  public_suffix (>= 2.0.2, < 6.0)
18
16
  ast (2.4.2)
19
- async (2.3.1)
20
- console (~> 1.10)
21
- io-event (~> 1.1)
22
- timers (~> 4.1)
23
- async-http (0.59.4)
24
- async (>= 1.25)
25
- async-io (>= 1.28)
26
- async-pool (>= 0.2)
27
- protocol-http (~> 0.23.1)
28
- protocol-http1 (~> 0.14.0)
29
- protocol-http2 (~> 0.14.0)
30
- traces (>= 0.8.0)
31
- async-http-faraday (0.11.0)
32
- async-http (~> 0.42)
33
- faraday
34
- async-io (1.34.1)
35
- async
36
- async-pool (0.3.12)
37
- async (>= 1.25)
38
- async-rspec (1.16.1)
39
- rspec (~> 3.0)
40
- rspec-files (~> 1.0)
41
- rspec-memory (~> 1.0)
42
17
  backport (1.2.0)
43
18
  benchmark (0.2.1)
44
19
  childprocess (4.1.0)
45
- console (1.16.2)
46
- fiber-local
47
20
  crack (0.4.5)
48
21
  rexml
49
22
  diff-lcs (1.5.0)
@@ -55,11 +28,9 @@ GEM
55
28
  faraday-net_http (>= 2.0, < 3.1)
56
29
  ruby2_keywords (>= 0.0.4)
57
30
  faraday-net_http (3.0.2)
58
- fiber-local (1.0.0)
59
31
  hana (1.3.7)
60
32
  hashdiff (1.0.1)
61
33
  iniparse (1.5.0)
62
- io-event (1.1.6)
63
34
  jaro_winkler (1.5.4)
64
35
  json (2.6.3)
65
36
  json_schemer (0.2.24)
@@ -83,13 +54,6 @@ GEM
83
54
  parallel (1.22.1)
84
55
  parser (3.2.0.0)
85
56
  ast (~> 2.4.1)
86
- protocol-hpack (1.4.2)
87
- protocol-http (0.23.12)
88
- protocol-http1 (0.14.6)
89
- protocol-http (~> 0.22)
90
- protocol-http2 (0.14.2)
91
- protocol-hpack (~> 1.4)
92
- protocol-http (~> 0.18)
93
57
  public_suffix (5.0.1)
94
58
  racc (1.6.2)
95
59
  rainbow (3.1.1)
@@ -107,10 +71,6 @@ GEM
107
71
  rspec-expectations (3.12.2)
108
72
  diff-lcs (>= 1.2.0, < 2.0)
109
73
  rspec-support (~> 3.12.0)
110
- rspec-files (1.1.3)
111
- rspec (~> 3.0)
112
- rspec-memory (1.0.3)
113
- rspec (~> 3.0)
114
74
  rspec-mocks (3.12.3)
115
75
  diff-lcs (>= 1.2.0, < 2.0)
116
76
  rspec-support (~> 3.12.0)
@@ -160,8 +120,6 @@ GEM
160
120
  yard (~> 0.9, >= 0.9.24)
161
121
  thor (1.2.1)
162
122
  tilt (2.0.11)
163
- timers (4.3.5)
164
- traces (0.8.0)
165
123
  unicode-display_width (2.4.2)
166
124
  uri_template (0.7.0)
167
125
  webmock (3.18.1)
@@ -177,7 +135,6 @@ PLATFORMS
177
135
  x86_64-linux
178
136
 
179
137
  DEPENDENCIES
180
- async-rspec
181
138
  overcommit
182
139
  rake (~> 13.0)
183
140
  rspec (~> 3.0)
@@ -8,15 +8,15 @@ module Zilla::ClientFactory
8
8
  }.freeze
9
9
 
10
10
  class << self
11
- def build(input)
12
- definition = Zilla::LoaderFactory.build(input).load
11
+ def build(input, host: nil, scheme: nil, &block)
12
+ definition = Zilla::LoaderFactory.build(input, &block).load
13
13
 
14
14
  version = definition["swagger"]
15
15
 
16
16
  raise ArgumentError, "#{input.inspect} is not an OpenAPI definition" if version.nil?
17
17
  raise UnsupportedVersion, "usupported version #{version.inspect}" if version && CLIENTS[version].nil?
18
18
 
19
- CLIENTS[version].new(definition)
19
+ CLIENTS[version].new(definition, host:, scheme:, faraday_config_block: block)
20
20
  end
21
21
  end
22
22
  end
@@ -4,13 +4,13 @@ module Zilla::LoaderFactory
4
4
  class UnknownLoader < Zilla::Error; end
5
5
 
6
6
  class << self
7
- def build(input)
7
+ def build(input, &block)
8
8
  case input
9
9
  when Hash
10
10
  Zilla::Loaders::Hash
11
11
  when String
12
12
  for_string(input)
13
- end.new(input)
13
+ end.new(input, faraday_config_block: block)
14
14
  rescue NoMethodError
15
15
  raise UnknownLoader, "cannot find loader for #{input.inspect}"
16
16
  end
@@ -3,7 +3,7 @@
3
3
  class Zilla::Loaders::HTTP < Zilla::Loaders::Loader
4
4
  def load_string
5
5
  Faraday.new(input) do |f|
6
- f.adapter :async_http
6
+ @faraday_config_block.call(f, :loader)
7
7
  f.response :raise_error
8
8
  end.get.body
9
9
  end
@@ -5,8 +5,9 @@ class Zilla::Loaders::Loader
5
5
 
6
6
  attr_reader :input
7
7
 
8
- def initialize(input)
8
+ def initialize(input, faraday_config_block: nil)
9
9
  @input = input
10
+ @faraday_config_block = faraday_config_block || ->(_f, _target) {}
10
11
  end
11
12
 
12
13
  def load
data/lib/zilla/version.rb CHANGED
@@ -4,5 +4,5 @@ module Zilla
4
4
  module Version
5
5
  end
6
6
 
7
- VERSION = "0.1.1"
7
+ VERSION = "0.1.3"
8
8
  end
@@ -5,14 +5,15 @@ class Zilla::Versions::V20::Client
5
5
 
6
6
  include Memery
7
7
 
8
- attr_reader :json, :host, :scheme
8
+ attr_reader :json, :host, :scheme, :faraday_config_block
9
9
 
10
- def initialize(json, host: nil, scheme: :https)
10
+ def initialize(json, host: nil, scheme: nil, faraday_config_block: nil) # rubocop:disable Metrics/AbcSize
11
11
  @json = json
12
12
  @host = host || api.host || raise(ArgumentError, ":host must be specified")
13
- @scheme = scheme.to_s
13
+ @scheme = (scheme || :https).to_s
14
+ @faraday_config_block = faraday_config_block || ->(_f, _target) {}
14
15
 
15
- unless api.schemes.include?(@scheme)
16
+ if api.schemes && !api.schemes.include?(@scheme)
16
17
  raise ArgumentError, "unsupported scheme #{@scheme.inspect}. Supported: #{api.schemes}"
17
18
  end
18
19
 
@@ -21,7 +22,7 @@ class Zilla::Versions::V20::Client
21
22
 
22
23
  memoize def api = API.new(json)
23
24
 
24
- memoize def executor = Executor.new(scheme, host)
25
+ memoize def executor = Executor.new(scheme, host, faraday_config_block:)
25
26
 
26
27
  private
27
28
 
@@ -8,6 +8,8 @@ class Zilla::Versions::V20::Endpoint
8
8
  attr_reader :path, :method, :json, :definitions
9
9
 
10
10
  def initialize(path, method, json, definitions: {})
11
+ raise ArgumentError, "json must be a Hash, given: #{json.class.inspect}" unless json.instance_of?(Hash)
12
+
11
13
  @path = path
12
14
  @method = method
13
15
  @json = json
@@ -23,4 +25,5 @@ class Zilla::Versions::V20::Endpoint
23
25
  memoize def path_template = PathTemplate.new(path)
24
26
 
25
27
  memoize def parameters = Parameters.new(json["parameters"], definitions:)
28
+ memoize def responses = json["responses"].transform_values { |v| JSONSchemer.schema(v) }
26
29
  end
@@ -5,24 +5,26 @@ class Zilla::Versions::V20::Executor
5
5
 
6
6
  attr_reader :scheme, :host
7
7
 
8
- def initialize(scheme, host)
8
+ def initialize(scheme, host, faraday_config_block:)
9
9
  @scheme = scheme
10
10
  @host = host
11
+ @faraday_config_block = faraday_config_block || ->(_, _target) {}
11
12
  end
12
13
 
13
14
  def call(endpoint, *args, **params)
14
- path_parameters = endpoint.path_template.path_parameters(*args, **params)
15
- validate_path_params!(endpoint, path_parameters)
15
+ normalized = endpoint.parameters.normalize!(*args, **params)
16
16
 
17
- path = endpoint.path_template.render(*args, **path_parameters)
18
- connection.public_send(endpoint.method, path)
17
+ path = endpoint.path_template.render(*args, **normalized[:path])
18
+ response = connection.public_send(endpoint.method, path, **normalized[:query])
19
+
20
+ validate_response!(endpoint, response)
19
21
  end
20
22
 
21
23
  private
22
24
 
23
25
  memoize def connection
24
26
  Faraday.new("#{scheme}://#{host}") do |f|
25
- f.adapter :async_http
27
+ @faraday_config_block.call(f, :client)
26
28
  f.response :raise_error
27
29
  end
28
30
  end
@@ -32,4 +34,14 @@ class Zilla::Versions::V20::Executor
32
34
  endpoint.parameters[k].validate!(v)
33
35
  end
34
36
  end
37
+
38
+ def validate_response!(endpoint, response)
39
+ schema = endpoint.responses[response.status.to_s]
40
+ parsed_body = JSON.parse(response.body)
41
+ errors = schema.validate(parsed_body)
42
+
43
+ raise ArgumentError, errors.to_a.to_s if errors.any?
44
+
45
+ parsed_body
46
+ end
35
47
  end
@@ -16,6 +16,9 @@ class Zilla::Versions::V20::Parameter
16
16
  end
17
17
  end
18
18
 
19
+ def in?(location) = json["in"] == location
20
+ def required? = !!@json["required"]
21
+
19
22
  def validate!(value)
20
23
  errors = schema.validate(value)
21
24
 
@@ -4,6 +4,15 @@ class Zilla::Versions::V20::Parameters
4
4
  include Zilla::Versions::V20
5
5
 
6
6
  include Memery
7
+ include Enumerable
8
+
9
+ LOCATIONS = {
10
+ path: "path",
11
+ query: "query",
12
+ body: "body",
13
+ header: "header",
14
+ form_data: "formData"
15
+ }.freeze
7
16
 
8
17
  attr_reader :json, :definitions
9
18
 
@@ -12,13 +21,75 @@ class Zilla::Versions::V20::Parameters
12
21
  @definitions = definitions
13
22
  end
14
23
 
24
+ def normalize!(*args, **params)
25
+ normalized = LOCATIONS.keys.inject({}) do |acc, location|
26
+ acc.merge(location => send("normalize_#{location}_params!", args, params))
27
+ end
28
+ validate!(normalized, params)
29
+ end
30
+
15
31
  memoize def parameters
16
32
  json.map { Parameter.new(_1, definitions:) }
17
33
  .group_by(&:name)
18
- .transform_values(&:first)
34
+ .transform_values do |params|
35
+ raise(ArgumentError, "parameter names must be unique: #{params}") if params.count > 1
36
+
37
+ params.first
38
+ end
39
+ end
40
+
41
+ LOCATIONS.each do |name, key|
42
+ memoize(define_method("#{name}_parameters") do
43
+ parameters.filter { |_k, param| param.in?(key) }
44
+ end)
19
45
  end
20
46
 
21
- def empty? = parameters.empty?
22
47
  def names = parameters.keys
23
48
  def [](name) = parameters[name]
49
+ def each(*, **, &) = parameters.each(*, **, &)
50
+
51
+ private
52
+
53
+ def normalize_path_params!(args, params)
54
+ params = params.transform_keys(&:to_s).slice(*path_parameters.keys)
55
+
56
+ if args.any?
57
+ raise ArgumentError, "path params must be passed EITHER in args OR in params" if params.any?
58
+
59
+ params = normalize_path_args(args)
60
+ end
61
+
62
+ return params if params.count == path_parameters.count
63
+
64
+ raise ArgumentError,
65
+ "there must be exactly #{path_parameters.count} path parameters in params. Given: #{params.count}"
66
+ end
67
+
68
+ def normalize_query_params!(_args, params) = params.transform_keys(&:to_s).slice(*query_parameters.keys)
69
+ def normalize_body_params!(_args, _params) = {}
70
+ def normalize_form_data_params!(_args, _params) = {}
71
+ def normalize_header_params!(_args, _params) = {}
72
+
73
+ def validate!(normalized, params)
74
+ params.each_key do |k|
75
+ raise ArgumentError, "unknown parameter #{k.inspect}" unless parameters[k.to_s]
76
+ end
77
+
78
+ parameters.each_value do |parameter|
79
+ p = normalized.dig(parameter.in.to_sym, parameter.name)
80
+ parameter.validate!(p) if parameter.required? || p
81
+ end
82
+ normalized
83
+ end
84
+
85
+ def normalize_path_args(args)
86
+ if args.count == path_parameters.count
87
+ return path_parameters.each_key.with_index.with_object({}) do |(k, i), acc|
88
+ acc[k] = args[i]
89
+ end
90
+ end
91
+
92
+ raise ArgumentError,
93
+ "there must be exactly #{path_parameters.count} path parameters in args. Given: #{args.count}"
94
+ end
24
95
  end
@@ -5,6 +5,8 @@ class Zilla::Versions::V20::Path
5
5
 
6
6
  include Memery
7
7
 
8
+ VERBS = ["get", "post", "put", "patch", "delete", "head", "options"].freeze
9
+
8
10
  attr_reader :path, :json, :definitions
9
11
 
10
12
  def initialize(path, json, definitions: {})
@@ -14,7 +16,13 @@ class Zilla::Versions::V20::Path
14
16
  end
15
17
 
16
18
  memoize def endpoints
19
+ parameters = json.fetch("parameters", []) # weird kubernetes API definition
20
+
17
21
  json.each_with_object({}) do |(k, v), acc|
22
+ next unless VERBS.include?(k)
23
+
24
+ v = v.merge("parameters" => v.fetch("parameters", []) + parameters)
25
+
18
26
  acc[k] = Endpoint.new(path, k, v, definitions:)
19
27
  end
20
28
  end
@@ -28,7 +28,7 @@ class Zilla::Versions::V20::PathTemplate
28
28
 
29
29
  return validate!(params.transform_keys(&:to_s)) if params.any?
30
30
 
31
- @parameters.keys.each.with_index.with_object({}) do |(k, i), acc|
31
+ @parameters.each_key.with_index.with_object({}) do |(k, i), acc|
32
32
  acc[k] = args[i]
33
33
  end
34
34
  end
data/lib/zilla.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "async"
4
- require "async/http/faraday"
5
3
  require "faraday"
6
4
  require "oj"
7
5
  require "zeitwerk"
@@ -15,9 +13,12 @@ loader.inflector.inflect(
15
13
  "api" => "API"
16
14
  )
17
15
 
18
- # Your code goes here...
19
- module Zilla # rubocop:disable Style/ClassAndModuleChildren
16
+ module Zilla
20
17
  class Error < StandardError; end
18
+
19
+ def self.for(input, host: nil, scheme: nil, &)
20
+ Zilla::ClientFactory.build(input, host:, scheme:, &)
21
+ end
21
22
  end
22
23
 
23
24
  loader.setup
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zilla
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gleb Sinyavskiy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-29 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: async
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 2.3.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 2.3.0
27
- - !ruby/object:Gem::Dependency
28
- name: async-http-faraday
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.11'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.11'
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: faraday
43
15
  requirement: !ruby/object:Gem::Requirement