proper syntax for generating JSON body in API call - ruby-on-rails

With the following long controller action code
#available = Available.find(694)
#tareservation_id = 8943
#request_date_time = Time.now.utc.iso8601
#request_id = Time.now.to_i
#in_date = (Date.today + 24.days).strftime("%Y-%m-%d").to_s
#book = %Q|{
"booking": {
"currencyCode": "USD",
"languageCode": "es",
"paxNationality": "ES",
"clientRef": {
"value": \"#{#tareservation_id}\",
"mustBeUnique": true
},
"items": [
{
"itemNumber": 1,
"immediateConfirmationRequired": true,
"productCode": \"#{#available.product_code}\",
"leadPaxName":
{ "firstName": "Guy",
"lastName": "Test"
},
"product":
{
"period":
{
"start": "2018-08-27",
"quantity": 2
}
}
} ]
},
"requestAuditInfo":
{ "agentCode": "001",
"requestPassword": "pass",
"requestDateTime": \"#{#requestDateTime}\",
"requestID": #{#request_id} },
"versionNumber": "2.0"
}|
This then must be shipped off to the API as JSON in the body call
#result = HTTParty.post(
'https://test.com/search',
:body => JSON.parse(#book).to_json,
headers: {
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Connection' => 'Keep-Alive'
}
)
If the following block is removed:
,
"product":
{
"period":
{
"start": "2018-08-27",
"quantity": 2
}
}
in console JSON.parse(#start), parses properly. With the block JSON::ParserError: 784: unexpected token. Yet I fail to see what is incorrect here?
Is Rails handling of string for future JSON conversion really strict on syntax, particularly since there is interpretation of instance variables - both as strings and integers - and har returns involved? What would it be then? Or is there a safer solution to get out of what quickly becomes quicksand?

It turns out that pasting too many lines into the console (iTerm2, in this case) does something to the memory. 25 lines of code pasted in a single time is the maximum observered where behaviour is as expected.

Related

How to take keep parts of an array and form a new array?

I am building a Rails 5 app.
In this app I have connected to the Google Calendar API.
The connection works fine and I get a list of calendars back.
What I need to do is to get the Id and Summary of this JSON object that I get back from Google.
This is what I get
[{
"kind": "calendar#calendarListEntry",
"etag": "\"1483552200690000\"",
"id": "xxx.com_asae#group.calendar.google.com",
"summary": "My office calendar",
"description": "For office meeting",
"location": "344 Common st",
"colorId": "8",
"backgroundColor": "#16a765",
"foregroundColor": "#000000",
"accessRole": "owner",
"defaultReminders": [],
"conferenceProperties": {
"allowedConferenceSolutionTypes": [
"hangoutsMeet"
]
}
},
{
"kind": "calendar#calendarListEntry",
"etag": "\"1483552200690000\"",
"id": "xxx.com_asae#group.calendar.google.com",
"summary": "My office calendar",
"description": "For office meeting",
"location": "344 Common st",
"colorId": "8",
"backgroundColor": "#16a765",
"foregroundColor": "#000000",
"accessRole": "owner",
"defaultReminders": [],
"conferenceProperties": {
"allowedConferenceSolutionTypes": [
"hangoutsMeet"
]
}
}]
This is what I want to end up with
[{
"id": "xxx.com_asae#group.calendar.google.com",
"title": "My office calendar",
}]
The purpose of this is that I want to populate a selectbox using Selectize plugin
Another way to achieve removing of certain keys in your hash is by using Hash#reject method:
response = { your_json_response }
expected = [response[0].reject {|k| k != :id && k != :summary}]
The original response remains unchanged while a mutated copy of the original response is returned.
You can filter the desierd keys with the select method:
responde = {your_json_response}
expected = [response[0].select{|k,v| ['id','title'].include?(k)}]
response[0] retrieves the hash, and the select compares each key with the ones you want and returns a hash with only those key: value pairs.
EDIT: I missed that you don't have a "title" key on the original response, I would do this then:
response = {your_json_response}
h = response[0]
expected = [{'id' => h['id'], 'title' => h['summary']}]
EDIT 2: Sorry, the first example was not clear that there would be multiple hashes
expected = response.map{|h| {'id' => h['id'], 'title' => h['summary']}}
map iterates over each element of response and returns the result of the block applied for each iteration as an array, so the blocks is apllied to each h and it generates a new hash from it
I suggest this approach.
expected = response.each { |h| h.keep_if { |k, _| k == :id || k == :summary } }
It returns just the required pairs:
# => [{:id=>"xxx.com_asae#group.calendar.google.com", :summary=>"My office calendar"}, {:id=>"xxx.com_asae#group.calendar.google.com", :summary=>"My office calendar"}]
To remove duplicates, just do expected.uniq
If you need to change the key name :summary to :title do:
expected = expected.each { |h| h[:title] = h.delete(:summary) }
One liner
expected = response.each { |h| h.keep_if { |k, _| k == :id || k == :summary } }.each { |h| h[:title] = h.delete(:summary) }.uniq
Of course, maybe it is better to move .uniq as first method expected = response.uniq.each { .....

Docusign integration with rails 4

I am using the docusign_rest gem for DocuSign REST API, and following are my DocuSign configuration.
# config/initializers/docusign_rest.rb
require 'docusign_rest'
DocusignRest.configure do |config|
config.username = 'myemail#email.com'
config.password = 'MyPassword'
config.integrator_key = 'My-key'
config.account_id = 'account_id'
config.endpoint = 'https://www.docusign.net/restapi'
config.api_version = 'v1'
end
When I try to connect and get account_id, I get nil as a response.
client = DocusignRest::Client.new
puts client.get_account_id # Returns nil.
I am using rails-4.1.4 and ruby-2.2.2
What did I miss? Please suggest.
Not sure if you figured this out or not quite yet. Here is another solution that wasn't too difficult using httparty. If you're trying to create a document for a template for example, your request might look like so:
baseUrl = "https://demo.docusign.net/restapi/v2/accounts/acct_number/envelopes"
#lease = Lease.find(lease.id)
#unit = #lease.unit
#application = #lease.application
#manager = #lease.property_manager
#application.applicants.each do |renter|
req = HTTParty.post(baseUrl,
body: {
"emailSubject": "DocuSign API call - Request Signature - Boom",
"templateId": "id of your template",
"templateRoles": [{
"name": "#{renter.background.legal_name}",
"email": "#{renter.email}",
"recipientId": "1",
"roleName": "Lessee",
"tabs": {
"texttabs": [{
"tablabel": "Rent",
"value": "#{#lease.rent}"
},{
"tablabel": "Address",
"value": "987 apple lane"
}]
}
},{
"email": "#{#manager.email}",
"name": "#{#manager.name}",
"roleName": "Lessor",
"tabs": {
"texttabs": [{
"tablabel": "Any",
"value": "#{#lease.labels}"
},{
"tablabel": "Address",
"value": "987 hoser lane"
}]
}
}],
"status": "sent"
}.to_json,
headers: {
"Content-Type" => "application/json",
'Accept' => 'application/json',
'X-DocuSign-Authentication' => '{
"Username" : "place your",
"Password" : "credentials",
"IntegratorKey" : "here"
}'
}, :debug_output => $stdout )
debug output on the final line is to allow you to debug the api request, it can be removed at any time.
This was a bug in docusign_rest 0.1.1; that method always returned nil. That bug has been fixed and the latest gem version includes that fix.

How do I safely parse data from a Ruby Hash?

I'm parsing a JSON result into a Ruby hash. The JSON result looks like this:
{
"records": [
{
"recordName": "7DBC4FAD-D18C-476A-89FB-14A515098F34",
"recordType": "Media",
"fields": {
"data": {
"value": {
"fileChecksum": "ABCDEFGHIJ",
"size": 9633842,
"downloadURL": "https://cvws.icloud-content.com/B/ABCDEF"
},
"type": "ASSETID"
}
},
"recordChangeTag": "ii23box2",
"created": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
},
"modified": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
}
}
]
}
I can't guarantee that it'll return with any/all those values, or that each value will be of a certain type (e.g. Array, Hash, string, number), and if I call it incorrectly then I get a crash.
Right now I need the downloadURL for the first item in the 'records' array, or to write it as I might with the Swift library SwiftyJSON (which I'm far more familiar with):
json["records"][0]["fields"]["data"]["value"]["downloadURL"]
I'm wondering what the safest/best/standard way to do this safely in Ruby is. Perhaps I'm thinking about it wrong?
In ruby 2.3 and above you can use Hash#dig and Array#dig
json = JSON.parse(...)
json.dig('records', 0, 'fields', 'data', 'value', 'downloadURL')
You'll get nil if any of the intermediate values is nil. If one of the intermediate values doesn't have a dig method, for example if `json['records'][0]['fields'] was unexpectedly an integer this will raise TypeError.
From the documentation (http://ruby-doc.org/stdlib-2.2.3/libdoc/json/rdoc/JSON.html):
require 'json'
my_hash = JSON.parse('{"hello": "goodbye"}')
puts my_hash["hello"] => "goodbye"
If you're worried that you might not have some data. See this question:
Equivalent of .try() for a hash to avoid "undefined method" errors on nil?
You can recursively search each object contained in the json object using
the recurse_proc method of the JSON module.
Here is an example using the data you provided.
require 'json'
json_string = '{
"records": [
{
"recordName": "7DBC4FAD-D18C-476A-89FB-14A515098F34",
"recordType": "Media",
"fields": {
"data": {
"value": {
"fileChecksum": "ABCDEFGHIJ",
"size": 9633842,
"downloadURL": "https://cvws.icloud-content.com/B/ABCDEF"
},
"type": "ASSETID"
}
},
"recordChangeTag": "ii23box2",
"created": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
},
"modified": {
"timestamp": 1449863552482,
"userRecordName": "_abcdef",
"deviceID": "12345"
}
}
]
}'
json_obj = JSON.parse(json_string)
JSON.recurse_proc(json_obj) do |obj|
if obj.is_a?(Hash) && obj['downloadURL']
puts obj['downloadURL']
end
end
Update Based on Frederick's answer and Cary's comment
I originally assumed you just wanted to find the downloadURL somewhere in the json without crashing, but based on Frederick's answer and Cary's comment, it's reasonable to assume that you only want to find the downloadURL if it is at the exact path, rather than if it just exists. Building on Frederick's answer and Cary's comment here are a couple of other options that should safely find the downloadURL at the expected path.
path = ['records', 0, 'fields', 'data', 'value', 'downloadURL']
parsed_json_obj = JSON.parse(json_string)
node_value = path.reduce(parsed_json_obj) do |json,node|
if json.is_a?(Hash) || (json.is_a?(Array) && node.is_a?(Integer))
path = path.drop 1
json[node]
else
node unless node == path.last
end
end
puts node_value || "not_found"
path = ['records', 0, 'fields', 'data', 'value', 'downloadURL']
begin
node_value = parsed_json_obj.dig(*path)
rescue TypeError
node_value = "not_found"
end
puts node_value || "not_found"
BTW, this assumes the json is at least valid, if that is not a given you might want to wrap the JSON.parse in a begin-rescue-end block as well.

Successful consumption of a Azure Machine Learning API?

Does anyone have good documentation of a successful implementation of the Azure ML studio API in a web app that's not ASP.net? I'd like to run on it with ruby on rails, but I guess I have to figure it out on my own.
It is simply a rest API call. Look at this...
data = {
"Inputs": {
"input1":
{
"ColumnNames": ["YearBuild", "City", "State", "HomeType", "TaxAssesmentYear", "LotSize", "HomeSize", "NumBedrooms"],
"Values": [ [ "0", "Anchorage", "AK ", "Apartment", "0", "0", "0", "0" ], [ "0", "Anchorage", "AK ", "Apartment", "0", "0", "0", "0" ], ]
}, },
"GlobalParameters": {
}
}
body = str.encode(json.dumps(data))
url = 'https://ussouthcentral.services.azureml.net/workspaces/45aeb4d8283d4be6ae211592f5366af5/services/07ffeeb6fcb84f16bc62cdcf67fd95b3/execute?api-version=2.0&details=true'
api_key = 'abc123' # Replace this with the API key for the web service
headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key)}
req = urllib2.Request(url, body, headers)
Give a shot at trying with the postman app in chrome first. Setting your headers, just as above, your data goes in the post payload in the json format.
Here you'll find Ruby code (not python)
data = {
'Inputs' => {
'input1' => [
{
'weekday' => 1,
'hour' => 2,
'events' => 0
}
]
},
'GlobalParameters' => {}
}
body = data.to_json
url = 'https://asiasoutheast.services.azureml.net/subscriptions/[tour stuff...]execute?api-version=2.0&format=swagger'
api_key = '[your api key]'
headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer '+ api_key)}
RestClient::Request.execute(method: :post, url: url, payload: body, headers: headers)

Convert JSON into a Hash

I have this JSON and I am trying to send it to a Rails API from Postman:
{"object":
{
"type": "out",
"vars":
{
"x": "x",
"y": "y"
},
"values":
{
"ts": "timestamp",
"ok":
{
"total": 2,
"min": "x",
"max": "y"
},
"error":
{
"total": 2,
"error1": "first",
"error2": "second"
}
}
}
}
I need to convert this into a Hash in my model so that I can manipulate it with before_create. Here's what I came with:
object = self.to_json # => converts object to json
object = JSON.parse(object) # => converts json to hash
1st problem: I get this (id=>nil is not relevant since it will be inserted automatically in the database):
{"id"=>nil, "type"=>"out", "vars"=>{"x"=>"x", "y"=>"y"}, "values"=>{"ts"=>"timestamp", "ok"=>"{\"total\"=>2, \"min\"=>\"x\", \"max\"=>\"y\"}", "error"=>"{\"total\"=>2, \"error1\"=>\"first\", \"error2\"=>\"second\"}"}, "created_at"=>"2015-01-29T15:45:01.329Z", "updated_at"=>"2015-01-29T15:45:01.329Z"}
and when I try to manipulate object["values"]["ok"], Rails sends the error:
unexpected token at '"{\"total\"=\u003e2, \"min\"=\u003e\"x\", \"max\"=\u003e\"y\"}"'
2nd problem: I can only call object["values"], and I want to call it with a symbol, not a string object[:values].
Solved my issues using:
object = self.as_json.with_indifferent_access
# => allowing me to use a symbol key instead of a string
ok_vals = object[:values][:ok].as_json.gsub(/\=\>/, ':')
# => allowing to change json string '{"val1"=>"val1", "val2"=>"val2"}' to '{"val1":"val1", "val2":"val2"}'
ok_vals = JSON.parse(ok_vals)
# => which transform json string to hash {val1: "val1", val2: "val2"}
Feel free to make any suggestions to this code. Thanks for the help.

Resources