frederick_api 0.4.3 → 0.7

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
- SHA1:
3
- metadata.gz: 885bbe71573e83a6cd26d1321539c05885b09bf3
4
- data.tar.gz: d8154b394e152ed1b15fa57704cec9588bdb083c
2
+ SHA256:
3
+ metadata.gz: f39d405adb2db91e4cb552498c7188779cba58f587c7d2a35824f0cc18ee4587
4
+ data.tar.gz: 53b2858d0355ac8bba535e75d7950c0407cbfe61317b52f38d4623e002f044e4
5
5
  SHA512:
6
- metadata.gz: '049c7b295aaeed0a069646b36cb66c95a6a4b6e4ccec7807c522e33e6fb0e16358ac3a71841dc3ab97c65f3b25eecfd45bd3277787ffa172fc6ecbe0353438f4'
7
- data.tar.gz: da213d17ac1a57a7b176f8fa3e4e8e43b3d85299f84fb52cadd3c04714cb8d819ce9930867390a4402e7275fdf6ee30bf1866dcbce02a730242c9b9f0ca1b591
6
+ metadata.gz: 65a77c37baa3e8d3f170438d206a3095d21ca3b4548a6f009dd3cb450dcba43453c8f99f8704df0898a631b67dd3595002a7e7723d0c21124b1349869beba5ee
7
+ data.tar.gz: eabcd9f083df57b8b7350a405daa27cdaa8eebe82a00ea65694dbbf03bcbc0e94e0ec7bc2f5fd345106cd4ee30ddd327c31a4c24ca21317ac272b10dc2e4fa98
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,9 @@ 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'
35
+ require 'frederick_api/v2/campaign'
36
+ require 'frederick_api/v2/email_document'
32
37
 
33
38
  # Namespace for all Frederick API client methods/classes
34
39
  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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # V2 Campaign Resource
6
+ class Campaign < Resource
7
+ belongs_to :location
8
+
9
+ self.read_only_attributes += [:location_id]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # V2 Email Document Resource
6
+ class EmailDocument < Resource
7
+ belongs_to :location
8
+
9
+ self.read_only_attributes += [:location_id]
10
+ end
11
+ end
12
+ 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,24 @@ module FrederickAPI
7
7
  class Requestor < JsonApiClient::Query::Requestor
8
8
  attr_reader :path
9
9
 
10
+ # For backward compatibility, preserve these JSON API client errors instead of raising
11
+ # FrederickAPI::Errors::Error
12
+ JSON_API_CLIENT_PASSTHROUGH_ERRORS = [
13
+ JsonApiClient::Errors::NotAuthorized,
14
+ JsonApiClient::Errors::AccessDenied,
15
+ JsonApiClient::Errors::NotFound,
16
+ JsonApiClient::Errors::Conflict,
17
+ JsonApiClient::Errors::ServerError,
18
+ JsonApiClient::Errors::UnexpectedStatus
19
+ ].freeze
20
+
21
+ # Paths that may have an unbounded query param length so we should always use a POST
22
+ # instead of a GET to get around AWS Cloudfront limitations
23
+ GET_VIA_POST_PATHS = [
24
+ %r{^.*locations\/[^\/]+\/contacts$},
25
+ %r{^.*locations\/[^\/]+\/interactions$}
26
+ ].map(&:freeze).freeze
27
+
10
28
  def initialize(klass, path = nil)
11
29
  @klass = klass
12
30
  @path = path
@@ -21,15 +39,49 @@ module FrederickAPI
21
39
  end
22
40
  end
23
41
 
42
+ def get(params = {})
43
+ path = resource_path(params)
44
+
45
+ params.delete(klass.primary_key)
46
+ if get_via_post_path?(path)
47
+ return request(:post, path, body: params.to_json, additional_headers: { 'X-Request-Method' => 'GET' })
48
+ end
49
+
50
+ request(:get, path, params: params)
51
+ end
52
+
53
+ def linked(path)
54
+ uri = URI.parse(path)
55
+ return super unless get_via_post_path?(uri.path)
56
+
57
+ path_without_params = "#{uri.scheme}://#{uri.host}#{uri.path}"
58
+ params = uri.query ? CGI.parse(uri.query).each_with_object({}) { |(k, v), h| h[k] = v[0] } : {}
59
+ request(:post, path_without_params, params: params, additional_headers: { 'X-Request-Method' => 'GET' })
60
+ end
61
+
24
62
  # 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)
