rdstation-ruby-client 2.0.0 → 2.4.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 +4 -4
 - data/CHANGELOG.md +116 -4
 - data/README.md +114 -22
 - data/lib/rdstation-ruby-client.rb +6 -1
 - data/lib/rdstation.rb +19 -0
 - data/lib/rdstation/api_response.rb +1 -2
 - data/lib/rdstation/authentication.rb +32 -3
 - data/lib/rdstation/{authorization_header.rb → authorization.rb} +11 -8
 - data/lib/rdstation/builder/field.rb +70 -0
 - data/lib/rdstation/client.rb +17 -7
 - data/lib/rdstation/contacts.rb +22 -13
 - data/lib/rdstation/error.rb +2 -0
 - data/lib/rdstation/error/format.rb +29 -3
 - data/lib/rdstation/error/formatter.rb +69 -8
 - data/lib/rdstation/error_handler.rb +6 -1
 - data/lib/rdstation/events.rb +7 -12
 - data/lib/rdstation/fields.rb +35 -6
 - data/lib/rdstation/retryable_request.rb +35 -0
 - data/lib/rdstation/version.rb +1 -1
 - data/lib/rdstation/webhooks.rb +25 -13
 - data/rdstation-ruby-client.gemspec +2 -1
 - data/spec/lib/rdstation/api_response_spec.rb +34 -0
 - data/spec/lib/rdstation/authentication_spec.rb +164 -0
 - data/spec/lib/rdstation/{authorization_header_spec.rb → authorization_spec.rb} +3 -3
 - data/spec/lib/rdstation/builder/field_spec.rb +69 -0
 - data/spec/lib/rdstation/client_spec.rb +6 -6
 - data/spec/lib/rdstation/contacts_spec.rb +23 -3
 - data/spec/lib/rdstation/error/format_spec.rb +63 -0
 - data/spec/lib/rdstation/error/formatter_spec.rb +113 -0
 - data/spec/lib/rdstation/error_handler_spec.rb +23 -0
 - data/spec/lib/rdstation/events_spec.rb +8 -3
 - data/spec/lib/rdstation/fields_spec.rb +6 -1
 - data/spec/lib/rdstation/retryable_request_spec.rb +142 -0
 - data/spec/lib/rdstation/webhooks_spec.rb +26 -1
 - data/spec/lib/rdstation_spec.rb +18 -0
 - metadata +36 -11
 
| 
         @@ -1,8 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module RDStation
         
     | 
| 
       2 
2 
     | 
    
         
             
              module ApiResponse
         
     | 
| 
       3 
3 
     | 
    
         
             
                def self.build(response)
         
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
       5 
     | 
    
         
            -
                  return response_body if response.code.between?(200, 299)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  return JSON.parse(response.body) if response.code.between?(200, 299)
         
     | 
| 
       6 
5 
     | 
    
         | 
| 
       7 
6 
     | 
    
         
             
                  RDStation::ErrorHandler.new(response).raise_error
         
     | 
| 
       8 
7 
     | 
    
         
             
                end
         
     | 
| 
         @@ -5,10 +5,12 @@ module RDStation 
     | 
|
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                AUTH_TOKEN_URL = 'https://api.rd.services/auth/token'.freeze
         
     | 
| 
       7 
