Django equivalent for Node.js's Bluebird Promises [closed] - django

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I have a complex use case of promises in an Express (Node) server application, and now I'm being asked to migrate this server to Django. Basically my server (let's call it "A") is an OAuth2 client for another server (let's call it "B") and so A can request resources from B through B's API. Likewise, server A offers its own API which is intended to be consumed through ajax from javascript code in the browser. Let me show you the following picture to make things clearer:
My server A is working like a middleware between the browser and server B. So when the browser makes a call to one of A's API functions, A in turn makes several calls to B's API and based on those results A returns its own stuff to the browser.
So, in terms of code, I was doing something like this in Node.js (the code is just a simplification):
var express = require('express');
var router = express.Router();
var request = require('request-promise');
var Promise = require('bluebird');
...
//the following are helper functions
function getStuff1(req,res,params) {
request.get({
uri: "http://externalserver.com/api/whatever...",
headers: {
'Authorization':'Bearer' + req.user.accessToken //<-- notice that I'm using the user's access token (OAuth2)
}
}).then(function(input) {
//use params and input and compute stuff
return stuff;
}).catch(function(error) {
if(error.statusCode == 401) { // the accessToken has expired, we need to refresh it
return refreshOAuthToken(req,res)
.then(function(){
return getStuff1(req,res,params); // after refreshing the accessToken, we recursively call getStuff1 again
})
.catch(function(err) {
throw(err);
});
} else {
throw(error);
}
});
}
function getStuff2(req,res,params) { ... }
function getStuff3(req,res,params) { ... }
function getStuff4(req,res,params) { ... }
...
function refreshOAuthToken(req,res) {
return request.post({
uri: "http://otherserver/oauth/token",
form: {
'client_id': oauthClientId,
'client_secret': oauthClientSecret,
'grant_type': 'refresh_token',
'refreshToken': req.user.refreshToken // we're using the user's refresh token
})
.then( function(body) {
jsonResponse = JSON.parse(body);
req.user.accessToken = jsonResponse.access_token;
req.user.refreshToken = jsonResponse.refresh_token;
})
.catch( function(error) {
throw(error);
});
};
}
// the following is an actual API function
function apiFunction1(req,res) {
//first we compute params1 somehow
var params1 = req.whatever;
getStuff1(req,res, params1)
.then(function(stuff1) {
// do something with stuff1 and compute params2
return getStuff2(req,res,params2);
})
.then(function(stuff2) {
// do something with stuff2 and compute params3
return getStuff3(req,res,params3);
})
.then(function(stuff3) {
// now make 2 asynchronous calls at the same time
var promise4 = getStuff4(req,res,params4);
var promise5 = getStuff5(req,res,params5);
return Promise.all([promise4,promise5]); //we combine 2 promises into 1 with Promise.all
})
.then(function(results) {
var stuff4 = results[0];
var stuff5 = results[1];
//do something with stuff4 and stuff5, and compute the final answer
var answer = doSomethingWith(stuff4,stuff5);
res.send(answer); //finally we send the answer to the client
})
.catch(function(error) {
res.status(401).send({error: error}); // in case of any error, we send it back to the client
});
}
router.get('/api-function-1', apiFunction1);
module.exports = router;
This router is imported later like so:
var api = require('./routes/api');
app.use('/api', api);
So as you can see I'm doing a lot of requests to B which include refreshing OAuth2 tokens and making calls to B's API. Now the browser's javascript can call A's API function like so:
$.ajax('/api/api-function-1' + extra_params, {
dataType: 'json',
type: 'GET'
})
.done(doSomething)
.fail(handleError);
So what is the best way to achieve something like this in Django? I'm new to Django and python in general so I'm very open to any suggestion. Does Django have some equivalent for Node's bluebird library for promises? Any help regarding the OAuth2 part is also very welcomed.

Django conforms to, and is usually served, use the WSGI standard. WSGI and the default django deployment have a completely different execution model compared to node.
Node employs an event loop. Requests come in and are put on a single event loop. Bluebird (promises) allow you to put an event on the event loop and register an action to perform when that even completes. Django doesn't have a concept of an event loop, and doesn't have an equivalent to promises/futures (by default). In django a request comes in and is executed synchronously. There are a pool of workers, and when request comes in a single worker will handle executing the code until it is finished. There are no events registered onto an event loop.
Django code will look like:
# make an authenticated request using oauth user token
# if request fails make another request to refresh token
# remake request

Related

Apollo client useMutation in expo renders twice for every call

I have a basic expo app with React Navigation.
In the top function Navigation I am initiating a useMutation call to an Apollo server like so:
import { callToServer, useMutation } from '../graphQL';
function Navigation() {
console.log("RENDERED");
const [call] = useMutation(callToServer);
call({ variables: { uid: 'xyz', phoneNumber: '123' } });
...
And my GraphQL settings is as follows:
import {
ApolloClient,
createHttpLink,
InMemoryCache,
useMutation,
} from '#apollo/client';
import { onError } from '#apollo/client/link/error';
import { callToServer } from './authAPI';
const cache = new InMemoryCache();
const httpLink = createHttpLink({
uri: `XXXXXXX/my-app/us-central1/graphql`,
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
...
});
const client = new ApolloClient({
cache,
link: errorLink.concat(httpLink),
});
export {
useMutation,
callToServer,
};
export default client;
I want to clarify that I removed the httpLink from the client setting and I still get the two renders per call. I can see in the console that console.log("RENDERED") prints three times. Once when the app loads (normal) and twice after the useMutation call (not normal?)
What's going on here? Why is react re-renders twice per useMutation call? How do I avoid it?
UPDATE
I did further digging and it seems that useMutation does indeed cause the App to render twice - once when the request is sent, and once when it receives a response. I'm not sure I'm loving this default behavior which seems to have no way to disable. Why not let us decide if we want to re-render the App?
If someone has more insight to offer, Id love to hear about it.
Probably it's too late and maybe you've already found the solution, but still...
As I see you do not need data returned from mutation in the code above. In this case you can use useMutation option "ignoreResults" and set it to "true". So mutation will not update "data" property and will not cause any render.

How to invoke an API from Lambda

I have a requirement where I want DynamoDB TTL to put data into DynamoStream (when expired) and then send it to a Lambda. And I am able to achieve this. Now anytime an item gets expired it acts as a trigger to my lambda and I can see the items in console.log of Lambda method by investigating the event.
Question - I want to do some processing based on the items that are expiring. And for that to happen I need to make an API call to certain end point passing information about the items. I searched alot on google and even checked Lambda blueprints but I could not find a basic example where data received by lambda is being sent to a REST End point. Can someone guide me with this. All I find on google is how to integrate API Gateway with lambda. I am a beginner so need some guidance here.
Thanks!
It's pretty simple for a Node.js Lambda to call a REST API. It doesn't matter what your backend is coded in as long as it follows basic REST patterns. As an example, a very simple Node Lambda might look like:
var https = require('https');
exports.handler = (event, context, callback) => {
var params = {
host: "example.com",
path: "/api/v1/yourmethod"
};
var req = https.request(params, function(res) {
let data = '';
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
console.log("DONE");
console.log(JSON.parse(data));
});
});
req.end();
};
Obviously your code would contain a bit more as you have to process the DynamoDB event but this is the basics for a GET.

