One of my model's in swagger returns a 'Status' property that could be one of multiple strings.
for example:
"Status" : {
"type":"string",
"example" : "'Completed' || 'Group Completed' || 'Errored' || 'Group Errored' || 'Ready' || 'Stopped By User' || 'Initializing' || 'Queued' || 'Group Queued' || 'Running' || 'Group Running' || 'Stopping' || 'Group Stopping'"
},
Doing it this way just looks really messy. What is the best way to display all these possible outcomes to the user?
Define the enum for this property:
"Status" : {
"type": "string",
"enum": [
"Completed",
"Group Completed",
"Errored",
...
]
}
The documentation renderer will display all enum values in the schema documentation.
Related
I have an API. I want to check all the blank values in the request body and reject them. Also I have few attributes which can have either true / false (boolean) as value.
sample request body
{
"data": {
"id": 123,
"type": "abc",
"attributes": {
"is_abc": false,
"name": "gtd",
"is_dgc": true
}
}
}
I am validating if the request body has any blank values and raise exception if any.
def validate_blank_values(params)
blank_keys = params.map { |key, value| key if value.blank? }.compact
raise exception
end
value.blank? rejects even the boolean attributes when the request has false as its value which is a valid value here. Tried
value.present?
Also behaves the same way. The problem with .nil? is it returns false for empty string also.
''.nil? => false
The problem with .empty? is
false.empty? => (undefined method `empty?' for false:FalseClass)
I want to reject the values when they are
blank
empty string
nil
do not reject when
proper value
false as value
Suggest me a solution for this
There more then one way:
Solution 1
Converting to string, striping spaces and then testing if empty would do the job
value = false
As stated by #engineersmnky With rails you can use blank?:
value.to_s.blank?
With 2 conditions
value.class == String && value.blank?
With only ruby:
value.to_s.strip.empty?
With Regex
/^\s*$/.match?(value.to_s)
With 2 conditions
value.class == String && value.strip.empty?
To recursively test json
Option 1 With map and reduce
def is_empty(tmp)
tmp.map {
|k, v| v.class == Hash ? is_empty(v) : v.to_s.strip.empty?
}.reduce { |r1, r2| r1 || r2 }
end
json = {
"data": {
"id": 123,
"type": "abc",
"attributes": {
"is_abc": false,
"name": "gtd",
"is_dgc": true
}
}
}
puts is_empty(json)
Option 2 With reduce
def is_empty(tmp)
tmp.reduce(false) {
|result, (k, v)| v.class == Hash ? is_empty(v) : result || v.to_s.strip.empty?
}
end
json = {
"data": {
"id": 123,
"type": "abc",
"attributes": {
"is_abc": false,
"name": "gtd",
"is_dgc": true
}
}
}
puts is_empty(json)
You need to use multiple conditions here otherwise it won't work.
Try below condition
value.present? || value.class == FalseClass || value.class == TrueClass
P.S. - blank_keys = params.map { |key, value| key if value.blank? }.compact is not checking for nested values of attributes
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 { .....
I making a post request through postman with the following JSON code:
{
"order":
{
"txnid":"SA",
"stamp_amount": "200",
"service": {
"name": "Affidavit"
},
"payment": {
"created_at": {
"strftime": "12-6-2016"
},
"mode": "Cash"
},
"user": {
"profile": {
"first_name": "Mahesh",
"country": {
"phone_code":"080"
}
},
"phone":"9988998899"
},
"address":{
"address1":"dadacsa",
"address2":"ca",
"locality":{
"name":"asca"
},
"landmark":"asca",
"city":{
"name":"aacs", "state":{
"name":"asca"
}
}
},
"product":"asx",
"total_amount":"5000",
"document":{
"default_price":"100"
},
"delivery_amount":"99",
"discount":"0"
},
"no_of_copies":"1"
}
As I make request to my invoice_api_controller with the following:
def order_invoice
response = Hash.new
result = Hash.new
if params[:order] && params[:order][:txnid] && params[:no_of_copies] && params[:order][:total_amount]!= 0
#order = JSON.parse(params[:order]).each do |order|
#order['stamp_amount'] = order['stamp_amount'] || ''
#order['txnid'] = order['txnid']
#order['service']['name'] = order['service']['name'] || ''
#order['payment']['created_at']['strftime'] = order['payment']
['created_at']['strftime']
#order['payment']['mode'] = order['payment']['mode'] || ''
#order['user']['name'] = order['user']['name']
#order['user']['profile']['first_name'] = order['user']
['profile']['first_name'] || ''
#order['address']['address1'] = order['address']['address1']
|| ''
#order['address']['address2'] = order['address']['address2']
|| ''
#order['address']['locality']['name'] = order['address']
['locality']['name'] || ''
#order['address']['landmark'] = order['address']['landmark']
|| ''
#order['address']['city']['name'] = order['address']['city']
['name'] || ''
#order['address']['city']['state']['name'] = order['address']
['city']['state']['name'] || ''
#order['user']['profile']['country']['phone_code'] = ['user']
['profile']['country']['phone_code'] || ''
#order['user']['phone'] = order['user']['phone'] || ''
#order['product'] = order['product'] || ''
#order['total_amount'] = order['total_amount']
#order['document']['default_price'] = order['document']
['default_price'] || ''
#order['delivery_amount'] = order['delivery_amount'] || ''
#order['discount'] = order['discount'] || ''
#no_of_copies = params[:no_of_copies]
end
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
It throws the following error
no implicit conversion of ActionController::Parameters into String
for the line #order = JSON.parse(params[:order]).each do |order|
I am not sure what am I doing wrong for I am new to working with Rails and APi's.Please help with detailed elaboration.It would help a great deal
You need to set
Content-Type = 'application/json'
in postman
You may look at this question and this answer
After that you don't need to parse params, all parsing done by Rails. You just get params[:order]
These is sample response of hashes in ruby.
Eg:-
find abcd1234
should give me
i was able to find by but it's not sufficent
I have response of sth like these and list keep on going different value but same structure
[
{
"addon_service": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "heroku-postgresql"
},
"config_vars": [
"FOO",
"BAZ"
],
"created_at": "2012-01-01T12:00:00Z",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "acme-inc-primary-database",
"plan": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "heroku-postgresql:dev"
},
"app": {
"id"=>"342uo23iu4io23u4oi2u34",
"name"=>"heroku-staging"},
},
"provider_id": "abcd1234",
"updated_at": "2012-01-01T12:00:00Z",
"web_url": "https://postgres.heroku.com/databases/01234567-89ab-cdef-0123-456789abcdef"
} .........
]
can anyone know how to grab those?
You can iterate all array element (a hash) and display its content if the hash meet your requirement:
element_found = 0
YOUR_DATA.each do |element|
if element["provider_id"].match(/abcd1234/)
element_found += 1
puts "addon_service: #{element['addon_service']['name']}"
puts "app: #{element['app']['name']}"
end
end
if element_found == 0 puts "Sorry match didn't found"
Since the elements of the array are hashes you can select the appropriate ones by matching the desired key.
select {|app| app[:provider_id] == "abcd1234"}
Do you know what to do with the element once you select it?
I think you want some of the items from the hash, but not all of them.
That might look like:
select {|app| app[:provider_id] == "abcd1234"}.map {|app| app.select {|key, v| [:addon_service, :app].include?(key) } }
There is this rails app which I created to accept SNS topic notification data. In summery SNS notification is generated on completion of a elastic transcoder job. So the rails app simply accept the incoming post request from AWS SNS service and All I need to parse this data. What doesn't work for me is, I'm trying to retrieve the Message => jobId but I can't figure out how. Appreciate any help.
def sns_transcode_completed
amz_message_type = request.headers['x-amz-sns-message-type']
amz_sns_topic = request.headers['x-amz-sns-topic-arn']
if !amz_sns_topic.nil? &&
amz_sns_topic.to_s.downcase == 'arn:aws:sns:us-east-1:XXXXXXXXXX:MyApp_transcode_completed'
request_body = JSON.parse(request.body.read, {:symbolize_names => true})
notification = Hashie::Mash.new(request_body)
if amz_message_type.to_s.downcase == 'subscriptionconfirmation'
subscribe_url = request_body['SubscribeURL']
if !subscribe_url.to_s.empty? && !subscribe_url.nil?
subscribe_confirm = HTTParty.get subscribe_url
puts subscribe_confirm
puts subscribe_url
end
end
if amz_message_type.to_s.downcase == 'notification'
puts "--------------------------"
puts notification.inspect # See output 1
puts "--------------------------"
puts notification.MessageId # This works I can get the MessageId
puts "--------------------------"
puts notification.Message => # From here I need to get the jobId, but it comes as a String? See output 2
puts "--------------------------"
end
end
render :nothing => true, :status => 200, :content_type => 'text/html'
end
Output 1
#<Hashie::Mash Message="{\n \"state\" : \"COMPLETED\",\n \"version\" : \"2012-09-25\",\n \"jobId\" : \"1440122777052-XXXXXX\",\n \"pipelineId\" :
\"1432361831290-XXXXX\",\n \"input\" : {\n \"key\" : \"web-b796ab20-297c-0133-4ccf-378cf690e3b1.mp3\"\n },\n \"outputKeyPrefix\" :
\"hlsv4/246-21632840-29d7-0133-1d8e-XXXXXXXX/\",\n \"outputs\" : [ {\n \"id\" : \"1\",\n \"presetId\" : \"1351620000001-200071\",\n \"key\" : \"hls_64k\",\n
\"segmentDuration\" : 10.0,\n \"status\" : \"Complete\",\n \"duration\" : 60\n } ],\n \"playlists\" : [ {\n \"name\" : \"index\",\n \"format\" : \"HLSv4\",\n
\"outputKeys\" : [ \"hls_64k\" ],\n \"status\" : \"Complete\"\n } ]\n}" MessageId="2d26f6d0-1715-5edb-af39-b8809eff521f" Signature="XXXXXX" SignatureVersion="1"
SigningCertURL="https://sns.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11fXXXXXXX.pem" Subject="Amazon Elastic Transcoder has
finished transcoding job 1440122777052-XXXXXX." Timestamp="2015-08-21T02:06:34.523Z" TopicArn="arn:aws:sns:us-east-1:005550634774:MyApp_transcode_completed"
Type="Notification"
UnsubscribeURL="https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:005550634774:MyApp_transcode_completed:8c0e48fe-c34
9-4185- bb94-XXXXXXXXXX">
Output 2
{
"state": "COMPLETED",
"version": "2012-09-25",
"jobId": "1440122777052-XXXXX",
"pipelineId": "1432361831290-XXXXXX",
"input": {
"key": "web-b796ab20-297c-0133-4ccf-378cf690e3b1.mp3"
},
"outputKeyPrefix": "hlsv4/246-21632840-29d7-0133-1d8e-35ebbdecd855/",
"outputs": [
{
"id": "1",
"presetId": "1351620000001-200071",
"key": "hls_64k",
"segmentDuration": 10,
"status": "Complete",
"duration": 60
}
],
"playlists": [
{
"name": "index",
"format": "HLSv4",
"outputKeys": [
"hls_64k"
],
"status": "Complete"
}
]
}
I was having the same issue and I ended up ditching Hashie.(It's really not needed aside from being able to use dot notation in this case.) I did still have to double parse (due to the string returned when you dig out the message). Once I grabbed that, the object I wanted to get at was in an Array so I used the dig method to pull out the object and then access the data I needed.
def sns_endpoint
notification = JSON.parse(request.raw_post, {:symbolize_names => true})
case notification[:Type]
when "SubscriptionConfirmation"
confirm(notification[:TopicArn], notification[:Token])
when "Notification"
message_data = JSON.parse(notification[:Message])
thing_you_are_trying_to_get = message_data["Records"].dig(0)["s3"]...etc...(access the data you need here.)
else
Rails.logger.error "Unknown notification type #{notification[:Type]}"
end
render body: nil
end
I got this working parsing the notification.Message value again. I wish if there is a better way than this.
parsed_message = JSON.parse(notification.Message, {:symbolize_names => true})
puts parsed_message.inspect
puts "--------------------------"
puts parsed_message[:jobId]