7 
     | 
    
         
             
                DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
                REVOKE_URL = 'https://api.rd.services/auth/revoke'.freeze
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                def initialize(client_id, client_secret)
         
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
       11 
     | 
    
         
            -
                  @ 
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(client_id = nil, client_secret = nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  warn_deprecation if client_id || client_secret
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @client_id = client_id || RDStation.configuration&.client_id
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @client_secret = client_secret || RDStation.configuration&.client_secret
         
     | 
| 
       12 
14 
     | 
    
         
             
                end
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                #
         
     | 
| 
         @@ -47,8 +49,31 @@ module RDStation 
     | 
|
| 
       47 
49 
     | 
    
         
             
                  ApiResponse.build(response)
         
     | 
| 
       48 
50 
     | 
    
         
             
                end
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
      
 52 
     | 
    
         
            +
                def self.revoke(access_token:)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  response = self.post(
         
     | 
| 
      
 54 
     | 
    
         
            +
                    REVOKE_URL,
         
     | 
| 
      
 55 
     | 
    
         
            +
                    body: revoke_body(access_token),
         
     | 
| 
      
 56 
     | 
    
         
            +
                    headers: revoke_headers(access_token)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  )
         
     | 
| 
      
 58 
     | 
    
         
            +
                  ApiResponse.build(response)
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       50 
61 
     | 
    
         
             
                private
         
     | 
| 
       51 
62 
     | 
    
         | 
| 
      
 63 
     | 
    
         
            +
                def self.revoke_body(access_token)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  URI.encode_www_form({
         
     | 
| 
      
 65 
     | 
    
         
            +
                    token: access_token,
         
     | 
| 
      
 66 
     | 
    
         
            +
                    token_type_hint: 'access_token'
         
     | 
| 
      
 67 
     | 
    
         
            +
                  })
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def self.revoke_headers(access_token)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  {
         
     | 
| 
      
 72 
     | 
    
         
            +
                    "Authorization" => "Bearer #{access_token}",
         
     | 
| 
      
 73 
     | 
    
         
            +
                    "Content-Type" => "application/x-www-form-urlencoded"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  }
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
       52 
77 
     | 
    
         
             
                def post_to_auth_endpoint(params)
         
     | 
| 
       53 
78 
     | 
    
         
             
                  default_body = { client_id: @client_id, client_secret: @client_secret }
         
     | 
| 
       54 
79 
     | 
    
         
             
                  body = default_body.merge(params)
         
     | 
| 
         @@ -59,5 +84,9 @@ module RDStation 
     | 
|
| 
       59 
84 
     | 
    
         
             
                    headers: DEFAULT_HEADERS
         
     | 
| 
       60 
85 
     | 
    
         
             
                  )
         
     | 
| 
       61 
86 
     | 
    
         
             
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def warn_deprecation
         
     | 
| 
      
 89 
     | 
    
         
            +
                  warn "DEPRECATION WARNING: Providing client_id and client_secret directly to RDStation::Authentication.new is deprecated and will be removed in future versions. Use RDStation.configure instead."
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
       62 
91 
     | 
    
         
             
              end
         
     | 
| 
       63 
92 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,21 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module RDStation
         
     | 
| 
       2 
     | 
    
         
            -
              class  
     | 
| 
       3 
     | 
    
         
            -
                
         
     | 
| 
       4 
     | 
    
         
            -
                 
     | 
| 
      
 2 
     | 
    
         
            +
              class Authorization
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :refresh_token
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor :access_token, :access_token_expires_in
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(access_token:, refresh_token: nil, access_token_expires_in: nil)
         
     | 
| 
       5 
6 
     | 
    
         
             
                  @access_token = access_token
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @refresh_token = refresh_token
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @access_token_expires_in = access_token_expires_in
         
     | 
| 
       6 
9 
     | 
    
         
             
                  validate_access_token access_token
         
     | 
| 
       7 
10 
     | 
    
         
             
                end
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                def  
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def headers
         
     | 
| 
       10 
13 
     | 
    
         
             
                  { "Authorization" => "Bearer #{@access_token}", "Content-Type" => "application/json" }
         
     | 
| 
       11 
14 
     | 
    
         
             
                end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       13 
16 
     | 
    
         
             
                private
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       15 
18 
     | 
    
         
             
                def validate_access_token(access_token)
         
     | 
| 
       16 
19 
     | 
    
         
             
                  access_token_msg = ':access_token is required'
         
     | 
| 
       17 
20 
     | 
    
         
             
                  raise ArgumentError, access_token_msg unless access_token
         
     | 
| 
       18 
21 
     | 
    
         
             
                end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       20 
23 
     | 
    
         
             
              end
         
     | 
| 
       21 
24 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RDStation
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Builder
         
     | 
| 
      
 5 
     | 
    
         
            +
                # More info: https://developers.rdstation.com/pt-BR/reference/fields#methodPostDetails
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Field
         
     | 
