Class: Sdk4me::Client

Inherits:
Object
  • Object
show all
Includes:
SendWithRateLimitBlock, SendWithRetries
Defined in:
lib/sdk4me/client.rb,
lib/sdk4me/client/version.rb

Constant Summary collapse

MAX_PAGE_SIZE =
100
DEFAULT_HEADER =
{
  'Content-Type' => 'application/json',
  'User-Agent' => "4me-sdk-ruby/#{Sdk4me::Client::VERSION}"
}.freeze
VERSION =
'2.0.4'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Client

Create a new 4me SDK Client

Shared configuration for all 4me SDK Clients:

Sdk4me.configure do |config|
  config.access_token = 'd41f5868feb65fc87fa2311a473a8766ea38bc40'
  config.account = 'my-sandbox'
  ...
end

Override configuration per 4me SDK Client: sdk4me = Sdk4me::Client.new(account: 'trusted-sandbox')

All options available:

- logger:      The Ruby Logger instance, default: Logger.new(STDOUT)
- host:        The 4me REST API host, default: 'https://api.4me.com'
- api_version: The 4me REST API version, default: 'v1'
- access_token: *required* The 4me access token
- account:     Specify a different (trusted) account to work with
               @see https://developer.4me.com/v1/#multiple-accounts
- source:      The Source used when creating new records
               @see https://developer.4me.com/v1/general/source/
- user_agent:  The User-Agent header of each request

- max_retry_time: maximum nr of seconds to wait for server to respond (default = 5400 = 1.5 hours)
                  the sleep time between retries starts at 2 seconds and doubles after each retry
                  retry times: 2, 6, 18, 54, 162, 486, 1458, 4374, 13122, ... seconds
                  one retry will always be performed unless you set the value to -1
- read_timeout:   HTTP GET read timeout in seconds (default = 25)
- block_at_rate_limit: Set to +true+ to block the request until the rate limit is lifted, default: +false+
                       @see https://developer.4me.com/v1/#rate-limiting

- proxy_host:     Define in case HTTP traffic needs to go through a proxy
- proxy_port:     Port of the proxy, defaults to 8080
- proxy_user:     Proxy user
- proxy_password: Proxy password


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/sdk4me/client.rb', line 64

def initialize(options = {})
  @options = Sdk4me.configuration.current.merge(options)
  %i[host api_version].each do |required_option|
    raise ::Sdk4me::Exception, "Missing required configuration option #{required_option}" if option(required_option).blank?
  end
  @logger = @options[:logger]
  @ssl, @domain, @port = ssl_domain_port_path(option(:host))
  unless option(:access_token).present?
    if option(:api_token).blank?
      raise ::Sdk4me::Exception, 'Missing required configuration option access_token'
    else
      @logger.info('DEPRECATED: Use of api_token is deprecated, switch to using access_token instead. -- https://developer.4me.com/v1/#authentication')
    end
  end
  @ssl_verify_none = options[:ssl_verify_none]
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



213
214
215
# File 'lib/sdk4me/client.rb', line 213

def logger
  @logger
end

Instance Method Details

#delete(path, params = {}, header = {}) ⇒ Object

send HTTPS DELETE request and return instance of Sdk4me::Response



114
115
116
# File 'lib/sdk4me/client.rb', line 114

def delete(path, params = {}, header = {})
  _send(Net::HTTP::Delete.new(expand_path(path, params), expand_header(header)))
end

#each(path, params = {}, header = {}, &block) ⇒ Object

Yield all retrieved resources one-by-one for the given (paged) API query. Raises an ::Sdk4me::Exception with the response retrieved from 4me is invalid Returns total nr of resources yielded (for logging)



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/sdk4me/client.rb', line 89

def each(path, params = {}, header = {}, &block)
  # retrieve the resources using the max page size (least nr of API calls)
  next_path = expand_path(path, { per_page: MAX_PAGE_SIZE, page: 1 }.merge(params))
  size = 0
  while next_path
    # retrieve the records (with retry and optionally wait for rate-limit)
    response = get(next_path, {}, header)
    # raise exception in case the response is invalid
    raise ::Sdk4me::Exception, response.message unless response.valid?

    # yield the resources
    response.json.each(&block)
    size += response.json.size
    # go to the next page
    next_path = response.pagination_relative_link(:next)
  end
  size
end

#export(types, from = nil, block_until_completed = false, locale = nil) ⇒ Object

Export CSV files

