Loopback 4: Query the relation model - loopback

I am new to Loop Back, but I want to use it for my upcoming API application. I am currently testing the features it has to offer, but I'm stuck on doing an advance query on a sub model of my root entity. I apologize in advance if this is already answered, but I've spent hours searching the web for answers and have not gotten anywhere. Furthermore, I saw below on LB4 website, but for unknown reasons it's not working for me.
customerRepo.find({
include: [
{
relation: 'orders',
scope: {
where: {name: 'ToysRUs'},
include: [{relation: 'manufacturers'}],
},
},
],
});
I am essentially using two models - User and Note where User has many Notes. I used LB4 CLI 99.9% of times to create the datasource, models, repositories and relations. I have even added inclusion resolver to my note repository as below.
this.registerInclusionResolver('user', this.user.inclusionResolver);
However, when I try to run below filter against my note repository, it does not apply the where filter against User. Oddly, when I add the scope block, the user is no longer included in the response.
{
"limit": 5,
"include": [
{
"relation": "user",
"scope": {
"where": {
"username": "jdoe#example.com"
}
}
}
]
}
My project is a boilerplate code created using lb4 app command. I've then added my datasource, models, repositories, and controllers.
Any help will be greatly appreciated.

Related

Evaluate Casbin policies real-time

I use Casbin as authorization library for my REST API, written in Go.
To load the policy from my Mongo database, I use MongoDB Adapter.
A single policy Mongo document looks like this:
{
"_id": {
"$oid": "639491f73e4c9bec05a1d1ec"
},
"ptype": "p",
"v0": "admin",
"v1": "laptops",
"v2": "read",
"v3": "",
"v4": "",
"v5": ""
}
In my business logic, I validate if the user can access (read) laptops:
// Resolves to true
if can, _ := e.Enforce(user, "laptops", "read"); can {
...
This works fine.
The problem now is when I delete the policy document, I would expect that I'm not allowed to access laptops anymore. This is only the case when I restart my application.
Thus, it appears that the Enforce checks are not being evaluated real-time.
As a workaround, I could call the LoadPolicy method as soon as the request comes in but this looks like a dirty hack to me.
I would really appreciate some help / suggestions.

how to query custom dimension google anaylitics api

I have setup a custom dimension in google analytics 'dimension2' into which I want to capture a WPForms UniqueID. I added this to Google Tag manager and I can see the custom dimension with a value when I preview site in GTA preview.
. I added this to gtags.js on this word press site,
var dimensionValue = $.cookie("_wpfuuid");
gtag('config', 'UA-1234567890-2', {
'custom_map': {'dimension2': 'wpfid'}
});
gtag('set', 'dimension2', {'wpfid': dimensionValue});
In google analytics query explorer, I can see dimension2 in the test results.
"columnHeaders": [
{
"name": "ga:dimension2",
"columnType": "DIMENSION",
"dataType": "STRING"
},
{
"name": "ga:users",
"columnType": "METRIC",
"dataType": "INTEGER"
}
],
"totalsForAllResults": {
"ga:users": "1"
},
"rows": [
[
"40502794-ecf1-4cf6-97b9-2c16c7f6c949",
"1"
]
]
And, I can see the dimension2 data in google analytics user explorer, so it is making it to the browser interface for analytics.
However, when I add the following to my API query script, it breaks and is not generating any php errors, or the error is that it does not recognize 'dimension2'. I tried this on 2 views and both act the same. Here is my code to add the custom dimension to my query
$dimension = new Google_Service_AnalyticsReporting_Dimension();
$dimension->setName("ga:dimension2");
What am I missing? Why isn't this visible in google api results and/or where I can I see any errors?
Some hours later, this code started to work, which suggests to me that custom dimensions are not immediately available to the api. In this case the api recognized dimension2 long after the data was visible in the analytics website.
Second possibility to check which can cause this seemingly good code to not work, you are using the wrong view id. Of the 2 views I tested, this only works on one.

Complex claims in JWT

The JWT RFC does not seem to have any problem containing complex arrays such as:
{
"email": "test#test.com",
"businesses": [
{
"businessId": "1",
"businessName": "One",
"roles": [
"admin",
"accountant"
]
},
{
"businessId": "2",
"businessName": "Two",
"roles": [
"support"
]
}
]
}
And this seems a desirable scenario for our needs, since as part of the token we'd like to have a list of businesses a user has access to and what roles does he have for each business (it's part of its identity). The authorization policies at the API would later understand those groups and apply the required authorization logic.
I have seen that with IdentityServer4 the claims are added to the ProfileDataRequestContext's IEnumerable<Claim> IssuedClaims property.
Is there any recommended alternative to this complex claim structure? If not, is there any way to build that structure with IdentityServer4 (maybe some extension?) or the only way would be to manually serialize the JSON since the Claim seems to accept only a string?
PS: I have seen this question and this other where one of the authors of Identity Server talks about something similar being an antipattern. Not sure if the antipattern would be to have complex claims' structure or "authorization implementation details" in the claims.
Any advice on this would be great!
UPDATE:
After giving some thoughts I agree having a complex hierarchy of claims is not desirable and I could go around this problem with a dirty solution of prefixing roles for each businessId. Something like this:
{
"email": "test#test.com",
"roles": [
"1_admin",
"1_accountant",
"2_support"
],
"businesses": [
"1_One",
"2_Two"
]
}
that way I keep a simple structure and later on, at the client or API I can read the claims and find out that 1 is the id for the business with name One and it has the roles admin and account.
Would this be a better solution?
Claims are about identity information - and not complex permission "objects". You are far better off with a dedicated permission service that returns your permissions in any format you want based on the identity of the user.
I also hope your permission data doesn't change while the token is being used, otherwise you end up with stale data.
That said - claims are always strings in .NET - but you can serialize JSON objects into it by setting the ClaimValueType to IdentityServerConstants.ClaimValueTypes.Json.

