frederick_api 0.4.2 → 0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3405bd97d370f433b11cf71636a9a45eda63db2a
4
- data.tar.gz: c67e2e1aad4e82a35b454f6f926e9440061c86e1
2
+ SHA256:
3
+ metadata.gz: 9848d60ef419d88f6758574988f60f90337c64645316a0c716c12aaf497f43a9
4
+ data.tar.gz: 2e6096aeca9837b93ee25a2468bc7d3e2cf2712f6c735fb7330cf13a489f3b1e
5
5
  SHA512:
6
- metadata.gz: 425f96c7393736efcc851f35a1627f16007c1daba10d52c138f16f8e065832bd13f1f24326c0e7448ff613978a6ff8412a30cc621a11e5d39161563977257f28
7
- data.tar.gz: 278b005db0e22ca5f2791e3fa54b58d2ea29956a92ab3735a621b1a9e298c8c8f2bbbecb635e96c083690c7d8a128cddb73654088d024b64237457ed2e0ba420
6
+ metadata.gz: 06f920d1c60e09288bb9c75ef3b9be31168b93fe24ab681c33b1e2b77fb9127172e86608da5c8f862a43965867e1e33f571a91dfb80bd768cc9b227979b084dc
7
+ data.tar.gz: 738cf8ec474b270d81e79a95499325f62f70557baf09bc16220abd4d467564ead6607bf180bb22352bd309d78fdab6ce84435863341ea8c99c89985d430939b7
data/README.md CHANGED
@@ -101,3 +101,14 @@ FrederickAPI::V2::Location.with_access_token(access_token) do
101
101
  # => [...]
102
102
  end
