AWS Lambda NodeJS locale/variable isolation - amazon-web-services

There is a concern about potential problem with reusable variables in aws-lambda.
A user's locale is passed as
Browser cookies => AWS API Gateway => Lambda (NodeJS 6.10)
On the server side localization is implemented with a static variable in a class. Presenting typescript code for clarity but can be done in pure ECMAScript.
Module Language.ts
export default class Language
{
public static Current: LanguageCode = LanguageCode.es;
}
Static Language.Current variable is used across different parts of the application for manual localization and it works perfectly on the client side (react + redux).
Lambda function
import {APIGatewayEvent, Context, Callback} from 'aws-lambda';
import Language from './pathToModule/Language.ts';
export const api = function(event: APIGatewayEvent, context: Context, callback: Callback)
{
Language.Current = event.headers.cookie.locale;
// do the logic here
}
Potential problem
According to AWS documentation NodeJS instances can be reused for different requests. It means that famous concurrent problems have to be considered, e.g.
User 1 calls lambda function. The locale is set to English.
In parallel user 2 calls the same lambda instance. The local is changed to Spanish.
User 1 code continues and reads modified (wrong) locale variable from the shared module Language.
How do you resolve this problem?
For convenience it is good to have only one place for locale change. As I understand the same concern exists for all famous i18n npm packages (i18next, i18n, yahoo i18n, etc).

One of the best practices for Lambda functions is to try and not write code which maintains state.
Here you are initializing the locale based on an initial request and applying it to all future requests, which is inherently flawed even on server based code, forget server less.
To fix this, you will need to initialize the localization library for each request, or at least maintain an in memory lazy map, which you can make use of use the current request's locale to achieve the desired localization.