Ember-CLI-Mirage enforcing JSON:API?

Stumped on a couple failures and want to know if I'm understanding Mirage correctly:
1.In ember-cli-mirage, am I correct that the server response I define should reflect what my actual server is returning? For example:
this.get('/athletes', function(db, request) {
let athletes = db.athletes || [];
return {
athletes: athletes,
meta: { count: athletes.length }
}
});
I am using custom serializers and the above matches the format of my server response for a get request on this route, however, on two tests I'm getting two failures with this error: normalizeResponse must return a valid JSON API document: meta must be an object
2.Is mirage enforcing the json:api format, and is it doing so because of the way I'm setting up the tests?
For example, I have several tests that visit the above /athletes route, yet my failures occur when I use an async call like below. I would love to know the appropriate way to correctly overwrite the server response behavior, as well as why the normalizeResponse error appears in the console for 2 tests but only causes the one below to fail.
test('contact params not sent with request after clicking .showglobal', function(assert) {
assert.expect(2);
let done = assert.async();
server.createList('athlete', 10);
//perform a search, which shows all 10 athletes
visit('/athletes');
fillIn('.search-inner input', "c");
andThen(() => {
server.get('/athletes', (db, request) => {
assert.notOk(params.hasOwnProperty("contacts"));
done();
});
//get global athletes, which I thought would now be intercepted by the server.get call defined within the andThen block
click('button.showglobal');
});
});
Result:
✘ Error: Assertion Failed: normalizeResponse must return a valid JSON API document:
* meta must be an object
expected true
I tried changing my server response to a json:api format as suggested in the last example here but this looks nothing like my actual server response and causes my tests to fail since my app doesn't parse a payload with this structure. Any tips or advice must appreciated.
You are correct. Are the failures happening for the mock you've shown above? It looks to me like that would always return meta as an object, so verify the response is what you think it should be by looking in the console after the request is made.
If you'd like to see responses during a test, enter server.logging = true in your test:
test('I can view the photos', function() {
server.logging = true;
server.createList('photo', 10);
visit('/');
andThen(function() {
equal( find('img').length, 10 );
});
});
No, Mirage is agnostic about your particular backend, though it does come with some defaults. Again I would try enabling server.logging here to debug your tests.
Also, when writing asserts against the mock server, define the route handlers at the beginning of the test, as shown in the example from the docs.
I was able to get my second test to pass based on Sam's advice. My confusion was how to assert against the request params for a route that I have to visit and perform actions on. I was having to visit /athletes, click on different buttons, and each of these actions was sending separate requests (and params) to the /athletes route. That's is why I was trying to redefine the route handler within the andThen block (i.e. after I had already visited the route using the route definition in my mirage/config file).
Not in love with my solution, but the way I handled it was to move my assertion out of route handler and instead assign the value of the request to a top-level variable. That way, in my final andThen() block, I was able to assert against the last call to the /athletes route.
assert.expect(1);
//will get assigned the value of 'request' on each server call
let athletesRequest;
//override server response defined in mirage/config in order to
//capture and assert against request/response after user actions
server.get('athletes', (db, request) => {
let athletes = db.athletes || [];
athletesRequest = request;
return {
athletes: athletes,
meta: { count: athletes.length }
};
});
//sends request to /athletes
visit('/athletes');
andThen(() => {
//sends request to /athletes
fillIn('.search-inner input', "ab");
andThen(function() {
//sends (final) request to /athletes
click('button.search');
andThen(function() {
//asserts against /athletes request made on click('button.search') assert.notOk(athletesRequest.queryParams.hasOwnProperty("contact"));
});
});
});
I'm still getting console errors related to meta is not an object, but they are not preventing tests from passing. Using the server.logging = true allowed me to see that meta is indeed an object in all FakeServer responses.
Thanks again to Sam for the advice. server.logging = true and pauseTest() make acceptance tests a lot easier to troubleshoot.