103
103
  ```
104
+ ### Background Jobs
105
+
106
+ FrederickAPI Gem handles asynchronous responses as suggested in
107
+ [JSONApi Recommendations](https://jsonapi.org/recommendations/#asynchronous-processing)
108
+ [Asynchronous Processing](https://jsonapi.org/recommendations/#asynchronous-processing).
109
+ Polling until the job is complete, fetching and returning the completed resource.
110
+
111
+ * A FrederickAPI::V2::Errors::BackgroundJobFailure exception is raised if the API returns
112
+ an error on an asyncronous job.
113
+ * A BackgroundJob Resource will be returned from the client in the case that a successful
114
+ job does not return a resources.
@@ -12,11 +12,13 @@ require 'frederick_api/v2/helpers/has_many'
12
12
  require 'frederick_api/v2/helpers/paginator'
13
13
  require 'frederick_api/v2/helpers/query_builder'
14
14
  require 'frederick_api/v2/helpers/requestor'
15
+ require 'frederick_api/v2/helpers/backgroundable_parser'
15
16
  require 'frederick_api/v2/resource'
16
17
  require 'frederick_api/v2/public_resource'
17
18
 
18
19
  require 'frederick_api/v2/user'
19
20
  require 'frederick_api/v2/location'
21
+ require 'frederick_api/v2/background_job'
20
22
 
21
23
  # Public resources
22
24
  require 'frederick_api/v2/business_category'
@@ -29,6 +31,7 @@ require 'frederick_api/v2/contact_property'
29
31
  require 'frederick_api/v2/contact_list'
30
32
  require 'frederick_api/v2/contact_type'
31
33
  require 'frederick_api/v2/interaction'
34
+ require 'frederick_api/v2/role'
32
35
 
33
36
  # Namespace for all Frederick API client methods/classes
34
37
  module FrederickAPI
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # V2 Frederick API async background job class for parsing
6
+ # background job responses coming from API.
7
+ class BackgroundJob < Resource
8
+ attr_accessor :response
9
+
10
+ def has_errors?
11
+ @attributes['status'] == 'error'
12
+ end
13
+
14
+ def retry_after
15
+ try_time = @response[:headers]['retry-after'].to_i
16
+ @retry_after ||= try_time > 1 ? try_time : 1
17
+ end
18
+
19
+ def response_code
20
+ @response_code ||= @response[:status]
21
+ end
22
+
23
+ def status
24
+ @attributes['status']
25
+ end
26
+
27
+ def errors
28
+ @attributes['messages']
29
+ end
30
+
31
+ def id
32
+ @attributes['id']
33
+ end
34
+ end
35
+ end
36
+ end
@@ -23,6 +23,10 @@ module FrederickAPI
23
23
  class BadRequest < Error; end
24
24
  class UnprocessableEntity < Error; end
25
25
 
26
+ # an exception class for when the server reports that a
27
+ # long running job has failed.
28
+ class BackgroundJobFailure < Error; end
29
+
26
30
  ERROR_CODES = {
27
31
  '400' => BadRequest,
28
32
  '422' => UnprocessableEntity
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ module Helpers
6
+ # Custom Parser for parsing BackgroundJob resources for FrederickAPI V2
7
+ class BackgroundableParser < ::JsonApiClient::Parsers::Parser
8
+ def self.parse(klass, response)
9
+ result_set = super(klass, response)
10
+ return result_set unless result_set&.first&.type == 'background_jobs'
11
+ result_set = super(::FrederickAPI::V2::BackgroundJob, response)
12
+ result_set&.first&.response = { headers: response.headers, status: response.status }
13
+ result_set
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -20,7 +20,7 @@ module FrederickAPI
20
20
  def query_builder(url)
21
21
  association_class.query_builder.new(
22
22
  association_class,
23
- association_class.requestor_class.new(association_class, url)
23
+ requestor: association_class.requestor_class.new(association_class, url)
24
24
  )
25
25
  end
26
26
 
@@ -9,9 +9,9 @@ module FrederickAPI
9
9
  class QueryBuilder < JsonApiClient::Query::Builder
10
10
  attr_reader :requestor
11
11
 
12
- def initialize(klass, requestor = nil)
13
- super(klass)
14
- @requestor = requestor || klass.requestor
12
+ def initialize(klass, opts = {})
13
+ super(klass, opts)
14
+ @requestor = opts[:requestor] || klass.requestor
15
15
  end
16
16
 
17
17
  def params
@@ -23,17 +23,6 @@ module FrederickAPI
23
23
  .merge(additional_params)
24
24
  end
25
25
 
26
- def find(args = {})
27
- case args
28
- when Hash
29
- where(args)
30
- else
31
- @primary_key = args
32
- end
33
-
34
- requestor.get(params)
35
- end
36
-
37
26
  def filter_params
38
27
  super_filter_params = super
39
28
 
@@ -65,6 +54,27 @@ module FrederickAPI
65
54
  { prefix => object }
66
55
  end
67
56
  end
57
+
58
+ protected
59
+
60
+ def _fetch
61
+ (requestor || klass.requestor).get(params)
62
+ end
63
+
64
+ private
65
+
66
+ def _new_scope(opts = {})
67
+ self.class.new(@klass,
68
+ requestor: requestor,
69
+ primary_key: opts.fetch(:primary_key, @primary_key),
70
+ pagination_params: @pagination_params.merge(opts.fetch(:pagination_params, {})),
71
+ path_params: @path_params.merge(opts.fetch(:path_params, {})),
72
+ additional_params: @additional_params.merge(opts.fetch(:additional_params, {})),
73
+ filters: @filters.merge(opts.fetch(:filters, {})),
74
+ includes: @includes + opts.fetch(:includes, []),
75
+ orders: @orders + opts.fetch(:orders, []),
76
+ fields: @fields + opts.fetch(:fields, []))
77
+ end
68
78
  end
69
79
  end
70
80
  end
@@ -7,6 +7,13 @@ module FrederickAPI
7
7
  class Requestor < JsonApiClient::Query::Requestor
8
8
  attr_reader :path
9
9
 
10
+ # Paths that may have an unbounded query param length so we should always use a POST
11
+ # instead of a GET to get around AWS Cloudfront limitations
12
+ GET_VIA_POST_PATHS = [
13
+ %r{^.*locations\/[^\/]+\/contacts$},
14
+ %r{^.*locations\/[^\/]+\/interactions$}
15
+ ].map(&:freeze).freeze
16
+
10
17
  def initialize(klass, path = nil)
11
18
  @klass = klass
12
19
  @path = path
@@ -21,15 +28,49 @@ module FrederickAPI
21
28
  end
22
29
  end
23
30
 
31
+ def get(params = {})
32
+ path = resource_path(params)
33
+
34
+ params.delete(klass.primary_key)
35
+ if get_via_post_path?(path)
36
+ return request(:post, path, body: params.to_json, additional_headers: { 'X-Request-Method' => 'GET' })
37
+ end
38
+
39
+ request(:get, path, params: params)
40
+ end
41
+
42
+ def linked(path)
43
+ uri = URI.parse(path)
44
+ return super unless get_via_post_path?(uri.path)
45
+
46
+ path_without_params = "#{uri.scheme}://#{uri.host}#{uri.path}"
47
+ params = uri.query ? CGI.parse(uri.query).each_with_object({}) { |(k, v), h| h[k] = v[0] } : {}
48
+ request(:post, path_without_params, params: params, additional_headers: { 'X-Request-Method' => 'GET' })
49
+ end
50
+
24
51
  # Retry once on unhandled server errors
25
- def request(type, path, params)
26
- handle_errors(super)
27
- rescue JsonApiClient::Errors::ConnectionError, JsonApiClient::Errors::ServerError => ex
28
- raise ex if ex.is_a?(JsonApiClient::Errors::NotFound) || ex.is_a?(JsonApiClient::Errors::Conflict)
29
- handle_errors(super)
52
+ def request(type, path, params: nil, body: nil, additional_headers: {})
53
+ headers = klass.custom_headers.merge(additional_headers)
54
+ make_request = proc do
55
+ handle_background(handle_errors(make_request(type, path, params: params, body: body, headers: headers)))
56
+ end
57
+
58
+ begin
59
+ make_request.call
60
+ rescue JsonApiClient::Errors::ConnectionError, JsonApiClient::Errors::ServerError => ex
61
+ raise ex if ex.is_a?(JsonApiClient::Errors::NotFound) || ex.is_a?(JsonApiClient::Errors::Conflict)
62
+ make_request.call
63
+ end
30
64
  end
31
65
 
32
66
  private
67
+ def handle_background(response)
68
+ return response unless
69
+ (job = response&.first).is_a?(::FrederickAPI::V2::BackgroundJob) && job.status != 'complete'
70
+ raise FrederickAPI::V2::Errors::BackgroundJobFailure, job if job.has_errors?
71
+ sleep job.retry_after
72
+ linked(job.links.attributes['self'])
73
+ end
33
74
 
34
75
  def handle_errors(result)
35
76
  return result unless result.has_errors?
@@ -37,6 +78,18 @@ module FrederickAPI
37
78
  FrederickAPI::V2::Errors::Error
38
79
  raise error_klass, result
39
80
  end
81
+
82
+ def make_request(type, path, params:, body:, headers:)
83
+ faraday_response = connection.run(type, path, params: params, body: body, headers: headers)
84
+ return klass.parser.parse(klass, faraday_response) unless faraday_response.status == 303
85
+ linked(faraday_response.headers['location'])
86
+ rescue JsonApiClient::Errors::ClientError => ex
87
+ klass.parser.parse(klass, ex.env.response)
88
+ end
89
+
90
+ def get_via_post_path?(path)
91
+ GET_VIA_POST_PATHS.any? { |r| r.match(path) }
92
+ end
40
93
  end
41
94
  end
42
95
  end
@@ -10,6 +10,7 @@ module FrederickAPI
10
10
  self.query_builder = FrederickAPI::V2::Helpers::QueryBuilder
11
11
  self.paginator = FrederickAPI::V2::Helpers::Paginator
12
12
  self.requestor_class = FrederickAPI::V2::Helpers::Requestor
13
+ self.parser = ::FrederickAPI::V2::Helpers::BackgroundableParser
13
14
 
14
15
  attr_accessor :custom_headers
15
16
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # /v2/users
6
+ class Role < Resource
7
+ has_many :users
8
+ end
9
+ end
10
+ end
@@ -4,6 +4,8 @@ module FrederickAPI
4
4
  module V2
5
5
  # /v2/users
6
6
  class User < Resource
7
+ has_many :roles, class_name: 'FrederickAPI::V2::Role'
8
+ has_many :permitted_locations, class_name: 'FrederickAPI::V2::Location'
7
9
  end
8
10
  end
9
11
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module FrederickAPI
4
4
  # Current gem version
5
- VERSION = '0.4.2'
5
+ VERSION = '0.6'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frederick_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frederick Engineering
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-26 00:00:00.000000000 Z
11
+ date: 2020-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json_api_client
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.5.3
19
+ version: 1.17.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.5.3
26
+ version: 1.17.1
27
27
  description: Ruby client for the Frederick API
28
28
  email:
29
29
  - tech@hirefrederick.com
@@ -36,6 +36,7 @@ files:
36
36
  - lib/frederick_api.rb
37
37
  - lib/frederick_api/configuration.rb
38
38
  - lib/frederick_api/v2/automation.rb
39
+ - lib/frederick_api/v2/background_job.rb
39
40
  - lib/frederick_api/v2/business_category.rb
40
41
  - lib/frederick_api/v2/communication_content.rb
41
42
  - lib/frederick_api/v2/contact.rb
@@ -43,6 +44,7 @@ files:
43
44
  - lib/frederick_api/v2/contact_property.rb
44
45
  - lib/frederick_api/v2/contact_type.rb
45
46
  - lib/frederick_api/v2/errors/errors.rb
47
+ - lib/frederick_api/v2/helpers/backgroundable_parser.rb
46
48
  - lib/frederick_api/v2/helpers/has_many.rb
47
49
  - lib/frederick_api/v2/helpers/paginator.rb
48
50
  - lib/frederick_api/v2/helpers/query_builder.rb
@@ -51,13 +53,14 @@ files:
51
53
  - lib/frederick_api/v2/location.rb
52
54
  - lib/frederick_api/v2/public_resource.rb
53
55
  - lib/frederick_api/v2/resource.rb
56
+ - lib/frederick_api/v2/role.rb
54
57
  - lib/frederick_api/v2/user.rb
55
58
  - lib/frederick_api/version.rb
56
59
  homepage: https://github.com/HireFrederick/frederick_api_gem
57
60
  licenses:
58
61
  - MIT
59
62
  metadata: {}
60
- post_install_message:
63
+ post_install_message:
61
64
  rdoc_options: []
62
65
  require_paths:
63
66
  - lib
@@ -72,9 +75,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
75
  - !ruby/object:Gem::Version
73
76
  version: '0'
74
77
  requirements: []
75
- rubyforge_project:
76
- rubygems_version: 2.6.11
77
- signing_key:
78
+ rubygems_version: 3.0.8
79
+ signing_key:
78
80
  specification_version: 4
79
81
  summary: Frederick API Client
80
82
  test_files: []