Writing nested path parameters in FeathersJS - rest

How to generate multi level path parameters in feathers js like below :
api.com/category/catergoryId/subCatergory/subCatergoryId

The following is taken from this Feathers FAQ entry:
Normally we find that they actually aren't needed and that it is much better to keep your routes as flat as possible. For example something like users/:userId/posts is - although nice to read for humans - actually not as easy to parse and process as the equivalent /posts?userId=<userid> that is already supported by Feathers out of the box. Additionaly, this will also work much better when using Feathers through websocket connections which do not have a concept of routes at all.
However, nested routes for services can still be created by registering an existing service on the nested route and mapping the route parameter to a query parameter like this:
app.use('/posts', postService);
app.use('/users', userService);
// re-export the posts service on the /users/:userId/posts route
app.use('/users/:userId/posts', app.service('posts'));
// A hook that updates `data` with the route parameter
function mapUserIdToData(hook) {
if(hook.data && hook.params.userId) {
hook.data.userId = hook.params.userId;
}
}
// For the new route, map the `:userId` route parameter to the query in a hook
app.service('users/:userId/posts').hooks({
before: {
find(hook) {
hook.params.query.userId = hook.params.userId;
},
create: mapUserIdToData,
update: mapUserIdToData,
patch: mapUserIdToData
}
})
Now going to /users/123/posts will call postService.find({ query: { userId: 123 } }) and return all posts for that user.

Related

Kuzzle - inter-plugins communication (via method calls)

Is there a way with Kuzzle, to make two plugins communicate with each other?
Let's say a plugin A wants to call a method of a plugin B at boot time, or even runtime for some use cases. How can I do that ?
For now, there is no way to retrieve a particular plugin instance from another plugin. Plugins manager isn't reachable at plugin initialization, but in some way via a Kuzzle request (not the proper way of doing it)
function (request) {
const kSymbol = Object.getOwnPropertySymbols(request.context.user)[0];
request.context.user[kSymbol].pluginsManager.MyPlugin.someMethod(...args);
...
}
The idea behind this question would be to do something like this, when initializing the plugin
function init (customConfig, context) {
const { pluginsManager } = context;
const result = pluginsManager.MyPlugin.someMethod(...args);
// make something with the result ?
// For later use of plugins manager perhaps ?
this.context = context
}
Looks like Kuzzle Pipes would be the right thing to do it, cause they are synchronous and chainable, but pipes don't return anything when triggering an event
function init (customConfig, context) {
const result = context.accessors.trigger('someEvent', {
someAttribute: 'someValue'
});
console.log(result) // undefined
}
Thanks for your help !
full disclosure: I work for Kuzzle
Even if accessing the pluginManager like this may work at this time, we may change the internal implementation without warning since it's not documented so your plugin may not work in next version of Kuzzle.
The easiest way to use a feature from another plugin is exposing a new API method through a custom controller and then call it with the query method of the embedded SDK.
For example if your plugin name is notifier-plugin in the manifest:
this.controllers = {
email: {
send: request => { /* send email */ }
}
};
Then you can call it in another plugin like this:
await this.context.accessors.sdk.query({
controller: 'notifier-plugin/email',
action: 'send'
});
Please note that you can't make API calls in the plugin init method.
If you need to make API calls when Kuzzle start, then you can add a hook/pipe on the core:kuzzleStart event.
this.hooks = {
'core:kuzzleStart': () => this.context.accessors.sdk.query({
controller: 'notifier-plugin/email',
action: 'send'
});
};
Finally I noticed that you can't use pipes returns like in your example but I have proposed a feature to allow plugin developers to use the pipe chain return.
It will be available in the next version of Kuzzle.

Get semantic object and semantic action in controller

I'm trying to get the semantic object and semantic action of my deployed SAPUI5 application. I tried looking into ushell services - URLParsing and LaunchPage but it does not seem to return my semantic objects and actions.
Have anybody tried this?
This worked for me so far:
sap.ui.require([ // modules lazily instead of accessing them with global names.
"sap/ushell/library", // Consider adding `"sap.ushell": { lazy: true }` to dependencies in manifest.json
"sap/ui/core/routing/HashChanger",
], async (sapUshellLib, HashChanger) => {
const fullHash = new HashChanger().getHash(); // returns e.g. "masterDetail-display?sap-ui-theme=sap_fiori_3&/product/HT-1000"
const urlParsing = await sapUshellLib.Container.getServiceAsync("URLParsing");
urlParsing.parseShellHash(fullHash); /** returns e.g. {
action: "display",
appSpecificRoute: "&/product/HT-1000",
contextRaw: undefined,
params: { "sap-ui-theme": "sap_fiori_3" },
semanticObject: "masterDetail"
} **/
});
You can always just use
window.location.hash
Which you can parse yourself pretty easily. If you really want launchpad code, you can often find it here:
sap.ushell.services.AppConfiguration.getCurrentApplication().sShellHash
I've noticed it's not always set though when you're looking at an embedded component
A simplistic way to do this would be:
var oHashObject = new sap.ui.core.routing.HashChanger();
oHashObject.getHash();
//this will return the semantic object and action alongwith the routing params

How do I use findOrCreateEach in waterline/sailsjs?

