Sails 1.0 custom static sources - sails.js

Prior to Sails 1.0 one could use the customMiddleware to add static sources:
customMiddleware: function (app) {
app.use('/ui', express.static(process.cwd() + '/ui'));
app.use('/docs', express.static(process.cwd() + '/docs'));
app.use('/node_modules', express.static(process.cwd() + '/node_modules'));
},
But now in Sails 1.0 the customMiddleware is deprecated:
debug: Warning: use of `customMiddleware` is deprecated in Sails 1.0.
debug: Instead, use an Express 4-compatible middleware (res, res, next) function.
debug: See http://sailsjs.com/docs/upgrading/to-v-1-0#?express-4 for more info.
And the referenced link does not say how to replace app.use('/ui', express.static(process.cwd() + '/ui')); anywhere.
I've tried so many things, including using serve-static and also trying to set up custom routes, but nothing works.
Is it really possible that in Sails 1.0 we cannot set up custom static sources?

Related

How to configure fastify and openapiglue to only coerce query parameters

Our application is using fastify and we're registering the openapiGlue plugin to parse/validate http requests. We're using the ajv option to disable all type coercion.
Everything works great...
However, the issue with the ajv coerce option is that it is applied globally. What we'd like to do is enable type coercion only for the query parameters, but since the ajv coerce option is global, there's no way to do that.
We've tried to register another plugin, that we wrote, after the openapiGlue that uses the fastify setValidatorCompiler() to solve this problem for us. We've set up separate ajv options sections that get applied based on the part of the http request getting validated. The issue is the setValidatorCompiler callback function never gets invoked for any of the message parts, and thus we are unable to enable type coercion on just the query parameters.
It's as if the openapiGlue plugin doesn't want to relinquish control of the message parsing and validating.
Anyone have any suggestions?
// this works
await app.register(openapiGlue, {
specification: localSystemApi,
service: handlers,
prefix: apiPrefix,
ajvOptions: {
coerceTypes: false // defaults to true by openapiGlue otherwise
}
});
// new code to add plugin to only coerce query parameters
// has no effect.
await app.register(ajvPlugin);
// Our ajvPlugin
const httpPartSchema = z.union([z.literal('body'), z.literal('headers'), z.literal('querystring'), z.literal('params')]);
export const ajvPlugin: FastifyPluginAsync<FastifyOpenapiGlueOptions> = async (fastify) => {
fastify.setValidatorCompiler(({ schema, httpPart }) => {
// this code never gets invoked
if (httpPart === undefined) {
throw new Error('Missing httpPart');
}
const parseResult = httpPartSchema.parse(httpPart);
const compiler = schemaCompilers[parseResult];
return compiler.compile(schema) as any;
});
};
The issue is the setValidatorCompiler callback function never gets invoked for any of the message parts, and thus we are unable to enable type coercion on just the query parameters
This is a feature 😄
Fastify does not create any ajv instances if your routes do not use the schema option to speed up your server startup time.
You can do what you need by adding a route that adds a schema.query object.
I would suggest to use the fastify-split-validator plugin to change the ajv settings based on the HTTP entity.
As further reading, this issue in the fastify repository explains in detail how it works.

Autofac IContainer and DiagnosticTracer for ASP .Net 5

How I can get IContainer instance for my ASP.Net 5 app?
I need to enable diagnostics, and based on official documentation SubscribeToDiagnostics method is accessible only from IContainer, and ASP Net Core 3+ Integration this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); exposes only ILifetimeScope.
I have also noticed that Autofac supports DiagnosticListener - is this a way how I should trace for informations?
Does Autofac provide build in formatters for example for RequestDiagnosticData?
What are your recommendations?
I've updated the ASP.NET Core 3 example for Autofac to show how this works. The secret is using a build callback.
In your ConfigureContainer method, you register a callback to subscribe to diagnostics.
public void ConfigureContainer(ContainerBuilder builder)
{
// Add any Autofac modules or registrations, then...
//
// If you want to enable diagnostics, you can do that via a build
// callback. Diagnostics aren't free, so you shouldn't just do this
// by default. Note: since you're diagnosing the container you can't
// ALSO resolve the logger to which the diagnostics get written, so
// writing directly to the log destination is the way to go.
var tracer = new DefaultDiagnosticTracer();
tracer.OperationCompleted += (sender, args) =>
{
Console.WriteLine(args.TraceContent);
};
builder.RegisterBuildCallback(c =>
{
var container = c as IContainer;
container.SubscribeToDiagnostics(tracer);
});
}

Get body request parameters using express in V2 Dialogflow

