sending data back in response after SOAP call - ruby-on-rails-3

So I would like to to this:
receive a POST from external url in controller and capture data
use this data in SOAP request; use response back from SOAP request to modify data and then
send data back to external client in the response to external url in step 1.
It seems I have some timing issues in my approach as the object returned to external client is empty. If I comment out my Savon code doing the SOAP call the object is complete. Here is the short version of what I am doing using Rails 3.0.9, Savon 0.9.7
1) works great
#
# get object from POST
#
string = request.body.read
#myobject = ActiveSupport::JSON.decode(string)
2) also works great (leaving out most of Savon magic as response has precisely what it should)
...
response = client.request :urn, "MySOAPOperation" do
...
3) Here is where I am a little "off"
response_hash =response.to_hash[:my_soap_operation_response]
#myobject.status = response_hash[:status].to_s
logger.info ("Status: " + #myobject.status)
#
# at this point myobject updated values are confirmed in log
#
respond_to do |format|
format.json { render :json => #myobject }
end
Now the response is correctly forwarded back to Step 1. external client but with #myobject empty. If I comment out the SOAP request and response the full #myobject is sent along with response to the client. I am thinking this is a timing issue as I also obtained the same result by commenting out the SOAP request and adding
sleep 20
in its place. Any suggestions will be greatly appreciated. Thank you for reading.
Dan

The culprit turned out to be settings on the PHP side of things.
Before (empty object returned):
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
After (complete object returned):
curl_setopt($ch, CURLOPT_TIMEOUT, 60);

Related

Send DELETE request to an API endpoint using Nestful Sinatra

I want to send a DELETE request to an external API endpoint in a controller of my Sinatra application using nestful gem. I want to delete an event by sending a DELETE request to an endpoint of TeamSnap API. I have defined the following route in controller:
delete '/events/:id' do
delete 'https://api.teamsnap.com/v3/events/76674685'
end
When the API endpoint is hit with delete action, I get the following error:
*** "Delete" argument "endpoint" needs to be a number
If I send a get request to the API endpoint, I get the correct response. The get request I am using is given below:
get '/events/:id' do
get 'https://api.teamsnap.com/v3/events/76674685'
end
Can anyone confirm how can we send the DELETE request in the controller and what I am missing?
Thanks in advance!
[Solved]: I sent a DELETE request through Nestful using the following commands:
delete '/events/:id' do
request = Nestful::Request.new(endpoint, options)
request.method = 'delete'
response = request.execute
end
In the above piece of code, endpoint is "https://api.teamsnap.com/v3/events/EVENT_ID" and options is a hash which contains "Content-Type" and "Authorization" headers.

Expiring api request caches

I've implemented API caching based on http://robots.thoughtbot.com/caching-api-requests. I'm using memory as the storage. How can I reset the cache manually without restarting the server?
I've tried using Rails.cache.clear, but it doesn't seem to work. The data is still getting pulled from the cache. I checked it by observing the server log for my puts message (as shown below).
Caching code:
module Meh
class Api
include HTTParty
#...
cache_name = options[:path] + "/" + options[:params].values.join(",")
response = nil
APICache.get(cache_name, cache: 3600) do
response = self.class.get options[:path], query: options[:params]
# For future debugging
puts "[API] Request: #{response.request.last_uri.to_s}"
# Just return nil if there's an error with the request, for now
if response.code == 200
response.reverse!
else
response = nil
end
end
end
Have you tried 'rake tmp:cache:clear' or deleting the contents of tmp/cache/ manually?
Are you trying to delete the contents of the cache from within the code?
Reading through the api_cache gem, it looks like this is a memory cache, not a file cache. Which would be consistent with your reports. It also looks like there is a .delete method on the APICache api. link So APICache.delete(cache_name) may be what you are looking for.

Upload file to Solr with HttpClient and MultipartEntity

httpclient, httpmime 4.1.3
I am trying to upload a file through http to a remote server with no success.
Here's my code:
HttpPost method;
method = new HttpPost(solrUrl + "/extract");
method.getParams().setParameter("literal.id", fileId);
method.getParams().setBooleanParameter("commit", true);
MultipartEntity me = new MultipartEntity();
me.addPart("myfile", new InputStreamBody(doubleInput, contentType, fileId));
method.setEntity(me);
//method.setHeader("Content-Type", "multipart/form-data");
HttpClient httpClient = new DefaultHttpClient();
HttpResponse hr = httpClient.execute(method);
The server is Solr.
This is to replace a working bash script that calls curl like this,
curl http://localhost:8080/solr/update/extract?literal.id=bububu&commit=true -F myfile=#bububu.doc
If I try to set "Content-Type" "multipart/form-data", the receiving part says that there's no boundary (which is true):
HTTP Status 500 - the request was rejected because no multipart boundary was found
If I omit this header setting, the server issues an error description that, as far as I discovered, indicates that the content type was not multipart [2]:
HTTP Status 400. The request sent by the client was syntactically incorrect ([doc=null] missing required field: id).
This is related to [1] but I couldn't determine the answer from it. I was wondering,
I am in the same situation but didn't understand what to do. I was hoping that the MultipartEntity would tell the HttpPost object that it is multipart, form data and have some boundary, and I wouldnt set content type by myself. I didn't quite get how to provide boundaries to the entities - the MultipartEntity doesn't have a method like setBoundary. Or, how to get that randomly generated boundary to specify it in addHeader by myself - no getBoundary methor either...
[1] Problem with setting header "Content-Type" in uploading file with HttpClient4
[2] http://lucene.472066.n3.nabble.com/Updating-the-index-with-a-csv-file-td490013.html
I am suspicious of
method.getParams().setParameter("literal.id", fileId);
method.getParams().setBooleanParameter("commit", true);
In the first line, is fileId a string or file pointer (or something else)? I hope it is a string. As for the second line, you can rather set a normal parameter.
I am trying to tackle the HTTP Status 400. I dont know much Java (or is that .Net?)
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error

Getting Response code = 406. Response message = Not Acceptable error when parsing JSON

I've built an Rails app that is called by another one (Rails as well), using ActiveResource.
The situation is that I expose the info from the first app as JSON as follows:
App 1:
class PromotionsController < ApplicationController
# GET /promotions
# GET /promotions.xml
def index
#promotions = Promotion.all
respond_to do |format|
format.json { render :json => #promotions }
end
end
end
And I receive it on the App 2, through an ActiveResource model, as follows:
class Promotion < ActiveResource::Base
self.site = "app_1_url"
self.element_name = "promotion"
end
When I want to read the data as JSON, doing the following, I get 406 Not Acceptable error message:
class PromotionsController < ApplicationController
# GET /promotions
# GET /promotions.xml
def index
#promotions = Promotion.all
respond_to do |format|
format.json { render :json => #promotions }
end
end
end
But, when I try to parse the info as XML (doing the same as the code shown above, except for changing "json" for "xml" everywhere) it works.
Any ideas?
Thanks.
You have to change the format to JSON for the application that is receiving the data (App 2)
class Promotion < ActiveResource::Base
#your code
self.format = :json #XML is default
end
Here's how I went about figuring that out (for any googlers that end up here)
Step 1: researching the error code
Per Wikipedia:
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
(Basically, the data you received is in a different language than you wanted)
Step 2: diagnosing the problem
Because a 400 level error code is a Client error code, I determined that the error must have been with App 2 (in this case, app 2 is the client requesting data from app 1). I saw that you were doing some formatting for JSON in app 1 and looked for similar code in App 2 and didn't see it, so I assumed the error was with App 2 having a different Content-Type header than App 1. The Content-Type basically tells applications/browsers what language each is speaking when they send/receive data. The value that you store in the Content-Type is the MIME Type and there are lots of them.
You said that the XML type worked but JSON wasn't, so I checked the rails ActiveResource API (used in App 2) looking for some headers or content-type method and saw a format method and property which matches what you used in the Action Controller for App 1. I also saw that the format is defaulted to XML if it is not supplied.
#Returns the current format, default is ActiveResource::Formats::XmlFormat.
def format
read_inheritable_attribute(:format) || ActiveResource::Formats[:xml]
end
Step 3: fixin thangz
add this line to the class in app 2:
self.format = :json
I'm sure you could adjust the Content-Type header with the headers method as well, but the API didn't have sample code displaying how to do that. Adjusting the Content-Type with the headers method would kind of just be the 'harder' way to do it, and because adjusting the Content-Type is so common rails created format to streamline the process. I saw that the API has an example of adjusting the format attribute of the class that conveniently uses json and read that the format method/property "Sets the format that attributes are sent and received in from a mime type reference" aka sets the Content-Type HTTP Header.
In addition to CoryDanielson's answer.
Even when my application is using :xml format (which is as Cory noted set by default) I still had to include
self.format = :xml
in order to fix 406 error.

Accessing the HTTP headers from a WCF Service

I need to access the HTTP response headers that are to be returned to the client from a WCF Service. Accessing the HTTPContext is easy(through HttpContext.Current.Response), but what is the event/extension/behavior that is executed lastly, when the StatusCode is already set (for ex. if the status is 500)?
EDIT: Message Inspectors don't seem to be a good solution here, because at the time they run, the status code isn't set yet. (At least in my trial that was the case)
You can access all headers on WebOperationContext.Current.IncomingRequest, like this:
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
WebHeaderCollection headers = request.Headers;
Console.WriteLine("-------------------------------------------------------");
foreach (string headerName in headers.AllKeys)
{
Console.WriteLine(headerName + ": " + headers[headerName]);
}
Console.WriteLine("-------------------------------------------------------");
See here
Simplest way for having control on the Headers is to use Message contracts.
Use Message Inspectors to monitor the message right after receiving it at the Service end.
In an extreme case, where you are not satisfied with any other standard routes, you can go for POX (Plain Old XML) type operations where you would be dealing with raw XML message.