Strongloop REST Connector - connecting to non-REST remote resources

We have an existing web application which has an API not based on REST. We'd like to put a REST API in front of it, using Strongloop, however, getting lost in the documentation and not sure if this can be achieved.
Example:
Want to configure an endpoint in Strongloop which looks like;
localhost:3000/api/DataObject/Orders?StartDate=01/01/2016&EndDate=31/01/2016
A GET on this end point should service the request from our existing web application, where the URL would like;
localhost:4000/wh?Page=ObjectBuilder&Name=Orders&StartDate=01/01/2016&EndDate=31/01/2016
i.e. take Orders from the API request and insert into the remote URL, along with the remaining parameters.
I could code this using express.js, but was wondering if this is possible using configuration in Strongloop?
Thanks!
I think you might be able to use the built-in REST connector even though your legacy API is not REST per se (although you don't get all the benefits of the built-in mapping to find, create, destroy, etc). The connector simply translates URLs into model methods. That said, I think you do need to have the old API spit out JSON... does it do that? If not, then you basically just have to write a full translator.
This is not working code, but might help you get part of the way there.
In your server/datasources.json file:
"old-service": {
"name": "old-service",
"connector": "rest",
"operations": [{
"template": {
"method": "GET",
"url": "http://localhost:4000/wh",
"headers": {
// whatever you might need to send...
},
"query": {
"Page": "ObjectBuilder",
"Name": "{name}",
"StartDate": "{start}",
"EndDate": "{end}"
},
"responsePath": "$.results.theObject" // be sure to custom ize this
},
"functions": {
"buildObject": ["name", "start", "end"]
}
}]
}
In your server/model-config.json be sure too map your DataObject model to this datasource:
{
// ...
"DataObject": {
"public": true,
"dataSource": "old-service"
},
}
And in your model itself (common/models/DataObject.js) you can now call the buildObject() method:
DataObject.buildObject('Order', '01/01/2016', '31/01/2016', function(err, result, response) {
if (err) { ... }
// otherwise look at the result or response...
});
Now that you can call this method, you could put it into a remoteMethod or even override the default find method for this model.
Good luck, but in many of these cases you simply have to write the "conversion" code yourself. Might be easier to rewrite the API from scratch. ;)

Elasticsearch query design via RESTful API

I'm trying to build a query that first lets me get a list of followers that are following a user, second it should take that list and then check to see if they are 'online'.
I have two 'indexes' or endpoints /channel and /following.
The channel endpoint JSON object looks like this (parts abbreviated)
{ channel: {"username":"username1", ... , "online":"true" } }
The following endpoint object looks a bit like this
{ following : {"username1":{"username2":"username2", "username3":"username3"} }
if I run a simple query /following/_search I get back hits like...
{
"_index": "following",
"_type": "following",
"_id": "_Liso_",
"_score": 1,
"_source": {
"Gabe": "Gabe",
"Gavin": "Gavin"
}
}
This result means that Gavin is following Gabe.
I believe the issue is how I'm storing the data.
In firebase my data looks like this
following
|---Gabe
|----Gavin:Gavin
so each child object of following object has key/value children of {username}:{username}
Now I can run queries that individually get the results I need. For example, if I ask ElasticSearch (ES) if channel "Gavin" is "online" I get back one result depending on if they are or are not online. And same with Following. However I can't seem to get the query to first see who is following Gavin and then see if they are online and return those users whom are online.
I've found a better solution (or maybe not). First you query the database for users whom are following a user.
From this list you send another query
{
"query":{
"filtered":{
"query":{
"match_all":{}
},
"filter":{
"bool":{
"must":{
"terms":{
"username":["username1"]
}
},
"must_not":{
"terms":{
"online":["true"]
}
}
}
}
}
}
}
This works however the username cannot be mixed with capitols. I don't know if this is an indexing issue on my part or terms have to be very specific. The solution I'm using on the client side is to lowercase the search terms before I submit them. It's crude and hacky but it works for now.
Issues that I may run into:
If a user has millions of followers pulling all that data from the
database will make the client sluggish.
a possible solution to this is to paginate the following results and run the query for every 20 returned results.
I'll continue to revise the answer as I develop / learn better query methods.