I'm migrating my Google Action from v1 => v2 using an express app, and in the past, I've been able to get url params & initialize my action map like this:
// INITIALIZE EXPRESS APPLICATION & ENDPOINTS
app.use(bodyParser.json({strict: false}));
// POST [TYPE] [PLATFORM] [PUBLISHER] PARAMS => PASS TO FULFILLMENT
app.post('/:platform/:type/:publisher', function(req, res) {
debugRequest(req);
console.log(`SENDING TO ${TYPE} => ${PLATFORM} => ${PUBLISHER} FULFILLMENT`);
fulfillment.fulfillment(req, res);
});
```
With v2, instead of using a .post route with express, I just need to use .use e.g. express().use(bodyParser.json(), app). However, I don't understand how to get the body params (req/res) using this method [still kind of a node newbie] from body parser.
I need the full URL path (type, platform, publisher) from the request in order to fulfill some app logic later on, within various intents.
If someone has a more built out express / v2 Dialogflow example, that'd be very helpful. I have all this working with v1, but times are a changing!
You should be able to get this data now with the new Framework Metadata feature added in 2.2.0. See this GitHub comment for more details.
An object containing framework metadata is now present as the 2nd parameter in the middleware function.
Now you can do something like:
app.middleware((conv, framework) => {
if (framework.express) {
conv.expressParams = framework.express.request.expressParams;
}
});
app.intent('some intent', conv => {
conv.ask(`Params sent was ${JSON.stringify(conv.expressParams)}`);
});
From a similar question in this GitHub issue:
You can retrieve the raw JSON data from conv.request for the core Actions on Google data and conv.body for the entire JSON body.

Sails JS how does generated entity have create when controller is empty ?

I got a basic question.
I'm trying out Sails (http://sailsjs.org/) and it has terminal commands to generate entity such as User entity:
sails generate api user
My question is, the UserController.js file shows:
/**
* UserController
*
* #description :: Server-side logic for managing users
* #help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
};
How come when I access:
http://localhost:1337/user/create
It knows how to create a new User entity ? The controller clearly does not have a create action like this:
module.exports = {
create: function(req, res) {
// code to create new user
}
};
So surely nothing should happen.
I did a bit of Symphony 2.0 PHP web framework and we needed to create those actions manually.
I'm confuzzled and impressed at the same time, any ideas ?
Welcome to the Sails.js world!
You have just discovered the Blueprint API.
When you lift your app, Sails will add generic actions to your controllers that have a model of the same name (to this day find, findOne, create, update, destroy, populate, add and remove actions exist implicitly). That's called Blueprints actions.
In addition, Blueprints routes can also be binded to your controllers' actions. Here is the list of those routes:
Blueprints RESTful routes: automatically generated routes to expose a conventionnal REST API on top of find, create, update, and destroy actions
GET /post -> PostController.find
GET /post/:id -> PostController.findOne
POST /post -> PostController.create
PUT /post/:id -> PostController.update
DELETE /post/:id -> PostController.destroy
Blueprints shortcuts routes: simple helpers to provide access to a controller's CRUD methods from your browser's URL bar
GET /user/create?name=joe -> Post.create
GET /user/update/1?name=mike -> Post.update
GET /user/destroy/1 -> Post.destroy
Blueprints actions routes: automatically create routes for your custom controller actions
GET /group/count -> Post.count
Each of them can be deactivated in the config/blueprints.js file.
You can find more details on the docs.
Check this SO question if you want to redefine the blueprints actions.

Reverse routing on an application deployed in a Tomcat context

I am developing a Play 1.2.5 application that will be installed in a Tomcat context:
http://mytomcat:8080/myapp
And in my routes file I have:
GET /companies/{companyID}/employees Employees.getForCompany
As per the instructions for deploying a Play application in a Tomcat context, I am generating URLs exlusively using the Reverse Router. This works fine, but I am not sure what to do in the case of a jQuery method such as an Ajax request of this style:
var companyID = $('#companies').find(":selected").val();
$.ajax({
url : "#{Employees.getForCompany(companyID)}",
...
});
Obviously the value of companyID isn't known at the time of the generation of the HTML and the resolution of routes by the reverse router.
The only solution that I can see is to reconfigure my routes file so that the URLs are different and the parameters are always at the end, but that makes the REST URLs less logical.
GET /companies/employees/{companyID} Employees.getForCompany
Is there a better way?
I have found the solution myself - Play includes the jsAction tag which generates a function that builds the correct URL:
var companyURL = #{jsAction #Employees.getForCompany(':companyID') /}
$.ajax({
url : companyURL({companyID:companyID}),
...
});
Like that I can maintain the preferred routes mapping:
GET /companies/{companyID}/employees Employees.getForCompany