Sails REST API with simple AUTH

I'm pretty new to sails, but after read the doc and followed some examples at the Internet, I decided to give it a shot ;)
I have made an APP that depend on a REST webservice that I want to build in Sails Framework - but after a lots of research I haven't found the right solutions in sails yet.
I think I want to pass a (username, password) or a api_key in each webservice call made from the app?
All the examples that i found was only with a session login method - not with an API key in each call.
I used this tutorial - http://jethrokuan.github.io/2013/12/19/Using-Passport-With-Sails-JS.html
But only logins at post to login page - I want it to login in every call and still want to use the build in REST API blueprints.
The problem in my solution is that a call to like this - will not give me all the users as expected because of the default REST method - I want it to auth the user and give me the result ..
http://example.com:1337/user/?username=test&password=xxx
What is the "best practises" for building a APP with a REST webservice backend? - "with sails"
Some of my auth code:
// policies/authentication.js
if(req.param('username') && req.param('password')) {
UserAuth.auth(req, res, function(err, user) {
if (err) return res.forbidden('You are not permitted to perform this action.');
if(user) {
return next();
}
});
}else{
return res.forbidden('You are not permitted to perform this action.');
}
// services/UserAuth.js
module.exports = {
auth : function(req, res, cb) {
var bcrypt = require('bcrypt');
var passport = require("passport");
passport.authenticate('local', function(err, user, info){
if (err) return cb({ error: 'auth error!', status: 400 });
if(user) {
cb(null, user);
}
})(req, res);
}
}
// config/policies.js
module.exports.policies = {
'*': "authentication"
};
First off, it's bad practice to continuously expose usernames and passwords in the wild like this. At the very least, you should consider issuing access_tokens that expire after some time, and need to be re-issued via a login system.
Second, if you want to authenticate on every request (instead of using sessions), it's better to do so using a request header, rather than putting the credentials in the query string. This is especially true when using Sails blueprints; otherwise you'll have to do extra work to keep the blueprints from using your credentials as search criteria.
When using a header, per-request authorization becomes simple with Sails. Set up a policy in api/policies called (for example) auth.js:
module.exports = function (req, res, next) {
// Find an access header
var accessToken = req.header('my-auth-header');
// No header, no access
if (!accessToken) {return res.forbidden();}
// Find the user with that token
User.findOne({accessToken: accessToken})
.exec(function(err, user) {
// Handle error
if (err) {return next(err);}
// Handle bad access token
if (!user) {return res.forbidden();}
// Handle success
return next();
});
}
Then you can set any controller actions that need authentication using the config/policies.js file:
module.exports = {
SomeController: {
'*': 'auth'
},
...etc...
}

