
Research
PyPI Package Disguised as Instagram Growth Tool Harvests User Credentials
A deceptive PyPI package posing as an Instagram growth tool collects user credentials and sends them to third-party bot services.
Client for accessing the 4me REST API
Add this line to your application's Gemfile:
gem '4me-sdk'
And then execute:
$ bundle
Or install it yourself as:
$ gem install 4me-sdk
Sdk4me.configure do |config|
config.access_token = 'd41f5868feb65fc87fa2311a473a8766ea38bc40'
config.account = 'my-sandbox'
config.logger = Rails.logger
...
end
All options available:
Logger.new(STDOUT)
true
to block the request until the rate limit is lifted, default: true
Retry-After
header is used to compute when the retry should be performed. If that moment is later than the max_throttle_time the request will not be blocked and the throttled response is returned.Each time an 4me SDK Client is instantiated it is possible to override the global configuration like so:
client = Sdk4me::Client.new(account: 'trusted-sandbox', source: 'my special integration')
The proxy settings are limited to basic authentication only. In case ISA-NTLM authentication is required, make sure to setup a local proxy configured to forward the requests. And example local proxy host for Windows is Fiddle.
Minimal example:
require 'sdk4me/client'
client = Sdk4me::Client.new(access_token: '3a4e4590179263839...')
response = client.get('me')
puts response[:primary_email]
The get
method can be used to retrieve a single record from SDK4ME.
response = Sdk4me::Client.new.get('organizations/4321')
puts response[:name]
By default this call will return all fields of the Organization.
The fields can be accessed using symbols and strings, and it is possible chain a number of keys in one go:
response = Sdk4me::Client.new.get('organizations/4321')
puts response[:parent][:name] # this may throw an error when +parent+ is +nil+
puts response[:parent, :name] # using this format you will retrieve +nil+ when +parent+ is +nil+
puts response['parent', 'name'] # strings are also accepted as keys
Although the get
method can be also used to retrieve a collection of records from SDK4ME, the preferred way is to use the each
method.
count = Sdk4me::Client.new.each('organizations') do |organization|
puts organization[:name]
end
puts "Found #{count} organizations"
By default this call will return all collection fields for each Organization. For more fields, check out the field selection documentation.
The fields can be accessed using symbols and strings, and it is possible chain a number of keys in one go:
count = Sdk4me::Client.new.each('organizations', fields: 'parent') do |organization|
puts organization[:parent][:name] # this may throw an error when +parent+ is +nil+
puts organization[:parent, :name] # using this format you will retrieve +nil+ when +parent+ is +nil+
puts organization['parent', 'name'] # strings are also accepted as keys
end
Note that an Sdk4me::Exception
could be thrown in case one of the API requests fails. When using the blocking options the chances of this happening are rather small and you may decide not to explicitly catch the Sdk4me::Exception
here, but leave it up to a generic exception handler.
The each
method described above is the preferred way to work with collections of data.
If you really want to paginate yourself, the get
method is your friend.
@client = Sdk4me::Client.new
response = @client.get('organizations', { per_page: 100 })
puts response.json # all data in an array
puts "showing page #{response.current_page}/#{response.total_pages}, with #{response.per_page} records per page"
puts "total number of records #{response.total_entries}"
# retrieve collection for previous and next pages directly from the response
prev_page = @client.get(response.pagination_relative_link(:prev))
next_page = @client.get(response.pagination_relative_link(:next))
By default this call will return all collection fields for each Organization. For more fields, check out the field selection documentation.
The fields can be accessed using symbols and strings, and it is possible chain a number of keys in one go:
response = Sdk4me::Client.new.get('organizations', { per_page: 100, fields: 'parent' })
puts response[:parent, :name] # an array with the parent organization names
puts response['parent', 'name'] # strings are also accepted as keys
Creating new records is done using the post
method.
response = Sdk4me::Client.new.post('people', {primary_email: 'new.user@example.com', organization_id: 777})
if response.valid?
puts "New person created with id #{response[:id]}"
else
puts response.message
end
Make sure to validate the success by calling response.valid?
and to take appropriate action in case the response is not valid.
Updating records is done using the put
method.
response = Sdk4me::Client.new.put('people/888', {name: 'Mrs. Susan Smith', organization_id: 777})
if response.valid?
puts "Person with id #{response[:id]} successfully updated"
else
puts response.message
end
Make sure to validate the success by calling response.valid?
and to take appropriate action in case the response is not valid.
Deleting records is done using the delete
method.
response = Sdk4me::Client.new.delete('organizations/88/addresses/')
if response.valid?
puts "Addresses of Organization with id #{response[:id]} successfully removed"
else
puts response.message
end
Make sure to validate the success by calling response.valid?
and to take appropriate action in case the response is not valid.
Adding attachments and inline images to rich text fields is a two-step process. First upload the attachments to 4me, and then refer to the uploaded attachments when creating or updating a 4me record.
To make this process easy, refer to the files using string filepaths or File references:
response = Sdk4me::Client.new.put('requests/416621', {
status: 'waiting_for_customer',
note: 'Please complete the attached forms and assign the request back to us.',
note_attachments: [
'/tmp/forms/README.txt',
File.open('/tmp/forms/PersonalData.xls', 'rb')
]
})
It is also possible to add inline attachments. Refer to attachments by their array index, with the index being zero-based. Text can only refer to inline images in its own attachments collection. For example:
begin
data = {
status: 'waiting_for_customer',
note: 'See [note_attachments: 0] and [note_attachments: 1]',
note_attachments: ['/tmp/info.png', '/tmp/help.png']
}
response = Sdk4me::Client.new.put('requests/416621', data)
if response.valid?
puts "Request #{response[:id]} updated and attachments added to the note"
else
puts "Update of request failed: #{response.message}"
end
catch Sdk4me::UploadFailed => ex
puts "Could not upload an attachment: #{ex.message}"
end
4me also provides an Import API. The 4me SDK Client can be used to upload files to that API.
response = Sdk4me::Client.new.import('\tmp\people.csv', 'people')
if response.valid?
puts "Import queued with token #{response[:token]}"
else
puts "Import upload failed: #{response.message}"
end
The second argument contains the import type.
It is also possible to monitor the progress of the import and block until the import is complete. In that case you will need to add some exception handling to your code.
begin
response = Sdk4me::Client.new.import('\tmp\people.csv', 'people', true)
puts response[:state]
puts response[:results]
puts response[:logfile]
unless response.valid?
puts "Import completed with errors: #{response[:message]}"
end
catch Sdk4me::UploadFailed => ex
puts "Could not upload the people import file: #{ex.message}"
catch Sdk4me::Exception => ex
puts "Unable to monitor progress of the people import: #{ex.message}"
end
Note that blocking for the import to finish is required when you import multiple CSVs that are dependent on each other.
4me also provides an Export API. The 4me SDK Client can be used to download (zipped) CSV files using that API.
response = Sdk4me::Client.new.export(['people', 'people_contact_details'], DateTime.new(2012,03,30,23,00,00))
if response.valid?
puts "Export queued with token #{response[:token]}"
else
puts "Export failed: #{response.message}"
end
The first argument contains the export types. The second argument is optional and limits the export to all changed records since the given time.
It is also possible to monitor the progress of the export and block until the export is complete. In that case you will need to add some exception handling to your code.
require 'open-uri'
begin
response = Sdk4me::Client.new.export(['people', 'people_contact_details'], nil, true)
puts response[:state]
if response.valid?
# write the export file to disk
File.open('/tmp/export.zip', 'wb') { |f| f.write(open(response[:url]).read) }
else
puts "Export failed with errors: #{response[:message]}"
puts response[:logfile]
end
catch Sdk4me::UploadFailed => ex
puts "Could not queue the people export: #{ex.message}"
catch Sdk4me::Exception => ex
puts "Unable to monitor progress of the people export: #{ex.message}"
end
Note that blocking for the export to finish is recommended as you will get direct access to the exported file.
When the currently used API token hits the 4me rate limiter a HTTP 429 response is returned that specifies after how many seconds the rate limit will be lifted.
If that time lies within the max_throttle_time the 4me SDK Client will wait and retry the action, if not, the throttled response will be returned. You can verify if a response was throttled using:
response = client.get('me')
puts response.throttled?
By setting the block_at_rate_limit to false
in the configuration the 4me SDK Client will never wait when a rate limit is hit.
Note that 4me has different rate limiters. If the intention is to only wait when the short-burst (max 20 requests in 2 seconds) rate limiter is hit, you could set the max_throttle_time to e.g. 5 seconds.
When exporting translations, the locale parameter is required:
response = Itrp::Client.new.export('translations', nil, false, 'nl')
The standard methods get
, post
, put
and delete
will always return a Response with an error message in case something went wrong.
By calling response.valid?
you will know if the action succeeded or not, and response.message
provides additinal information in case the response was invalid.
response = Sdk4me::Client.new.get('organizations/1a2b')
puts response.valid?
puts response.message
The methods each
and import
may throw an Sdk4me::Exception
in case something failed, see the examples above.
The changelog is available here.
Copyright (c) 2022 4me, Inc. See LICENSE for further details.
FAQs
Unknown package
We found that 4me-sdk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
A deceptive PyPI package posing as an Instagram growth tool collects user credentials and sends them to third-party bot services.
Product
Socket now supports pylock.toml, enabling secure, reproducible Python builds with advanced scanning and full alignment with PEP 751's new standard.
Security News
Research
Socket uncovered two npm packages that register hidden HTTP endpoints to delete all files on command.