Extend all views with custom data and filters in Sails.js - templates

I'm developing a Sails.js application and I want to extend all views with custom data and functions.
What would be the best course of action to do so?
I've tried to create a policy to do so, but policies are only applied to routes with controllers.

Custom data
You can use a custom hook in order to achieve that.
Create a file at the specified path: api/hooks/viewsGlobals/index.js with the following content:
module.exports = function viewsGlobals (sails) {
return {
routes: {
before: {
// This middleware will be executed for every request.
'*': function (req, res, next) {
// Using condition to filter out static file requests.
if (req.accepted.some(function (type) {
return type.value === 'text/html';
})) {
res.locals.someData = {
// Place your custom data here.
};
}
return next();
}
}
}
}
};
Custom filters
Create a file at the following path: config/view-filters/toUpper.js and the following content:
// Replace this with templating engine that you use.
var swig = require('swig');
// Use the API of your templating engine to add custom filter.
swig.setFilter('toUpper', function (value) {
return value.toUpperCase();
});

Related

How to use CodeceptJS to unit-test a JS function

I've set up CodeceptJS for a project and use it to test various end-to-end scenarios.
Now I want to extend the tests-suite to also run unit-tests to verify functionality of custom JS functions.
For example: I have a global object App that has a version attribute. As a first test, I want to confirm that App.version is present and has a value.
My first attempt is a test.js file with the following code:
Feature('Unit Tests');
Scenario('Test App presence', ({ I }) => {
I.amOnPage('/');
I.executeScript(function() {return App.version})
.then(function(value) { I.say(value) } );
});
Problems with this code
The major issue: How can I assert that the App.version is present?
My script can display the value but does not fail if it's missing
My code is very complex for such a simple test.
I'm sure there's a cleaner/faster way to perform that test, right?
Here is a solution that works for me:
Read data from the browser:
I created a custom helper via npx codecept gh and named it BrowserAccess.
The helper function getBrowserData uses this.helpers['Puppeteer'].page.evaluate() to run and return custom code from the browser scope. Documentation for .evaluate()
Custom assertions:
Install the codeceptjs-assert package, e.g. npm i codeceptjs-assert
Add the AssertWrapper-helper to the codecept-config file. This enables checks like I.assert(a, b)
Full Code
codecept.conf.js
exports.config = {
helpers: {
AssertWrapper: {
require: "codeceptjs-assert"
},
BrowserAccess: {
require: './browseraccess_helper.js'
},
...
},
...
}
browseraccess_helper.js
const Helper = require('#codeceptjs/helper');
class BrowserAccess extends Helper {
async getBrowserData(symbolName) {
const currentPage = this.helpers['Puppeteer'].page;
let res;
try {
res = await currentPage.evaluate((evalVar) => {
let res;
try {
res = eval(evalVar);
} catch (e) {
}
return Promise.resolve(res);
}, symbolName);
} catch (err) {
res = null;
}
return res;
}
}
jsapp_test.js (the test is now async)
Feature('Unit Tests');
Scenario('Test App presence', async ({ I }) => {
I.amOnPage('/');
const version = await I.getBrowserData('App.version');
I.assertOk(version);
});

is there a way to get string (data) from text file stored in s3 in Alexa localisation.js file?

