How to consume api rest using TurboGears2? - python-requests

I want to show the data in the view
r = requests.get('https://jsonplaceholder.typicode.com/posts')
print(r)
print(r.headers)
print(r.encoding)
data = r.json()
log.debug(data)
log.debug(r)
for post in data:
s = format(post["id"],post['title'])
Any ideas?

Inside your web-app create a controller having #expose('json') as the decorator and then you can request that url the way you want. While requesting the url you may want to append .json at the end of the url. For example in your case
#expose('json')
def posts(self, *args, **kwargs):
#do your stuff
return dict(data=data)
and then you can easily call this url.
r = requests.get('https://jsonplaceholder.typicode.com/posts.json')

Related

?How can I mock a manual redirect using python requests_mock?

I have a function where I'm doing a http request to a site that should do an automatic redirect but I'm doing this manually using allow_redirects=False. I have the function itself working something like this:
def make_redirect_request(self):
url = https://some.url.com
response = requests.get(url, headers={"header": "some header"}, status_code=302, allow_redirects=False)
redirected_response = requests.get(response.next.url, headers={"header": "some other header"}, status_code=200)
self.publish(redirected_response.content) # some publishing function for publishing content on page
This works as I would like it to, but I'm struggling to make a unittest that checks this correctly. I have tried something like this:
#unittest.mock.patch("publish")
#unittest.mock.patch("url", "mock_url")
def test_redirect_url(self, mock_publish):
with patch('requests.get', self.session.get) #predefined session with parameters I don't use in this function
mock_response = b"some byte response"
next_url = "mock_redirected_url"
self.adapter.register_uri("GET", "mock_url", status_code=302, allow_redirects=False, next=next_url)
#I want the next parameter to give me the url that I'm being redirected to, which is given by response.next.url in the actual function, but this doesn't work here and it also doesn't understand the allow_redirects parameter
self.adapter.register_uri("GET", next_url, content=mock_response, status_code=200)
self.session.mount("mock", self.adapter)
self.make_redirect_request()
mock_publish.assert_called_once()
I'm not sure how to get the first request to pass the url given by response.next.url to the second request, as the adapter doesn't seem to take the next and allows_redirects arguments.

Download an externalReference from PlannerTask using the Graph API

I'd like to download a file attached to a PlannerTask. I already have the external references but I can't figure out how to access the file.
An external reference is a JSON object like this:
{
"https%3A//contoso%2Esharepoint%2Ecom/sites/GroupName/Shared%20Documents/AnnualReport%2Epptx":
{
// ... snip ...
}
}
I've tried to use the following endpoint
GET /groups/{group-id}/drive/root:/sites/GroupName/Shared%20Documents/AnnualReport%2Epptx
but I get a 404 response. Indeed, when I use the query in Graph Explorer it gives me a warning about "Invalid whitespace in URL" (?).
A workaround that I've found is to use the search endpoint to look for files like this:
GET /groups/{group-id}/drive/root/search(q='AnnualReport.pptx')
and hope the file name is unique.
Anyway, with both methods I need extra information (ie. the group-id) that may not be readily available by the time I have the external reference object.
What is the proper way to get a download url for a driveItem that is referenced by an external reference object in a PlannerTask?
Do I really need the group-id to access such file?
The keys in external references are webUrl instances, so they can be used with the /shares/ endpoint. See this answer for details on how to do it.
When you get a driveItem object, the download url is available from AdditionalData: driveItem.AdditionalData["#microsoft.graph.downloadUrl"]. You can use WebClient.DownloadFile to download the file on the local machine.
Here is the final code:
var remoteUri = "https%3A//contoso%2Esharepoint%2Ecom/sites/GroupName/Shared%20Documents/AnnualReport%2Epptx";
var localFile = "/tmp/foo.pptx";
string sharingUrl = remoteUri;
string base64Value = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sharingUrl));
string encodedUrl = "u!" + base64Value.TrimEnd('=').Replace('/','_').Replace('+','-');
DriveItem driveItem = m_graphClient
.Shares[encodedUrl]
.DriveItem
.Request()
.GetAsync().Result;
using (WebClient client = new WebClient())
{
client.DownloadFile(driveItem.AdditionalData["#microsoft.graph.downloadUrl"].ToString(),
localFile);
}

adding params in faraday middleware

