zaikio-client-helpers 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3409eab2392246cb7990f31dff3d24b47fa8616b150ad217ed0bb5a7f89607a9
4
+ data.tar.gz: cefdc3ec682204a0552484e0d37ad06dda3449ff05c0ec00637aa4b85c695415
5
+ SHA512:
6
+ metadata.gz: a0b859d93659e155556e14c36b66b7ab2fbadcea7922f226b431e34d13668104144441239de59656055f321b92d84fc246ce1fe60f295f82d57a5f5879d7360a
7
+ data.tar.gz: e420427ff71df44f79ef37fa59ccb54a22c2becf3863021ce80b2a3164806c8336c0701ce0809f7571e9e7efd5c66dac07829adf861e15e4410900ac0632ac0f
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-06-17
4
+
5
+ - Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Zaikio GmbH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Zaikio::Client::Helpers
2
+
3
+ A small gem which includes helpers for parsing & pagination when working with
4
+ Zaikio APIs using the [Spyke] library. Parts of this are referenced in:
5
+
6
+ * [Zaikio Hub Client]
7
+ * [Zaikio Procurement Client]
8
+
9
+ [Spyke]: https://github.com/balvig/spyke
10
+ [Zaikio Hub Client]: https://github.com/zaikio/zaikio-hub-ruby
11
+ [Zaikio Procurement Client]: https://github.com/zaikio/zaikio-procurement-ruby
12
+
13
+ ## Installation
14
+
15
+ 1. Add to your gemspec or Gemfile:
16
+
17
+ ```ruby
18
+ spec.add_dependency "zaikio-client-helpers"
19
+ ```
20
+
21
+ 2. Require it when your code is loaded:
22
+
23
+ ```ruby
24
+ require "zaikio-client-helpers"
25
+ ```
26
+
27
+ 3. Extend the base class for your models:
28
+
29
+ ```ruby
30
+ class User < Zaikio::Client::Model
31
+ # ...
32
+ end
33
+ ```
34
+
35
+ ## Parsing API responses
36
+
37
+ Spyke needs a couple of extra things in addition to JSON parsing, so this library exposes
38
+ a Spyke-compatible JSON parser that raises the correct exceptions when things go wrong:
39
+
40
+ ```ruby
41
+ Faraday.new do |f|
42
+ f.use Zaikio::Client::Helpers::JSONParser
43
+ ...
44
+ end
45
+ ```
46
+
47
+ > Note: if you're also using the pagination middleware below, you should set this
48
+ > middleware _after_ the pagination middleware, because it needs to run before pagination
49
+ > can happen.
50
+
51
+ ## Automatic pagination
52
+
53
+ First, we need to configure the Faraday middleware, wherever the Faraday::Connection is
54
+ constructed:
55
+
56
+ ```ruby
57
+ Faraday.new do |f|
58
+ f.use Zaikio::Client::Helpers::Pagination::FaradayMiddleware
59
+ f.use Zaikio::Client::Helpers::JSONParser
60
+ ...
61
+ end
62
+ ```
63
+
64
+ If you aren't inheriting `Zaikio::Client::Model`, you should mixin our pagination library
65
+ like so: `include Zaikio::Client::Helpers::Pagination`.
66
+
67
+ The library works by overriding the `#all` method on a relation, so it will keep fetching
68
+ pages from the remote API until there are none left:
69
+
70
+ ```ruby
71
+ Model.all.map(&:id)
72
+ #=> There are 3 pages, I will load more pages automatically
73
+ #=> [1,2,3]
74
+ ```
75
+
76
+ If you wish to opt-out of automatic pagination, you can use the Lazy version (don't forget
77
+ to call `each` or `to_a` to materialize the records):
78
+
79
+ ```ruby
80
+ Model.all.lazy.take(2).map(&:id).to_a
81
+ ```
82
+
83
+ Note that if the endpoint doesn't support pagination (i.e. doesn't return a `Current-Page`
84
+ header), pagination is automatically disabled.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "zaikio/client/helpers/version"
4
+ require_relative "zaikio/client/helpers/json_parser"
5
+ require_relative "zaikio/client/helpers/pagination"
@@ -0,0 +1,33 @@
1
+ require "faraday"
2
+ require "multi_json"
3
+
4
+ module Zaikio::Client::Helpers
5
+ class JSONParser < Faraday::Response::Middleware
6
+ def on_complete(env)
7
+ connection_error(env) unless /^(2\d\d)|422|404$/.match?(env.status.to_s)
8
+
9
+ raise Spyke::ResourceNotFound if env.status.to_s == "404"
10
+
11
+ env.body = parse_body(env.body)
12
+ end
13
+
14
+ def connection_error(env)
15
+ raise Spyke::ConnectionError, "Status: #{env.status}, URL: #{env.url}, body: #{env.body}"
16
+ end
17
+
18
+ def parse_body(body)
19
+ json = MultiJson.load(body, symbolize_keys: true)
20
+ {
21
+ data: json,
22
+ metadata: {},
23
+ errors: json.is_a?(Hash) ? json[:errors] : {}
24
+ }
25
+ rescue MultiJson::ParseError
26
+ {
27
+ data: {},
28
+ metadata: {},
29
+ errors: {}
30
+ }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,111 @@
1
+ require "faraday"
2
+ require "spyke"
3
+
4
+ module Zaikio::Client::Helpers
5
+ module Pagination
6
+ HEADERS = {
7
+ total_count: "Total-Count",
8
+ total_pages: "Total-Pages",
9
+ current_page: "Current-Page"
10
+ }.freeze
11
+
12
+ METADATA_KEY = :pagination
13
+
14
+ # Faraday Middleware for extracting any pagination headers into the a top-level
15
+ # :metadata hash, or the env hash for non-JSON responses.
16
+ #
17
+ # @example Usage
18
+ # conn = Faraday.new do |f|
19
+ # f.use Zaikio::Client::Helpers::Pagination::FaradayMiddleware
20
+ # end
21
+ #
22
+ # response = conn.get("/")
23
+ # response.env[METADATA_KEY]
24
+ # #=> {total_count: 4, total_pages: 1, current_page: 1}
25
+ class FaradayMiddleware < Faraday::Response::Middleware
26
+ def on_complete(env)
27
+ @env = env
28
+
29
+ metadata = HEADERS.transform_values do |key|
30
+ header(key)
31
+ end
32
+
33
+ if env.body.is_a?(Hash)
34
+ @env.body[:metadata] ||= {}
35
+ @env.body[:metadata][METADATA_KEY] = metadata
36
+ else
37
+ @env[METADATA_KEY] = metadata
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def header(key)
44
+ value = @env.response_headers[key]
45
+ value.try(:to_i)
46
+ end
47
+ end
48
+
49
+ extend ActiveSupport::Concern
50
+
51
+ included do
52
+ scope :page, ->(value) { where(page: value) }
53
+ scope :per_page, ->(value) { where(per_page: value) }
54
+ end
55
+
56
+ module ClassMethods
57
+ # Overrides the method included by Spyke::Scoping to return paginated relations.
58
+ def all
59
+ current_scope || Relation.new(self, uri: uri)
60
+ end
61
+ end
62
+
63
+ class Relation < Spyke::Relation
64
+ HEADERS.each_key do |symbol|
65
+ define_method(symbol) do
66
+ find_some.metadata[METADATA_KEY][symbol]
67
+ end
68
+ end
69
+
70
+ def first_page?
71
+ current_page == 1
72
+ end
73
+
74
+ def next_page
75
+ current_page + 1
76
+ end
77
+
78
+ def last_page?
79
+ current_page >= total_pages
80
+ end
81
+
82
+ def supports_pagination?
83
+ current_page.present?
84
+ end
85
+
86
+ # Unlike the default implementation in Spyke, this version of #each is recursive
87
+ # and will repeatedly paginate through the remote API until it runs out of
88
+ # records.
89
+ #
90
+ # To avoid this behaviour, you can ask for a Lazy enumerator to take just the
91
+ # records you need, e.g.:
92
+ # User.all.lazy.take(3).to_a
93
+ # #=> [.., .., ..]
94
+ def each(&block)
95
+ return to_enum(:each) unless block_given?
96
+
97
+ find_some.each(&block)
98
+ return if !supports_pagination? || last_page?
99
+
100
+ puts "There are #{total_count} pages, I will load more pages automatically" if first_page?
101
+ clone.page(next_page).each(&block)
102
+ end
103
+
104
+ def clone
105
+ # We use cloning when fetching a second page using the same scope/query, however
106
+ # we want to clear any loaded records from @find_some before doing so.
107
+ super.tap { |obj| obj.instance_variable_set(:@find_some, nil) }
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zaikio
4
+ module Client
5
+ module Helpers
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "helpers/pagination"
2
+
3
+ module Zaikio
4
+ module Client
5
+ class Model < Spyke::Base
6
+ include Helpers::Pagination
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zaikio-client-helpers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Zaikio GMBH
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
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: multi_json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
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: spyke
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: vcr
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
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'
83
+ description:
84
+ email:
85
+ - suppoert@zaikio.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - CHANGELOG.md
91
+ - MIT-LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - lib/zaikio-client-helpers.rb
95
+ - lib/zaikio/client/helpers/json_parser.rb
96
+ - lib/zaikio/client/helpers/pagination.rb
97
+ - lib/zaikio/client/helpers/version.rb
98
+ - lib/zaikio/client/model.rb
99
+ homepage: https://github.com/zaikio/zaikio-client-helpers
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ changelog_uri: https://github.com/zaikio/zaikio-client-helpers/blob/main/CHANGELOG.md
104
+ homepage_uri: https://github.com/zaikio/zaikio-client-helpers
105
+ source_code_uri: https://github.com/zaikio/zaikio-client-helpers
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubygems_version: 3.2.15
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Small meta-gem with middleware and tools for working with Zaikio APIs
125
+ test_files: []