I have two models and both of them have Users. Let's call them:
Users_A
Users_B
I have to find a user in Users_A, but if it doesn't exist in Users_A then I have to search on Users_B.
If the user exists on User_B then I want to return it when I call Users_A.findOne()
Is there a way to do this?
You can create remote method to achieve this task and define async waterfall method to run code through several models.
for example:
in remote method execution:
async.waterfall([
function(callback){
//find results on USERS_A
},
function(data,callback){
//find results on USERS_B
}],
function(err){
if(err) console.log(err);
//return final result from either USERS_A or USERS_B
}
);
hope this method will helpful.
cheers.
I solved it in this way:
Users_A.afterRemote('findOne' function(ctx, instance, next) {
if (ctx.result) {
return next();
} else {
Users_A.app.models.Users_B.findOne(ctx.req.query.filter,
(error, data) => {
if (data) {
//some logic here
return next();
} else {
//instance the error
return next(error);
}
}
}
}
That worked for me, hope it helps
Related
I'm using apollo link in schema stitching as an access control layer. I'm not quite sure how to make the link return error response if a user does not have permissions to access a particular operation. I know about such packages as graphql-shield and graphql-middleware but I'm curious whether it's possible to achieve basic access control using apollo link.
Here's what my link looks like:
const link = setContext((request, previousContext) => merge({
headers: {
...headers,
context: `${JSON.stringify(previousContext.graphqlContext ? _.omit(previousContext.graphqlContext, ['logger', 'models']) : {})}`,
},
})).concat(middlewareLink).concat(new HttpLink({ uri, fetch }));
The middlewareLink has checkPermissions that returns true of false depending on user's role
const middlewareLink = new ApolloLink((operation, forward) => {
const { operationName } = operation;
if (operationName !== 'IntrospectionQuery') {
const { variables } = operation;
const context = operation.getContext().graphqlContext;
const hasAccess = checkPermissions({ operationName, context, variables });
if (!hasAccess) {
// ...
}
}
return forward(operation);
});
What should I do if hasAccess is false. I guess I don't need to forward the operation as at this point it's clear that a user does not have access to it
UPDATE
I guess what I need to do is to extend the ApolloLink class, but so far I didn't manage to return error
Don't know if anyone else needs this, but I was trying to get a NetworkError specifically in the onError callback using Typescript and React. Finally got this working:
const testLink = new ApolloLink((operation, forward) => {
let fetchResult: FetchResult = {
errors: [] // put GraphQL errors here
}
let linkResult = Observable.of(fetchResult).map(_ => {
throw new Error('This is a network error in ApolloClient'); // throw Network errors here
});
return linkResult;
});
Return GraphQL errors in the observable FetchResult response, while throwing an error in the observable callback will produce a NetworkError
After some digging I've actually figured it out. But I'm not quite sure if my approach is correct.
Basically, I've called forward with a subsequent map where I return an object containing errors and data fields. Again, I guess there's a better way of doing this (maybe by extending the ApolloLink class)
const middlewareLink = new ApolloLink((operation, forward) => {
const { operationName } = operation;
if (operationName !== 'IntrospectionQuery') {
const { variables } = operation;
const context = operation.getContext().graphqlContext;
try {
checkPermissions({ operationName, context, variables });
} catch (err) {
return forward(operation).map(() => {
const error = new ForbiddenError('Access denied');
return { errors: [error], data: null };
});
}
}
return forward(operation);
});
This is how I get all data in config.js:
this.get('/rentals', function (schema, request) {
if (request.queryParams.value) {
// The code should be here...
} else {
return schema.rentals.all();
}
}
I looked at documentation, but there's no way to get filtered ones. Apparently there are commands like all(), find(), findBy(), create(), etc. But there's nothing that filters out and returns. Any help?
Figured out: filter could be used with all().
this.get('/rentals', function (schema, request) {
if (request.queryParams.value) {
let filteredRentals = schema.rentals.all().filter(function (i) {
return i.attrs.value.toLowerCase().indexOf(request.queryParams.value.toLowerCase()) !== -1;
});
return filteredRentals;
}
return schema.rentals.all();
});
Actually I'm trying to cancel a hook to avoid duplicate pair entity-name/subname - by a server-side check.
My example is, if an entity already exists with the same name and subname, I'd like it not to be created/persisted.
Here's my code so far in my entity.js:
module.exports = function (ContactType) {
ContactType.observe('before save', function filterSameEntities(ctx, next) {
if (ctx.instance) {
ContactType.find({where: {name: ctx.instance.name, subname: crx.instance.subname}}, function (err, ct) {
if (ct.length > 0) {
//I'd like to exit and not create/persist the entity.
next(new Error("There's already an entity with this name and subname"));
}
});
}
next();
});
};
Actually the error is correctly displayed, but the entity is still created and I would like that it wouldn't be the case.
Your last next(); statement is always called, hence the save-action always happens.
You can end further execution using return.
Keep in mind that .find() is async, so just adding return inside the callback would still cause that last next(); statement to run.
Please try this:
module.exports = function (ContactType) {
ContactType.observe('before save', function filterSameEntities(ctx, next) {
if (!ctx.instance) {
return next();
}
ContactType.find({where: {name: ctx.instance.name, subname: ctx.instance.subname}}, function (err, ct) {
if (err) { // something went wrong with our find
return next(err);
}
if (ct.length > 0) {
//I'd like to exit and not create/persist the entity.
return next(new Error("There's already an entity with this name and subname"));
}
return next();
});
});
};
I'm trying to search for all employees that have a title of developer
As per the documentation (http://guides.emberjs.com/v1.10.0/models/finding-records/) It seems the correct way to do this is:
return this.store.find('employee', { title: "developer" });
But this is not working in Ember CLI 0.2.2, and I can't even see my template when I try this, even though when I do
return this.store.find('employee')
I can see a list of all employees and there are multiple employees with that title
Turns out I needed to override the DS.FixtureAdapter::queryFixtures method. I went into my adapters/application.js file and added
queryFixtures: function(records, query, type) {
return records.filter(function(record) {
for(var key in query) {
if (!query.hasOwnProperty(key)) { continue; }
var value = query[key];
if (record[key] !== value) { return false; }
}
return true;
});
}
I have two questions regarding "beforeRemote" method of loopback-
How do I get hold of model methods inside beforeRemote method? I mean inside beforeRemote I want to invoke (lets say) "upsert" method of the model.
How do I return invocation from beforeRemote? By return I mean instead of hitting the target invoked method the execution will return from beforeRemote method.
My code -
Installation.beforeRemote("create", function (context, result, next) {
var data = context.req.body;
console.log("ImEI" + data.imei);
data.vendor = "jahid";
var self = this;
var filter = {where: {imei: data.imei}};
//the self here point to global object. but i want self to point to model
self.findOne(filter, function (err, result) {
if (result) {
data.id = result.id;
self.upsert(data, function(err, result){
if(err){
next(err);
} else if(result) {
//here i want to send a valid response back to client with 200 and body as my model.
next(data);
}
});
return;
}
next();
});
});
You have access to the Installation model from the module.exports declaration:
module.exports = function(Installation) {
...
Installation.upsert...
...
}
You have access to the response object from the context object. So you could just respond with something like context.res.send('hello world') and not call next().