frederick_api 0.4.4 → 0.9

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: 711d8e8257f97e7ad18803cca7b24e8e87a9f43e
4
- data.tar.gz: baf134c2da2e7da52cfeb7a75ccd5b90a18a2200
2
+ SHA256:
3
+ metadata.gz: af582661b851880a278f82844f06ec98910f639cf0292ec1249f1ed1127b0a27
4
+ data.tar.gz: 5740b928ced7a2d9f4964388dbd5e543cbb0827c571f562899ab3a651a030f43
5
5
  SHA512:
6
- metadata.gz: 294478020b031f49b7f5ae1a636977ff5301befb4892f44b9526ddec5037c2b2f4f6461a391e7661ed70db4cdf05177f696bb08c63f158c0d6f1ee4ba7855dff
7
- data.tar.gz: fbce1720a78066c1caeeb2e1da9dbf688f250eacea4fa407307a838d492ef898ff8791cbdd83c7cfe1d38462b5f456f1643acc8772063ca7163c5d8effb1df45
6
+ metadata.gz: dd740314950b62480828dc4878a603582edd4bf00c6bddc7e7a6b694707b90da52fd2e31ccdf226b86afeff09aeccd0a12d94b900f322a1e153a3e5a48ce9515
7
+ data.tar.gz: e535fb7f7655cbf975424497a333292d1dae97c9c8cb3f0621fac118f6e694d360dd355250f1ab91427144038c6a009392c37a0c47a64b4a9536b34a223a6582
data/README.md CHANGED
@@ -88,6 +88,21 @@ FrederickAPI::V2::Location.with_access_token(access_token) do
88
88
  end
89
89
  ```
90
90
 
91
+ ### Using Headers
92
+
93
+ `Resource.with_access_token` is extended to take headers in the method `Resource.with_access_token_and_headers`. This is useful when we need to pass the universal customer to services that accept it.
94
+
95
+ ```ruby
96
+ access_token = '9jsdo320fjfkfdksls30dfdcd919bcaa1b7804dbbebda0'
97
+ headers = { 'X-Universal-Customer': '{"source_platform":"mindbody","source_location_id":"1","source_customer_id":"77"}' }
98
+
99
+ FrederickAPI::V2::CommunicationTemplate.with_access_token_and_headers(access_token, headers) do
100
+ template = FrederickAPI::V2::CommunicationTemplate.create(template_attributes)
101
+
102
+ template.name
103
+ # => 'Name from Attrs'
104
+ end
105
+ ```
91
106
  ### Nested Resources
92
107
 
93
108
  Nested resources must be accessed using `where` to scope the request:
@@ -101,3 +116,14 @@ FrederickAPI::V2::Location.with_access_token(access_token) do
101
116
  # => [...]
102
117
  end
103
118
  ```