I am trying to write a middleware that will set additional params to the query string. The use case is to be able to add additional authentication tokens, for eg, to the request as required by the backend, but wanting to inject it independent of the request creation itself.
This is what my middleware(pseudo code) looks like:
class MyMiddleware < Struct.new(:app, :key, :value)
def call(env)
env.params[key] = value #1
#env.params = {key => value} #2
app.call env
end
end
above raises NoMethodError (undefined method[]=' for nil:NilClass)`
above sets the params hash but the parameter is not encoded as part of the query string. The query string remains what it was before the middlewares start processing the request.
My analysis is that since the query string gets built from the params in rack_builder.rb:191
def build_response(connection, request)
app.call(build_env(connection, request))
end
def build_env(connection, request)
Env.new(request.method, request.body,
connection.build_exclusive_url(request.path, request.params), #<== rack_builder.rb:191
request.options, request.headers, connection.ssl,
connection.parallel_manager)
end
Middlewares don't get the opportunity to set additional params. While env has a params property, it is nil and doesn't appear to be touched during or after the middlewares get invoked.
So, I have the following questions:
1. Is there a supported way to achieve this?
2. If not, is there a way to rebuild the query string as part of the middleware executing?
3. Would it be better to defer the URL building to after most of the request middleware chain is executed (but of course, before the adapter gets to do its thing; or do it in the adapter)?
4. Any other options?
Appreciate any guidance.
The answer is here at github: https://github.com/lostisland/faraday/issues/483 by #mislav
Inside the middleware, the request URL is already fully constructed together with all the configured query parameters in a string. The only way to add or remove query parameters is to edit the env.url.query string, e.g.:
MyMiddleware = Struct.new(:app, :token) do
def call(env)
env.url.query = add_query_param(env.url.query, "token", token)
app.call env
end
def add_query_param(query, key, value)
query = query.to_s
query << "&" unless query.empty?
query << "#{Faraday::Utils.escape key}=#{Faraday::Utils.escape value}"
end
end
conn = Faraday.new "http://example.com" do |f|
f.use MyMiddleware, "MYTOKEN"
f.adapter :net_http
end
However, if your middleware is going to be like MyMiddleware above and just add a static query parameter to all requests, the much simpler approach is to avoid the middleware and just configure the Faraday connection instance to apply the same query parameter to all requests:
conn = Faraday.new "http://example.com" do |f|
f.params[:token] = "MYTOKEN"
f.adapter :net_http
end

reading Word file from POST request in grails

I'm trying to write a Groovy script that will post a Word (docx) file to a REST handler on my grails application.
The request is constructed like so:
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.mime.MultipartEntity
import org.apache.http.entity.mime.content.FileBody
import org.apache.http.entity.mime.content.StringBody
import org.apache.http.impl.client.DefaultHttpClient
class RestFileUploader {
def sendFile(file, filename) {
def url = 'http://url.of.my.app';
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
MultipartEntity reqEntity = new MultipartEntity();
FileBody bin = new FileBody(file);
reqEntity.addPart("file", new FileBody((File)file, "application/msword"));
def normalizedFilename = filename.replace(" ", "")
reqEntity.addPart("fileName", new StringBody(normalizedFilename));
httppost.setEntity(reqEntity);
httppost.setHeader('X-File-Size', (String)file.size())
httppost.setHeader('X-File-Name', filename)
httppost.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8')
println "about to post..."
HttpResponse restResponse = httpclient.execute(httppost);
HttpEntity resEntity = restResponse.getEntity();
def responseXml = resEntity.content.text;
println "posted..."
println restResponse
println resEntity
println responseXml.toString()
return responseXml.toString()
}
}
On the receiving controller, I read in the needed headers from the request, and then try to access the file like so:
def inStream = request.getInputStream()
I end up writing out a corrupted Word file, and from examining the file size and the contents, it looks like my controller is writing out the entire request, rather than just the file.
I've also tried this approach:
def filePart = request.getPart('file')
def inStream = filePart.getInputStream()
In this case I end up with an empty input stream and nothing gets written out.
I feel like I'm missing something simple here. What am I doing wrong?
You will need to make two changes:
Remove the line: httppost.setHeader('Content-Type'.... File upload HTTP POST requests must have content type multipart/form-data (set automatically by HttpClient when you construct a multipart HttpPost)
Change the line: reqEntity.addPart("file", ... to: reqEntity.addPart("file", new
FileBody(file)). Or use one of the other non-deprecated FileBody constructors to specify a valid content type and charset (API link) This assumes that your file method parameter is of type java.io.File -- this isn't clear to me from your snippet.
Then, as dmahapatro suggests, you should be able to read the file with: request.getFile('file')

plone.directives form - passing variables with a redirect

I have a plone form that basically gets search terms, performs a search, and then directs the user to another form. For this second form, I need to pass a couple variables.
class MySearch(form.SchemaForm):
grok.context(IMyContext)
grok.name('my-search')
ignoreContext = True
schema = ISearchSchema
#button.buttonAndHandler(_(u'Search Method'))
def searchMethod(self, action):
""" group update/removal """
data, errors = self.extractData()
if errors:
self.status = self.formErrorsMessage
return
results = somecall(data['term'])
if results:
self.request.set('myvar',results['myvar'])
self.request.response.redirect('##my-results')
else:
IStatusMessage(self.request).addStatusMessage(_(u"No results found"),"info")
return
This doesn't work - I guess a new request is generated so myvar is immediately lost. I could put this in a query string and include it in the redirect, but would prefer to send it as POST data if possible. I also tried something like
return getMultiAdapter((self.context,self.request),name='my-results')()
to see if I could use that as a starting point to passing in variables, but that just returns me to my-search.
The parameters set on the request object are not taken into account (nor should they) when issuing a redirect.
Append a query string to the redirection URL instead; urllib.urlencode() does the job admirably:
from urllib import urlencode
self.request.response.redirect('##my-results?' + urlencode({'myvar': results['myvar']}))
The .redirect() call returns a 303 See Other response with the URL you passed in the the method as the Location header; the browser then opens that new location, and will include any GET parameters you added to the URL.

Resources