63
+ def request(type, path, params: nil, body: nil, additional_headers: {})
64
+ headers = klass.custom_headers.merge(additional_headers)
65
+ make_request = proc do
66
+ handle_background(handle_errors(make_request(type, path, params: params, body: body, headers: headers)))
67
+ end
68
+
69
+ begin
70
+ make_request.call
71
+ rescue JsonApiClient::Errors::ConnectionError, JsonApiClient::Errors::ServerError => ex
72
+ raise ex if ex.is_a?(JsonApiClient::Errors::NotFound) || ex.is_a?(JsonApiClient::Errors::Conflict)
73
+ make_request.call
74
+ end
30
75
  end
31
76
 
32
77
  private
78
+ def handle_background(response)
79
+ return response unless
80
+ (job = response&.first).is_a?(::FrederickAPI::V2::BackgroundJob) && job.status != 'complete'
81
+ raise FrederickAPI::V2::Errors::BackgroundJobFailure, job if job.has_errors?
82
+ sleep job.retry_after
83
+ linked(job.links.attributes['self'])
84
+ end
33
85
 
34
86
  def handle_errors(result)
35
87
  return result unless result.has_errors?
@@ -37,6 +89,25 @@ module FrederickAPI
37
89
  FrederickAPI::V2::Errors::Error
38
90
  raise error_klass, result
39
91
  end
92
+
93
+ def make_request(type, path, params:, body:, headers:)
94
+ faraday_response = connection.run(type, path, params: params, body: body, headers: headers)
95
+ return klass.parser.parse(klass, faraday_response) unless faraday_response.status == 303
96
+
97
+ linked(faraday_response.headers['location'])
98
+ rescue JsonApiClient::Errors::ClientError => e
99
+ handle_json_api_client_error(e)
100
+ end
101
+
102
+ def get_via_post_path?(path)
103
+ GET_VIA_POST_PATHS.any? { |r| r.match(path) }
104
+ end
105
+
106
+ def handle_json_api_client_error(error)
107
+ raise error if JSON_API_CLIENT_PASSTHROUGH_ERRORS.include?(error.class)
108
+
109
+ klass.parser.parse(klass, error.env.response)
110
+ end
40
111
  end
41
112
  end
42
113
  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,7 +4,8 @@ module FrederickAPI
4
4
  module V2
5
5
  # /v2/users
6
6
  class User < Resource
7
- has_many :roles
7
+ has_many :roles, class_name: 'FrederickAPI::V2::Role'
8
+ has_many :permitted_locations, class_name: 'FrederickAPI::V2::Location'
8
9
  end
9
10
  end
10
11
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module FrederickAPI
4
4
  # Current gem version
5
- VERSION = '0.4.3'
5
+ VERSION = '0.7'
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.3
4
+ version: '0.7'
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: 2018-01-09 00:00:00.000000000 Z
11
+ date: 2020-11-14 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,13 +36,17 @@ 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
41
+ - lib/frederick_api/v2/campaign.rb
40
42
  - lib/frederick_api/v2/communication_content.rb
41
43
  - lib/frederick_api/v2/contact.rb
42
44
  - lib/frederick_api/v2/contact_list.rb
43
45
  - lib/frederick_api/v2/contact_property.rb
44
46
  - lib/frederick_api/v2/contact_type.rb
47
+ - lib/frederick_api/v2/email_document.rb
45
48
  - lib/frederick_api/v2/errors/errors.rb
49
+ - lib/frederick_api/v2/helpers/backgroundable_parser.rb
46
50
  - lib/frederick_api/v2/helpers/has_many.rb
47
51
  - lib/frederick_api/v2/helpers/paginator.rb
48
52
  - lib/frederick_api/v2/helpers/query_builder.rb
@@ -51,13 +55,14 @@ files:
51
55
  - lib/frederick_api/v2/location.rb
52
56
  - lib/frederick_api/v2/public_resource.rb
53
57
  - lib/frederick_api/v2/resource.rb
58
+ - lib/frederick_api/v2/role.rb
54
59
  - lib/frederick_api/v2/user.rb
55
60
  - lib/frederick_api/version.rb
56
61
  homepage: https://github.com/HireFrederick/frederick_api_gem
57
62
  licenses:
58
63
  - MIT
59
64
  metadata: {}
60
- post_install_message:
65
+ post_install_message:
61
66
  rdoc_options: []
62
67
  require_paths:
63
68
  - lib
@@ -72,9 +77,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
77
  - !ruby/object:Gem::Version
73
78
  version: '0'
74
79
  requirements: []
75
- rubyforge_project:
76
- rubygems_version: 2.6.14
77
- signing_key:
80
+ rubygems_version: 3.0.8
81
+ signing_key:
78
82
  specification_version: 4
79
83
  summary: Frederick API Client
80
84
  test_files: []