Parameters:

  • types:

    The types to export, e.g. person, organization, people_contact_details

  • from:

    Retrieve all files since a given data and time

  • block_until_completed:

    Set to true to monitor the export progress

  • locale:

    Required for translations export

Raises:

  • Sdk4me::Exception in case the export progress could not be monitored



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/sdk4me/client.rb', line 174

def export(types, from = nil, block_until_completed = false, locale = nil)
  data = { type: [types].flatten.join(',') }
  data[:from] = from unless from.blank?
  data[:locale] = locale unless locale.blank?
  response = post('/export', data)
  if response.valid?
    if response.raw.code.to_s == '204'
      @logger.info { "No changed records for '#{data[:type]}' since #{data[:from]}." }
      return response
    end
    @logger.info { "Export for '#{data[:type]}' successfully queued with token '#{response[:token]}'." }
  end

  if block_until_completed
    raise ::Sdk4me::UploadFailed, "Failed to queue '#{data[:type]}' export. #{response.message}" unless response.valid?

    token = response[:token]
    loop do
      response = get("/export/#{token}")
      return response if response[:state] == 'error'

      unless response.valid?
        sleep(5)
        response = get("/export/#{token}") # single retry to recover from a network error
        return response if response[:state] == 'error'

        raise ::Sdk4me::Exception, "Unable to monitor progress for '#{data[:type]}' export. #{response.message}" unless response.valid?
      end
      # wait 30 seconds while the response is OK and export is still busy
      break unless %w[queued processing].include?(response[:state])

      @logger.debug { "Export of '#{data[:type]}' is #{response[:state]}. Checking again in 30 seconds." }
      sleep(30)
    end
  end

  response
end

#get(path, params = {}, header = {}) ⇒ Object

send HTTPS GET request and return instance of Sdk4me::Response



109
110
111
# File 'lib/sdk4me/client.rb', line 109

def get(path, params = {}, header = {})
  _send(Net::HTTP::Get.new(expand_path(path, params), expand_header(header)))
end

#import(csv, type, block_until_completed = false) ⇒ Object

upload a CSV file to import

Parameters:

  • csv:

    The CSV File or the location of the CSV file

  • type:

    The type, e.g. person, organization, people_contact_details

Raises:

  • Sdk4me::UploadFailed in case the file could was not accepted by SDK4ME and block_until_completed is true

  • Sdk4me::Exception in case the import progress could not be monitored



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/sdk4me/client.rb', line 134

def import(csv, type, block_until_completed = false)
  csv = File.open(csv, 'rb') unless csv.respond_to?(:path) && csv.respond_to?(:read)
  data, headers = Sdk4me::Multipart::Post.prepare_query(type: type, file: csv)
  request = Net::HTTP::Post.new(expand_path('/import'), expand_header(headers))
  request.body = data
  response = _send(request)
  @logger.info { "Import file '#{csv.path}' successfully uploaded with token '#{response[:token]}'." } if response.valid?

  if block_until_completed
    raise ::Sdk4me::UploadFailed, "Failed to queue #{type} import. #{response.message}" unless response.valid?

    token = response[:token]
    loop do
      response = get("/import/#{token}")
      return response if response[:state] == 'error'

      unless response.valid?
        sleep(5)
        response = get("/import/#{token}") # single retry to recover from a network error
        return response if response[:state] == 'error'

        raise ::Sdk4me::Exception, "Unable to monitor progress for #{type} import. #{response.message}" unless response.valid?
      end
      # wait 30 seconds while the response is OK and import is still busy
      break unless %w[queued processing].include?(response[:state])

      @logger.debug { "Import of '#{csv.path}' is #{response[:state]}. Checking again in 30 seconds." }
      sleep(30)
    end
  end

  response
end

#option(key) ⇒ Object

Retrieve an option



82
83
84
# File 'lib/sdk4me/client.rb', line 82

def option(key)
  @options[key]
end

#patch(path, data = {}, header = {}) ⇒ Object Also known as: put

send HTTPS PATCH request and return instance of Sdk4me::Response



119
120
121
# File 'lib/sdk4me/client.rb', line 119

def patch(path, data = {}, header = {})
  _send(json_request(Net::HTTP::Patch, path, data, expand_header(header)))
end

#post(path, data = {}, header = {}) ⇒ Object

send HTTPS POST request and return instance of Sdk4me::Response



125
126
127
# File 'lib/sdk4me/client.rb', line 125

def post(path, data = {}, header = {})
  _send(json_request(Net::HTTP::Post, path, data, expand_header(header)))
end