| 
      
 7 
     | 
    
         
            +
                  DATA_TYPES = %w(STRING INTEGER BOOLEAN STRING[]).freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
                  PRESENTATION_TYPES = %w[TEXT_INPUT TEXT_AREA URL_INPUT PHONE_INPUT
         
     | 
| 
      
 9 
     | 
    
         
            +
                                          EMAIL_INPUT CHECK_BOX NUMBER_INPUT COMBO_BOX
         
     | 
| 
      
 10 
     | 
    
         
            +
                                          RADIO_BUTTON MULTIPLE_CHOICE].freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  REQUIRED_FIELDS = %w[api_identifier data_type presentation_type label name].freeze
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize(api_identifier)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    raise 'api_identifier required' unless api_identifier
         
     | 
| 
      
 16 
     | 
    
         
            +
                    unless valid_identifier(api_identifier)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      raise 'api_identifier is not in a valid format, need start with "cf_"'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    @values = {}
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @values['api_identifier'] = api_identifier
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def data_type(data_type)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    raise "Not valid data_type - #{DATA_TYPES}" unless DATA_TYPES.include? data_type
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    @values['data_type'] = data_type
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def presentation_type(presentation_type)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    unless PRESENTATION_TYPES.include? presentation_type
         
     | 
| 
      
 32 
     | 
    
         
            +
                      raise "Not valid presentation_type - #{PRESENTATION_TYPES}"
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    @values['presentation_type'] = presentation_type
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def label(language, label)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @values['label'] = { language.to_s => label }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def name(language, name)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @values['name'] = { language.to_s => name }
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def validation_rules(validation_rules)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @values['validation_rules'] = validation_rules
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def valid_options(valid_options)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @values['valid_options'] = valid_options
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def build
         
     | 
| 
      
 55 
     | 
    
         
            +
                    empty_fields = REQUIRED_FIELDS.select { |field| @values[field].nil? }
         
     | 
| 
      
 56 
     | 
    
         
            +
                    unless empty_fields.empty?
         
     | 
| 
      
 57 
     | 
    
         
            +
                      raise "Required fields are missing - #{empty_fields}"
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    @values
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  private
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def valid_identifier(api_identifier)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    api_identifier.start_with? 'cf_'
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rdstation/client.rb
    CHANGED
    
    | 
         @@ -1,23 +1,33 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module RDStation
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Client
         
     | 
| 
       3 
     | 
    
         
            -
                def initialize(access_token:)
         
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(access_token:, refresh_token: nil)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  warn_deprecation unless refresh_token
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @authorization = Authorization.new(
         
     | 
| 
      
 6 
     | 
    
         
            +
                    access_token: access_token,
         
     | 
| 
      
 7 
     | 
    
         
            +
                    refresh_token: refresh_token
         
     | 
| 
      
 8 
     | 
    
         
            +
                  )
         
     | 
| 
       5 
9 
     | 
    
         
             
                end
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       7 
11 
     | 
    
         
             
                def contacts
         
     | 