Backbone.js Problems with fetch() in IE

I'm building a web app with Django on back-end and Backbone.js on front-end
I have problems with IE when I'm trying to fetch data from the server. When I run my HTML page in IE, the collection fetch always invoke the error func.
My code:
$(function(){
var Chapter = Backbone.Model.extend({});
var Chapters = Backbone.Collection.extend({
model: Chapter,
url: 'http://ip.olya.ivanovss.info/chapters'
});
var chapters = new Chapters();
var Router = new (Backbone.Router.extend({
routes: {
"": "choose_activity",
"/": "choose_activity"
},
choose_activity: function () {
chapters.fetch({
success: function () {
AppView.render();
},
error: function() {
alert('error');
}
});
}
}))();
var AppView = new (Backbone.View.extend({
el: '.popup',
templates: {
choose_activity: Handlebars.compile($('#tpl-activities').html())
},
render: function () {
this.$el.html(this.templates["choose_activity"]({ chapters: chapters.toJSON()}));
}
}))();
Backbone.history.start();
});
Django's View:
def chapters(request):
chapters = list(Chapter.objects.order_by('id'))
response = HttpResponse(json.dumps(chapters, default=encode_myway), mimetype='text/plain')
if request.META.get('HTTP_ORIGIN', None) in ('http://localhost', 'http://html.olya.ivanovss.info', 'http://10.0.2.2'):
response['Access-Control-Allow-Origin'] = request.META['HTTP_ORIGIN']
return response
Thank you in advance
IE7 doesn't support CORS.
There are 2 ways around this. The EASY way is Proxy over your API. My Python is rusty (I'm a Node/PHP dev), but I'm sure that there are a million and one resources on how do do this. The good thing about this is you don't have to touch the API. But it means your local server has to CURL and return every single request from your API server.
And second (and much less server intensive way) is JSONP! The idea of JSONP is that it appends a <script> to the document with the URL you specify. jQuery appends a ?callback=jQueryNNN where NNN is a random number. So effectively when the <script> loads, it calls jQueryNNN('The Response Text') and jQuery knows to parse the response from there. The bad thing about this is you have to wrap all of your responses on the API side (which is super easy if you're just starting, not so easy if you already have an infrastructure built out).
The annoying things about JSONP is that by it's nature you can't do a POST/PUT/DELETE. BUT you can emulate it if you have access to the API:
Backbone.emulateHTTP = true;
model.save(); // POST to "/collection/id", with "_method=PUT" + header.
To integrate JSONP with Backbone is pretty simple (little secret Backbone.sync uses jQuery's $.ajax() and the options parameters forwards over to jQuery ;)).
For each one of your models/collections which access a cross origin you can add a su
var jsonpSync = function (method, model, options) {
options.timeout = 10000; // for 404 responses
options.dataType = "jsonp";
return Backbone.sync(method, model, options);
};
In each collection and model what does cross-origin:
var MyCollection = Backbone.Collection.extend({
sync : jsonpSync
});
Or just overwrite the whole Backbone sync
Backbone.__sync = Backbone.sync;
var jsonpSync = function (method, model, options) {
options.timeout = 10000; // for 404 responses
options.dataType = "jsonp";
return Backbone.__sync(method, model, options);
};
Backbone.sync = jsonpSync;
On the server side you can do: this to return a JSONP response (copy pasted here):
def randomTest(request):
callback = request.GET.get('callback', '')
req = {}
req ['title'] = 'This is a constant result.'
response = json.dumps(req)
response = callback + '(' + response + ');'
return HttpResponse(response, mimetype="application/json")