Problem:
I am trying to get the data from a text file stored in s3, I get it right in intent handler using a sync await but I want to get string in localisation file as I am trying to implement the solution in 2 languages.
I am getting err saying skill does not respond correctly.
This is file.js
const AWS = require('aws-sdk');
//========================
// This step is not required if you are running your code inside lambda or in
// the local environment that has AWS set up
//========================
const s3 = new AWS.S3();
async function getS3Object (bucket, objectKey) {
try {
const params = {
Bucket: 'my-bucket',
Key: 'file.txt',
};
const data = await s3.getObject(params).promise();
let dat = data.Body.toString('utf-8');
return dat;
} catch (e) {
throw new Error(`Could not retrieve file from S3: ${e.message}`);
}
}
module.exports = getS3Object;
this is the localisation.js file code
const dataText = require('file.js');
async let textTitle = await dataText().then(); **// this does not work**
module.exports = {
en: {
translation: {
WELCOME_BACK_MSG : textTitle,
}
},
it: {
translation: {
WELCOME_MSG: textTitle,
}
}
}
The problem is that in your localisation.js file you are trying to export something that is obtained via an asynchronous function call, but you cannot do that directly, module.exports is assigned and returned synchronously. Please, see for instance this SO question and answer for an in-deep background.
As you are mentioning Alexa skill, and for the name of the file, localisation.js, I assume you are trying something similar to the solution proposed in this GitHub repository.
Analyzing the content of the index.js file they provide, it seems the library is using i18next for localisation.
The library provides the concept of backend if you need to load your localisation information from an external resource.
You can implement a custom backend, although the library offers one that could fit your needs, i18next-http-backend.
As indicated in the documentation, you can configure the library to fetch your localization resources with this backend with something like the following:
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.init({
backend: {
// for all available options read the backend's repository readme file
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
Here in SO you can find a more complete example.
You need to provide a similar configuration to the localisation interceptor provided in the Alexa skill example project, perhaps something like:
import HttpApi from 'i18next-http-backend';
/**
* This request interceptor will bind a translation function 't' to the handlerInput
*/
const LocalizationInterceptor = {
process(handlerInput) {
const localisationClient = i18n
.use(HttpApi)
.init({
lng: Alexa.getLocale(handlerInput.requestEnvelope),
// resources: languageStrings,
backend: {
loadPath: 'https://your-bucket.amazonaws.com/locales/{{lng}}/translations.json',
crossDomain: true,
},
returnObjects: true
});
localisationClient.localise = function localise() {
const args = arguments;
const value = i18n.t(...args);
if (Array.isArray(value)) {
return value[Math.floor(Math.random() * value.length)];
}
return value;
};
handlerInput.t = function translate(...args) {
return localisationClient.localise(...args);
}
}
};
Please, be aware that instead of a text file you need to return a valid son file with the appropriate translations:
{
"WELCOME_MSG" : "Welcome!!",
"WELCOME_BACK_MSG" : "Welcome back!!"
}

ApolloGraphQL: useSubscription Hook Syntax with onSubscriptionData?

I'm trying to build an Apollo useSubscription hook that uses onSubscriptionData.
I've looked in the Apollo docs, but I haven't yet an example.
E.g. something like:
const { loading, error, data } = useSubscription(
INCOMING_MESSAGES_SUBSCRIPTION_QUERY,
{
variables: {"localUserId": Meteor.userId()},
onSubscriptionData: myFunctionThatRunsWhenSubscriptionDataArrives
}
);
That can't be right yet, because it doesn't include OnSubscriptionDataOptions<TData>, which is mentioned in the Apollo docs.
What is the correct way to build a useSubscription hook that uses onSubscriptionData?
The onSubscriptionData function is passed a single options parameter of the type OnSubscriptionDataOptions. The options object has two properties:
client -- the ApolloClient instance used to query the server
subscriptionData -- an object with the following properties: loading, data, error
Example usage:
const { loading, error, data } = useSubscription(
INCOMING_MESSAGES_SUBSCRIPTION_QUERY,
{
variables: {"localUserId": Meteor.userId()},
onSubscriptionData: ({ subscriptionData: { data } }) => {
// do something with `data` here
}
},
)

When launching icCube report how can I get the user name

in ic3Report.html file, is it possible to get the user name in the callback function ?
var options = {
root: "ic3-report/app/",
rootLocal: "ic3-report/app-local/",
rootVersion: "_IC3_ROOT_VERSION_",
callback: function () {
$('#intro').remove();
window.ic3application = ic3.startReport(
{
<!-- ic3-start-report-options (DO NOT REMOVE - USED TO GENERATE FILES) -->
});
// get user name here !
}
};
in order to gather current user information you should setup GVI configuration. It could be done with appropriate method:
var options = {
root: "ic3-report/app/",
rootLocal: "ic3-report/app-local/",
rootVersion: "_IC3_ROOT_VERSION_",
callback: function () {
$('#intro').remove();
var ic3reporting = new ic3.Reporting(
{
noticesLevel: ic3.NoticeLevel.INFO,
dsSettings: {
url: GVI_URL
}
});
ic3reporting.setupGVIConfiguration(function () {
var userName = ic3reporting.userName();
})
}
};
After that user information will be available inside context. See code above for details.
Update
A more robust solution is adding the code to the local files that are not overwritten with a new version of the reporting. You can use ic3bootstrapLocal function in Admin > Common JS configuration.
function ic3bootstrapLocal(options) {
var ic3reporting = new ic3.Reporting({
noticesLevel: ic3.NoticeLevel.INFO,
});
ic3reporting.setupGVIConfiguration(function(){
ic3reporting.userName()
options.callback && options.callback();
});
}

Ember.js global routed/transitioned events

Is it possible to subscribe to all transition events in the application? Or alternatively some observable property containing the current route?
I'm integrating with a third-party UI component that needs to be synchronized to the current route.
The application controller has a currentRouteName property, as explained here. It's mostly for debugging, but I imagine that it's a fairly stable property that could be used in production.
EDIT: If you need to be alerted of all changes, use the hashchange event like Ember does internally. This will only work if you're using hash based routing though. If you're using Ember's history API based routing, you'll have to use that.
In your app_controller you can add this snippet which fires on every path/route change
currentPathDidChange: function currentPathDidChange() {
var path = this.get('currentPath')
}.observes('currentPath')
I solved this by hooking into Router.didTransition
Live example: http://jsbin.com/yuzedacu/5/edit (modified the example found here)
App.Router.reopen({
updateCurrentRoute: function(infos) {
var appController = this.container.lookup('controller:application');
if (!('currentRoute' in appController)) {
Ember.defineProperty(appController, 'currentRoute');
}
if (infos && infos.length > 0) {
// The last part of the route contains the route name
var route = infos[infos.length - 1].name;
// Collect the dynamic route parameters
var params = infos.reduce(function(a, b) {
// Parameter can be named anything
// assume there are 0 or 1 parameters
for (var name in b.params) {
if (b.params.hasOwnProperty(name)) {
// 1 parameter
return a.concat(b.params[name]);
}
}
// 0 parameters
return a;
}, []);
var path = [route].concat(params);
Ember.set(appController, 'currentRoute', path);
} else {
Ember.set(appController, 'currentRoute', []);
}
},
didTransition: function(infos) {
this.updateCurrentRoute(infos);
return this._super(infos);
}
});