| 
       8 
     | 
    
         
            -
                  @contacts ||= RDStation::Contacts.new( 
     | 
| 
      
 12 
     | 
    
         
            +
                  @contacts ||= RDStation::Contacts.new(authorization: @authorization)
         
     | 
| 
       9 
13 
     | 
    
         
             
                end
         
     | 
| 
       10 
14 
     | 
    
         | 
| 
       11 
15 
     | 
    
         
             
                def events
         
     | 
| 
       12 
     | 
    
         
            -
                  @events ||= RDStation::Events.new( 
     | 
| 
      
 16 
     | 
    
         
            +
                  @events ||= RDStation::Events.new(authorization: @authorization)
         
     | 
| 
       13 
17 
     | 
    
         
             
                end
         
     | 
| 
       14 
18 
     | 
    
         | 
| 
       15 
19 
     | 
    
         
             
                def fields
         
     | 
| 
       16 
     | 
    
         
            -
                  @fields ||= RDStation::Fields.new( 
     | 
| 
      
 20 
     | 
    
         
            +
                  @fields ||= RDStation::Fields.new(authorization: @authorization)
         
     | 
| 
       17 
21 
     | 
    
         
             
                end
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
       19 
23 
     | 
    
         
             
                def webhooks
         
     | 
| 
       20 
     | 
    
         
            -
                  @webhooks ||= RDStation::Webhooks.new( 
     | 
| 
      
 24 
     | 
    
         
            +
                  @webhooks ||= RDStation::Webhooks.new(authorization: @authorization)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def warn_deprecation
         
     | 
| 
      
 30 
     | 
    
         
            +
                  warn "DEPRECATION WARNING: Specifying refresh_token in RDStation::Client.new(access_token: 'at', refresh_token: 'rt') is optional right now, but will be mandatory in future versions. "
         
     | 
| 
       21 
31 
     | 
    
         
             
                end
         
     | 
| 
       22 
32 
     | 
    
         
             
              end
         
     | 
| 
       23 
33 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rdstation/contacts.rb
    CHANGED
    
    | 
         @@ -3,9 +3,10 @@ module RDStation 
     | 
|
| 
       3 
3 
     | 
    
         
             
              # More info: https://developers.rdstation.com/pt-BR/reference/contacts
         
     | 
| 
       4 
4 
     | 
    
         
             
              class Contacts
         
     | 
| 
       5 
5 
     | 
    
         
             
                include HTTParty
         
     | 
| 
       6 
     | 
    
         
            -
                
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
                include ::RDStation::RetryableRequest
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(authorization:)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @authorization = authorization
         
     | 
| 
       9 
10 
     | 
    
         
             
                end
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
12 
     | 
    
         
             
                #
         
     | 
| 
         @@ -13,13 +14,17 @@ module RDStation 
     | 
|
| 
       13 
14 
     | 
    
         
             
                #   The unique uuid associated to each RD Station Contact.
         
     | 
| 
       14 
15 
     | 
    
         
             
                #
         
     | 
| 
       15 
16 
     | 
    
         
             
                def by_uuid(uuid)
         
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                  retryable_request(@authorization) do |authorization|
         
     | 
| 
      
 18 
     | 
    
         
            +
                    response = self.class.get(base_url(uuid), headers: authorization.headers)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    ApiResponse.build(response)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
       18 
21 
     | 
    
         
             
                end
         
     | 
| 
       19 
22 
     | 
    
         | 
| 
       20 
23 
     | 
    
         
             
                def by_email(email)
         
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 24 
     | 
    
         
            +
                  retryable_request(@authorization) do |authorization|
         
     | 
| 
      
 25 
     | 
    
         
            +
                    response = self.class.get(base_url("email:#{email}"), headers: authorization.headers)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    ApiResponse.build(response)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
       23 
28 
     | 
    
         
             
                end
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
                # The Contact hash may contain the following parameters:
         
     | 
| 
         @@ -34,8 +39,10 @@ module RDStation 
     | 
|
| 
       34 
39 
     | 
    
         
             
                # :website
         
     | 
| 
       35 
40 
     | 
    
         
             
                # :tags
         
     | 
| 
       36 
41 
     | 
    
         
             
                def update(uuid, contact_hash)
         
     | 
| 
       37 
     | 
    
         
            -
                   
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 42 
     | 
    
         
            +
                  retryable_request(@authorization) do |authorization|
         
     | 
| 
      
 43 
     | 
    
         
            +
                    response = self.class.patch(base_url(uuid), :body => contact_hash.to_json, :headers => authorization.headers)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    ApiResponse.build(response)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
       39 
46 
     | 
    
         
             
                end
         
     | 
| 
       40 
47 
     | 
    
         | 
| 
       41 
48 
     | 
    
         
             
                #
         
     | 
| 
         @@ -47,14 +54,16 @@ module RDStation 
     | 
|
| 
       47 
54 
     | 
    
         
             
                #   Contact data
         
     | 
| 
       48 
55 
     | 
    
         
             
                #
         
     | 
| 
       49 
56 
     | 
    
         
             
                def upsert(identifier, identifier_value, contact_hash)
         
     | 
| 
       50 
     | 
    
         
            -
                   
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 57 
     | 
    
         
            +
                  retryable_request(@authorization) do |authorization|
         
     | 
| 
      
 58 
     | 
    
         
            +
                    path = "#{identifier}:#{identifier_value}"
         
     | 
| 
      
 59 
     | 
    
         
            +
                    response = self.class.patch(base_url(path), body: contact_hash.to_json, headers: authorization.headers)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    ApiResponse.build(response)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
       53 
62 
     | 
    
         
             
                end
         
     | 
| 
       54 
63 
     | 
    
         | 
| 
       55 
64 
     | 
    
         
             
                private
         
     | 
| 
       56 
65 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                def base_url(path =  
     | 
| 
      
 66 
     | 
    
         
            +
                def base_url(path = '')
         
     | 
| 
       58 
67 
     | 
    
         
             
                  "https://api.rd.services/platform/contacts/#{path}"
         
     | 
| 
       59 
68 
     | 
    
         
             
                end
         
     | 
| 
       60 
69 
     | 
    
         
             
              end
         
     | 
    
        data/lib/rdstation/error.rb
    CHANGED
    
    | 
         @@ -19,11 +19,13 @@ module RDStation 
     | 
|
| 
       19 
19 
     | 
    
         
             
                class Conflict < Error; end
         
     | 
| 
       20 
20 
     | 
    
         
             
                class UnsupportedMediaType < Error; end
         
     | 
| 
       21 
21 
     | 
    
         
             
                class UnprocessableEntity < Error; end
         
     | 
| 
      
 22 
     | 
    
         
            +
                class TooManyRequests < Error; end
         
     | 
| 
       22 
23 
     | 
    
         
             
                class InternalServerError < Error; end
         
     | 
| 
       23 
24 
     | 
    
         
             
                class NotImplemented < Error; end
         
     | 
| 
       24 
25 
     | 
    
         
             
                class BadGateway < Error; end
         
     | 
| 
       25 
26 
     | 
    
         
             
                class ServiceUnavailable < Error; end
         
     | 
| 
       26 
27 
     | 
    
         
             
                class ServerError < Error; end
         
     | 
| 
      
 28 
     | 
    
         
            +
                class UnknownError < Error; end
         
     | 
| 
       27 
29 
     | 
    
         | 
| 
       28 
30 
     | 
    
         
             
                # 400 - Bad Request
         
     | 
| 
       29 
31 
     | 
    
         
             
                class ConflictingField < BadRequest; end
         
     | 
| 
         @@ -1,9 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module RDStation
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Error
         
     | 
| 
       3 
5 
     | 
    
         
             
                class Format
         
     | 
| 
       4 
     | 
    
         
            -
                  FLAT_HASH = 'FLAT_HASH' 
     | 
| 
       5 
     | 
    
         
            -
                  HASH_OF_ARRAYS = 'HASH_OF_ARRAYS' 
     | 
| 
       6 
     | 
    
         
            -
                  ARRAY_OF_HASHES = 'ARRAY_OF_HASHES' 
     | 
| 
      
 6 
     | 
    
         
            +
                  FLAT_HASH = 'FLAT_HASH'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  HASH_OF_ARRAYS = 'HASH_OF_ARRAYS'
         
     | 
| 
      
 8 
     | 
    
         
            +
                  ARRAY_OF_HASHES = 'ARRAY_OF_HASHES'
         
     | 
| 
      
 9 
     | 
    
         
            +
                  HASH_OF_MULTIPLE_TYPES = 'HASH_OF_MULTIPLE_TYPES'
         
     | 
| 
      
 10 
     | 
    
         
            +
                  HASH_OF_HASHES = 'HASH_OF_HASHES'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  SINGLE_HASH = 'SINGLE_HASH'
         
     | 
| 
       7 
12 
     | 
    
         | 
| 
       8 
13 
     | 
    
         
             
                  def initialize(errors)
         
     | 
| 
       9 
14 
     | 
    
         
             
                    @errors = errors
         
     | 
| 
         @@ -11,20 +16,41 @@ module RDStation 
     | 
|
| 
       11 
16 
     | 
    
         | 
| 
       12 
17 
     | 
    
         
             
                  def format
         
     | 
| 
       13 
18 
     | 
    
         
             
                    return FLAT_HASH if flat_hash?
         
     | 
| 
      
 19 
     | 
    
         
            +
                    return SINGLE_HASH if single_hash?
         
     | 
| 
       14 
20 
     | 
    
         
             
                    return HASH_OF_ARRAYS if hash_of_arrays?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    return HASH_OF_HASHES if hash_of_hashes?
         
     | 
| 
      
 22 
     | 
    
         
            +
                    return HASH_OF_MULTIPLE_TYPES if hash_of_multiple_types?
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       15 
24 
     | 
    
         
             
                    ARRAY_OF_HASHES
         
     | 
| 
       16 
25 
     | 
    
         
             
                  end
         
     | 
| 
       17 
26 
     | 
    
         | 
| 
       18 
27 
     | 
    
         
             
                  private
         
     | 
| 
       19 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                  def single_hash?
         
     | 
| 
      
 30 
     | 
    
         
            +
                    return unless @errors.is_a?(Hash)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    @errors.key?('error')
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       20 
35 
     | 
    
         
             
                  def flat_hash?
         
     | 
| 
       21 
36 
     | 
    
         
             
                    return unless @errors.is_a?(Hash)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       22 
38 
     | 
    
         
             
                    @errors.key?('error_type')
         
     | 
| 
       23 
39 
     | 
    
         
             
                  end
         
     | 
| 
       24 
40 
     | 
    
         | 
| 
       25 
41 
     | 
    
         
             
                  def hash_of_arrays?
         
     | 
| 
       26 
42 
     | 
    
         
             
                    @errors.is_a?(Hash) && @errors.values.all? { |error| error.is_a? Array }
         
     | 
| 
       27 
43 
     | 
    
         
             
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def hash_of_hashes?
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @errors.is_a?(Hash) && @errors.values.all? { |error| error.is_a? Hash }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def hash_of_multiple_types?
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @errors.is_a?(Hash) &&
         
     | 
| 
      
 51 
     | 
    
         
            +
                      @errors.values.any? { |error| error.is_a? Hash } &&
         
     | 
| 
      
 52 
     | 
    
         
            +
                      @errors.values.any? { |error| error.is_a? Array }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
       28 
54 
     | 
    
         
             
                end
         
     | 
| 
       29 
55 
     | 
    
         
             
              end
         
     | 
| 
       30 
56 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require_relative './format'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module RDStation
         
     | 
| 
         @@ -11,27 +13,61 @@ module RDStation 
     | 
|
| 
       11 
13 
     | 
    
         
             
                    return @error_response unless @error_response.is_a?(Hash)
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
                    case error_format.format
         
     | 
| 
      
 16 
     | 
    
         
            +
                    when RDStation::Error::Format::SINGLE_HASH
         
     | 
| 
      
 17 
     | 
    
         
            +
                      return from_single_hash
         
     | 
| 
       14 
18 
     | 
    
         
             
                    when RDStation::Error::Format::FLAT_HASH
         
     | 
| 
       15 
19 
     | 
    
         
             
                      return from_flat_hash
         
     | 
| 
       16 
20 
     | 
    
         
             
                    when RDStation::Error::Format::HASH_OF_ARRAYS
         
     | 
| 
       17 
21 
     | 
    
         
             
                      return from_hash_of_arrays
         
     | 
| 
      
 22 
     | 
    
         
            +
                    when RDStation::Error::Format::HASH_OF_HASHES
         
     | 
| 
      
 23 
     | 
    
         
            +
                      return from_hash_of_hashes
         
     | 
| 
      
 24 
     | 
    
         
            +
                    when RDStation::Error::Format::HASH_OF_MULTIPLE_TYPES
         
     | 
| 
      
 25 
     | 
    
         
            +
                      return from_hash_of_multiple_types
         
     | 
| 
       18 
26 
     | 
    
         
             
                    end
         
     | 
| 
       19 
27 
     | 
    
         | 
| 
       20 
28 
     | 
    
         
             
                    errors
         
     | 
| 
       21 
29 
     | 
    
         
             
                  end
         
     | 
| 
       22 
30 
     | 
    
         | 
| 
      
 31 
     | 
    
         
            +
                  def from_single_hash
         
     | 
| 
      
 32 
     | 
    
         
            +
                    error_hash = @error_response.dup
         
     | 
| 
      
 33 
     | 
    
         
            +
                    error_message = error_hash.delete('error')
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    [
         
     | 
| 
      
 36 
     | 
    
         
            +
                      {
         
     | 
| 
      
 37 
     | 
    
         
            +
                        'error_type' => 'TOO_MANY_REQUESTS',
         
     | 
| 
      
 38 
     | 
    
         
            +
                        'error_message' => error_message,
         
     | 
| 
      
 39 
     | 
    
         
            +
                        'details' => error_hash
         
     | 
| 
      
 40 
     | 
    
         
            +
                      }
         
     | 
| 
      
 41 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       23 
44 
     | 
    
         
             
                  def from_flat_hash
         
     | 
| 
       24 
45 
     | 
    
         
             
                    [errors]
         
     | 
| 
       25 
46 
     | 
    
         
             
                  end
         
     | 
| 
       26 
47 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                  def  
     | 
| 
       28 
     | 
    
         
            -
                     
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                       
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                       
     | 
| 
       33 
     | 
    
         
            -
                       
     | 
| 
      
 48 
     | 
    
         
            +
                  def from_hash_of_multiple_types
         
     | 
| 
      
 49 
     | 
    
         
            +
                    array_of_errors = []
         
     | 
| 
      
 50 
     | 
    
         
            +
                    errors.each do |attribute_name, errors|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      if errors.is_a? Array
         
     | 
| 
      
 52 
     | 
    
         
            +
                        result = build_error_from_array(attribute_name, errors)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      end
         
     | 
| 
      
 54 
     | 
    
         
            +
                      if errors.is_a? Hash
         
     | 
| 
      
 55 
     | 
    
         
            +
                        result = build_error_from_multilingual_hash(attribute_name, errors)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
      
 57 
     | 
    
         
            +
                      array_of_errors.push(*result)
         
     | 
| 
       34 
58 
     | 
    
         
             
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    array_of_errors
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def from_hash_of_hashes
         
     | 
| 
      
 64 
     | 
    
         
            +
                    array_of_errors = []
         
     | 
| 
      
 65 
     | 
    
         
            +
                    errors.each do |attribute_name, errors|
         
     | 
| 
      
 66 
     | 
    
         
            +
                      result = build_error_from_multilingual_hash(attribute_name, errors)
         
     | 
| 
      
 67 
     | 
    
         
            +
                      array_of_errors.push(*result)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    array_of_errors
         
     | 
| 
       35 
71 
     | 
    
         
             
                  end
         
     | 
| 
       36 
72 
     | 
    
         | 
| 
       37 
73 
     | 
    
         
             
                  def error_format
         
     | 
| 
         @@ -39,7 +75,32 @@ module RDStation 
     | 
|
| 
       39 
75 
     | 
    
         
             
                  end
         
     | 
| 
       40 
76 
     | 
    
         | 
| 
       41 
77 
     | 
    
         
             
                  def errors
         
     | 
| 
       42 
     | 
    
         
            -
                    @errors ||= @error_response 
     | 
| 
      
 78 
     | 
    
         
            +
                    @errors ||= @error_response.fetch('errors', @error_response)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  private
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  def build_error_from_array(attribute_name, attribute_errors)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    path = { 'path' => "body.#{attribute_name}" }
         
     | 
| 
      
 85 
     | 
    
         
            +
                    attribute_errors.map { |error| error.merge(path) }
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def build_error_from_multilingual_hash(attribute_name, errors_by_language)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    array_of_errors = []
         
     | 
| 
      
 90 
     | 
    
         
            +
                    errors_by_language.each do |language, errors|
         
     | 
| 
      
 91 
     | 
    
         
            +
                      result = build_error_from_array("#{attribute_name}.#{language}", errors)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      array_of_errors.push(*result)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    array_of_errors
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def from_hash_of_arrays
         
     | 
| 
      
 98 
     | 
    
         
            +
                    errors.each_with_object([]) do |errors, array_of_errors|
         
     | 
| 
      
 99 
     | 
    
         
            +
                      attribute_name = errors.first
         
     | 
| 
      
 100 
     | 
    
         
            +
                      attribute_errors = errors.last
         
     | 
| 
      
 101 
     | 
    
         
            +
                      errors = build_error_from_array(attribute_name, attribute_errors)
         
     | 
| 
      
 102 
     | 
    
         
            +
                      array_of_errors.push(*errors)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    end
         
     | 
| 
       43 
104 
     | 
    
         
             
                  end
         
     | 
| 
       44 
105 
     | 
    
         
             
                end
         
     | 
| 
       45 
106 
     | 
    
         
             
              end
         
     |