I been looking around on the sails site and was lead to the waterline page. I am curious to how I can use the findOrCreateEach method. Specifically, number of arguments, what it will return, and how it will benefit me using it? I been searching, around and going to have to dive into the source code. I figure I ask here while I look.
Method without bluebird promises
Model.findOrCreateEach(/* What Goes Here */).exec(/* What Returns Here */);
With bluebird promises
Model.findOrCreateEach(/* What Goes Here */).then(/* What Returns Here */);
findOrCreateEach is deprecated; that's why it's not in the documentation. The best way to replicate the functionality is by using .findOrCreate() in an asynchronous loop, for example with async.map:
// Example: find or create users with certain names
var names = ["scott", "mike", "cody"];
async.map(names, function(name, cb) {
// If there is a user with the specified name, return it,
// otherwise create one
User.findOrCreate({name: name}, {name: name}).exec(cb);
},
function done(err, users) {
if (err) { <handle error and return> }
<users now contains User instances with the specified names>
});

SailsJS best practice to seed database with data before other Models are initialized

There is a model that all other models assume its existence.
It should be initialized before any API function is called.
The way I do this (it doesn't work):
1) Define model in api/models, let's call it Location.js
2) Add the following to bootstrap.js
var Locations = require('../api/models/Locations.js');
module.exports.bootstrap = function (cb) {
// seed the database with Locations
var locationsObj = {
country: 'Australia',
states: ['Brisbane', 'Perth', 'Sydney']
};
Location.create(locationsObj, function locationsObj(err, locations) {
if (err) {
cb(err);
}
console.log('locations created: ', locations);
});
}
Question 1
Is it the right way to do initial database seeding?
I get this error:
Locations.create(locationsObj, function locationsObj(err, locations) {
^
TypeError: Object #<bject> has no method 'create'
Question 2
How does the cb function of bootstrap work?
what if there as an error, what to do?
The sails models are globally available; so you don't need to require at bootstrap.js.
This is what I use to seed my database. (See the links I enclose to go to the gists)
Include seed function at config/models.js. The methods you declare in this file will be extended to all your models.
Link: Seed method gist
Define de data the seed will consume in your model Link: Model's seed data
Call the seed method in config/bootstrap.js using async. Link: Calling method
UPDATE
Have a look at this threat also: Best way to migrate table changes to production sailsjs tables
From Cannot unit test my model in sailsjs:
"Once Sails app is lifted, you will have your models available automatically...
And in your case, your first line overrides the User model which would be otherwise constructed by Sails.js, that's why even though you have an object it's not a Waterline model."
I know this is old but, for completeness:
You set
var Locations = ...
But but you call
Location.create()
(no 's') so you just have a typo.
in config/bootstrap.js you can write your seeds directly. Take a look at the example below.
await sails.models.role.createEach([
{
name: 'Admin',
},
{
name: 'Normal-user',
},
]);
here 'role' is name of the table created and not the model name.

CodeIgniter: URIs and Forms

I'm implementing a search box using CodeIgniter, but I'm not sure about how I should pass the search parameters through. I have three parameters: the search string; product category; and the sort order. They're all optional. Currently, I'm sending the parameters through $_POST to a temporary method, which forwards the parameters to the regular URI form. This works fine. I'm using a weird URI format though:
http://site.com/products/search=computer,sort=price,cat=laptop
Does anyone have a better/cleaner format of passing stuff through?
I was thinking of passing it into the products method as arguments, but since the parameters are optional things would get messy. Should I suck it up, and just turn $_GET methods on? Thanks in advance!
Query Strings
You can enable query strings in CodeIgniter to allow a more standard search function.
Config.php
$config['enable_query_strings'] = FALSE;
Once enabled, you can accept the following in your app:
http://site.com/products/search?term=computer&sort=price&cat=laptop
The benefit here is that the user will find it easy to edit the URL to make a quick change to their search, and your search uses common search functionality.
The down side of this approach is that you are going against one of the design decisions of the CodeIgniter development team. However, my personal opinion is that this is OK provided that query strings are not used for the bulk of your content, only for special cases such as search queries.
A much better approach, and the method the CI developers intended, is to add all your search parameters to the URI instead of a query string like so:
http://site.com/products/search/term/computer/sort/price/cat/laptop
You would then parse all the URI segments from the 3rd segment ("term") forward into an array of key => value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Products extends Controller {
...
// From your code I assume you are calling a search method.
function search()
{
// Get search parameters from URI.
// URI Class is initialized by the system automatically.
$data->search_params = $this->uri->uri_to_assoc(3);
...
}
...
}
This would give you easy access to all the search parameters and they could be in any order in the URI, just like a traditional query string.
$data->search_params would now contain an array of your URI segments:
Array
(
[term] => computer
[sort] => price
[cat] => laptop
)
Read more about the URI Class here: http://codeigniter.com/user_guide/libraries/uri.html
If you're using a fixed number of parameters, you can assign a default value to them and send it instead of not sending the parameter at all. For instance
http://site.com/products/search/all/somevalue/all
Next, in the controller you can ignore the parameter if (parameter == 'all'.)
Class Products extends Controller {
...
// From your code I assume that this your structure.
function index ($search = 'all', $sort = 'price', $cat = 'all')
{
if ('all' == $search)
{
// don't use this parameter
}
// or
if ('all' != $cat)
{
// use this parameter
}
...
}
...
}