119
+ ### Background Jobs
120
+
121
+ FrederickAPI Gem handles asynchronous responses as suggested in
122
+ [JSONApi Recommendations](https://jsonapi.org/recommendations/#asynchronous-processing)
123
+ [Asynchronous Processing](https://jsonapi.org/recommendations/#asynchronous-processing).
124
+ Polling until the job is complete, fetching and returning the completed resource.
125
+
126
+ * A FrederickAPI::V2::Errors::BackgroundJobFailure exception is raised if the API returns
127
+ an error on an asyncronous job.
128
+ * A BackgroundJob Resource will be returned from the client in the case that a successful
129
+ job does not return a resource.
data/lib/frederick_api.rb CHANGED
@@ -12,17 +12,20 @@ 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'
23
25
 
24
26
  # Core resources
25
27
  require 'frederick_api/v2/automation'
28
+ require 'frederick_api/v2/automation_step'
26
29
  require 'frederick_api/v2/communication_content'
27
30
  require 'frederick_api/v2/contact'
28
31
  require 'frederick_api/v2/contact_property'
@@ -30,6 +33,9 @@ require 'frederick_api/v2/contact_list'
30
33
  require 'frederick_api/v2/contact_type'
31
34
  require 'frederick_api/v2/interaction'
32
35
  require 'frederick_api/v2/role'
36
+ require 'frederick_api/v2/campaign'
37
+ require 'frederick_api/v2/email_document'
38
+ require 'frederick_api/v2/communication_template'
33
39
 
34
40
  # Namespace for all Frederick API client methods/classes
35
41
  module FrederickAPI
@@ -5,6 +5,8 @@ module FrederickAPI
5
5
  # /v2/locations/:location_id/automations
6
6
  class Automation < Resource
7
7
  belongs_to :location
8
+ has_many :automation_steps
9
+
8
10
  self.read_only_attributes += [:location_id]
9
11
  end
10
12
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # Resource for automation step
6
+ class AutomationStep < Resource
7
+ belongs_to :location
8
+ has_one :automation
9
+ has_one :previous_automation_step, class_name: 'FrederickAPI::V2::AutomationStep'
10
+
11
+ self.read_only_attributes += %i[location_id]
12
+ end
13
+ end
14
+ end
@@ -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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FrederickAPI
4
+ module V2
5
+ # V2 CommunicationTemplate Resource
6
+ class CommunicationTemplate < Resource; end
7
+ end
8
+ 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, body: params.to_json, 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
 
@@ -35,7 +36,16 @@ module FrederickAPI
35
36
  end
36
37
 
37
38
  def self.with_access_token(token)
38
- with_headers(authorization: "Bearer #{token}") do
39
+ with_access_token_and_headers(token) do
40
+ yield
41
+ end
42
+ end
43
+
44
+ def self.with_access_token_and_headers(token, headers = {})
45
+ with_headers(
46
+ authorization: "Bearer #{token}",
47
+ **headers
48
+ ) do
39
49
  yield
40
50
  end
41
51
  end
@@ -5,6 +5,7 @@ module FrederickAPI
5
5
  # /v2/users
6
6
  class User < Resource
7
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.4'
5
+ VERSION = '0.9'
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.4
4
+ version: '0.9'
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-11 00:00:00.000000000 Z
11
+ date: 2021-06-11 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,19 @@ 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/automation_step.rb
40
+ - lib/frederick_api/v2/background_job.rb
39
41
  - lib/frederick_api/v2/business_category.rb
42
+ - lib/frederick_api/v2/campaign.rb
40
43
  - lib/frederick_api/v2/communication_content.rb
44
+ - lib/frederick_api/v2/communication_template.rb
41
45
  - lib/frederick_api/v2/contact.rb
42
46
  - lib/frederick_api/v2/contact_list.rb
43
47
  - lib/frederick_api/v2/contact_property.rb
44
48
  - lib/frederick_api/v2/contact_type.rb
49
+ - lib/frederick_api/v2/email_document.rb
45
50
  - lib/frederick_api/v2/errors/errors.rb
51
+ - lib/frederick_api/v2/helpers/backgroundable_parser.rb
46
52
  - lib/frederick_api/v2/helpers/has_many.rb
47
53
  - lib/frederick_api/v2/helpers/paginator.rb
48
54
  - lib/frederick_api/v2/helpers/query_builder.rb
@@ -58,7 +64,7 @@ homepage: https://github.com/HireFrederick/frederick_api_gem
58
64
  licenses:
59
65
  - MIT
60
66
  metadata: {}
61
- post_install_message:
67
+ post_install_message:
62
68
  rdoc_options: []
63
69
  require_paths:
64
70
  - lib
@@ -73,9 +79,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
79
  - !ruby/object:Gem::Version
74
80
  version: '0'
75
81
  requirements: []
76
- rubyforge_project:
77
- rubygems_version: 2.6.14
78
- signing_key:
82
+ rubygems_version: 3.0.8
83
+ signing_key:
79
84
  specification_version: 4
80
85
  summary: Frederick API Client
81
86
  test_files: []