There are several solutions:
Node JS container is reused only after a function process is finished (callback or error is occurred) (thanks to #idbehold). Thus there is always a unique context per a function call.
Refactor code and pass a locale variable back and force (#Yeshodhan Kulkarni suggestion).
For example, return a function as an intermediate result and use it before calling the result back.
var localizableResult = ...;
var result = localizableResult.Localize(requestedLocale).
If there is a need to use a local stack (kind of a thread context) for other projects there is a npm package node-continuation-local-storage.
Case 1 makes it really simple to use global variables for current locale.

Related

How can I attach a value at login time for Jinja and reuse it later on in Apache Superset's SQLLab?

We need some parameters handed over using a JWT token that are evaluated at a custom SecurityManager within Apache Superset. I read some information about g being able to handle information like this (in newer versions also across requests).
Ninja states it can read Flask's information from 'g' out of the box (docu Ninja Standard Context). This is a sample code snippet:
#expose('/login/', methods=['GET', 'POST'])
def login(self):
g.my_value = 'bar'
...
Still I do have issues when trying to use the information within SQLLab using e.g.
--{% set x = 'foo' %}
SELECT
{{x}},
{{g.my_value}}
FROM table_xyz
Will result in
g not defined
x is evaluated correctly to 'foo' if the g field is removed. Any hint if this is possible?
Accessing the flask.g context is not that straight forward as execution happens on the celery worker. The celery worker has no access to the Flask context (also note depending on configuration it might even run in async mode).
Internal JINJA variables like current_user needed some additional work in Supersets codebase to be made available, but there's no general mechanism to open up user defined JINJA variables out of the box at the time of writing.

Storing temporary variables for use in Loopback 4 application

I have an authentication token I'd like to use in multiple Loopback 4 controllers. This token expires. Once expired I run some login logic to fetch a new token.
My issue is I'm not sure how or where to store this token.
So I can use this throughout my application I'm thinking to either save the token as a environment variable eg.
process.env.AUTH_TOKEN = 'TEST';
or use Loopback 4's Application-level context
https://loopback.io/doc/en/lb4/Context.html
Are these suitable solutions for storing this token? If not what would be an alternative solution?
In the case of using Context, how would I go about doing this using best practices?
Taking all the comments above into account I would recommend you to crate a separate module which will encapsulate the logic related to your authentication token and how you use it. I.e. a new module will be responsible for:
Fetching a new token when it is empty
Storing of the token
Refreshing the token when it has expired
Execution of the API calls (or whatever you do with that token, sorry it was not clear from your description) - can be moved to a separate module, but it is a different story
I imagine your module in JavaScript may look something like:
let AUTH_TOKEN = "";
function makeAPICall(some, params) {
if (! AUTH_TOKEN) {
acquireNewToken();
}
if (expired()) {
refreshToken();
}
return "some_data"; // TODO: here you do you what you want with your auth token and return some data
}
function acquireNewToken() {
authToken = "new_token"; // TODO: put the logic to acquire a new token here
}
function refreshToken() {
authToken = "new_token"; // TODO: put the logic to refresh a token here
}
function expired() {
return false; // TODO: put the logic to check if token expired here
}
module.exports = {
makeAPICall: makeAPICall
};
Then you can require the authModule in all your controllers and use it like below:
let authModule = require('./modules/authModule');
authModule.makeAPICall("some", "params");
I believe you will never need to expose the auth token to your controllers as you can implement all the logic related to auth token usage within the authModule and only pass some parameters to makeAPICall function to tell it what to do and which data to get. But in case if you really need to expose it you can change the authModule a bit (add getToken function and add it to module.exports):
function getToken() {
return authToken;
}
module.exports = {
makeAPICall: makeAPICall,
getToken: getToken
};
Now, let's get back to your questions:
Are these suitable solutions for storing this token? If not what would be an alternative solution?
As proposed above the solution is to store the token as a local variable in scope of custom module. Note, as Node.js uses caching for modules your AUTH_TOKEN variable will be the same across all the controllers (every new require will return you exactly the same object with the same token).
If you do not want to require the authModule every time you need to access your AUTH_TOKEN you can also simply declare it as a global variable: global.AUTH_TOKEN = "";. Note, that global variables have it's drawback like it may cause implicit coupling between files, etc. Here is a good article about when you should and when you should not use global variables: https://stackabuse.com/using-global-variables-in-node-js/
In the case of using Context, how would I go about doing this using
best practices?
You can use Loopback 4 Context as well and it will be almost an equivalent of the solution with the custom authModule I proposed above. The only difference with the customer module - you can put a bit more custom logic there and avoid copy-pasting some of your code in the controllers. With Loopback 4 Context you can use Server level context and store your AUTH_TOKEN there, but you will still need some place where you get a new token and refresh it when it expires. Again, you can implement this logic in the custom authModule. I.e. you can still keep that custom module and store the AUTH_TOKEN in Loopback Context at the same time. This will be absolutely OK, but it will make the code a bit more complex from my point of view.

Is there an equivalent way of doing 'lb soap' programmatically with loopback?

According to documentation on loopback, lb soap creates models of underlying soap based datasource. Is there a programmatic way to do this? I want to do it programmatically to facilitate a dynamic soap consumption through dynamically created models and datasources.
Disclaimer: I am a co-author and maintainer of LoopBack.
Here is the source code implementing the command lb soap:
soap/index.js
soap/wsdl-loader.js
Here is the code that's generating models definition and method source code:
exports.generateAPICode = function generateAPICode(selectedDS, operationNames) { // eslint-disable-line max-len
var apis = [];
var apiData = {
'datasource': selectedDS,
'wsdl': selectedWsdl,
'wsdlUrl': selectedWsdlUrl,
'service': selectedService.$name,
'binding': selectedBinding.$name,
'operations': getSelectedOperations(selectedBinding, operationNames),
};
var code = soapGenerator.generateRemoteMethods(apiData);
var models = soapGenerator.generateModels(apiData.wsdl, apiData.operations);
var api = {
code: code,
models: models,
};
apis.push(api);
return apis;
};
As you can see, most of the work is delegated to soapGenerator, which refers to loopback-soap - a lower-level module maintained by the LoopBack team too. In your application, you can use loopback-soap directly (no need to depend on our CLI tooling) and call its API to generate SOAP-related models.
Unfortunately we don't have much documentation for loopback-soap since it has been mostly an internal module so far. You will have to read the source code to build a better understanding. If you do so, then we would gladly accept contributions improving the documentation for future users.

Django session variables sometimes get lost in multi-threaded environment

I'm trying to cache a set of strings per session by storing each one in their own variable and by using django.contrib.session.
I have the following code:
import copy
def get_result(request, operation):
previous_result = request.session.get(operation.name)
if previous_result:
result = copy.deepcopy(previous_result)
else:
result = get_json_response(operation)
request.session[operation.name] = copy.deepcopy(result)
return result
get_result() is
triggered via ajax requests
used for many different operations which may be called at the same time
may be called multiple times per operation in one session
This code works perfectly fine on my local environment. However, in production server where gevent and chausette is installed, it fails.
Most of the time, request.session.get(operation.name) would return None even when it is not the first time that get_result is called for that operation. In some cases, it returns a value but in some, it doesn't. There seems to be no pattern on when it does and doesn't work.
I suspect that the inconsistency is because different threads are referencing the session variable at different states. What would be the proper way to handle session variables in this case?
I did in fact have the same problems and also tried to save the session properly with the tweaks you posted.
In the end, what solved my problem was changing the default cache in settings.py to
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
Using FileBasedCache instead helps as well, but it crashes in the local environment (development). Dummy works for local as well as production.

Dynamic messages with gettext (AngularJS)

I have a application with a Django backend and an AngularJS front-end.
I use the angular-gettext plugin along with Grunt to handle translations.
The thing is, I sometimes received dynamic strings from my backend through the API. For instance a MySQL error about a foreign key constraint or duplicate key entry.
How can I add this strings to the .pot file or non harcoded string in general ?
I've tried to following but of course it cannot work :
angular.module('app').factory('HttpInterceptor', ['$q', '$injector', '$rootScope', '$cookieStore', 'gettext', function ($q, $injector, $rootScope, $cookieStore, gettext) {
responseError: function (rejection) {
gettext('static string'); //it works
gettext(rejection.data.error); //does not work
$rootScope.$emit('errorModal', rejection.data);
}
// Return the promise rejection.
return $q.reject(rejection);
}
};
}]);
})();
One solution I could think of would be to write every dynamic strings into a JSON object. Send this json to server and from there, write a static file containing these strings so gettext can extract them.
What do you suggest ?
I also use angular-gettext and have strings returned from the server that need to be translated. We did not like the idea of having a separate translation system for those messages so we send them over in the default language like normal.
To allow this to work we did two things. We created a function in our backend which we can call to retrieve all the possible strings to translate. In our case it's mainly static data that only changes once in a while. Ideally this would be automated but it's fine for now.
That list is formatted properly through code into html with the translate tag. This file is not deployed, it is just there to allow the extraction task to find the strings.
Secondly we created a filter to do the translation on the interpolated value, so instead of translating {{foo}} it will translate the word bar if that's was the value of foo. We called this postTranslate and it's a simple:
angular
.module('app')
.filter('postTranslate', ['gettextCatalog', function (gettextCatalog) {
return function (s) {
return gettextCatalog.getString(s);
};
}]);
As for things that are not in the database we have another file for those where we manually put them in. So your error messages may go here.
If errors are all you are worried about though, you may rather consider not showing all the error messages directly and instead determine what user friendly error message to show. That user friendly error message is in the front end and therefore circumvents all of this other headache :)