Lighthouse GraphQL - How to return current_user in mutation resolver? - laravel-passport

I have a situation where I'm writing a custom mutation resolver, and currently have to pass the current_user's ID from my frontend to be able to then perform a ::find on the User model. What would be ideal however, is to be able to use an instance of current_user so that I don't have to rely on passing over an ID to my GraphQL server.
I'm still fairly new to the world of Laravel and GraphQL in general, however I've been reading up on the Lighthouse docs that mention the #auth directive, and other StackOverflow answers that mention using auth('api')->user(), however that returns NULL for me. I should also mention that I'm using the lighthouse-graphql-passport-auth library for dealing with user authentication, if that makes any difference. Does anybody know how to access current_user?
public function __invoke($_, array $args)
{
// $user = \App\Models\User::find($args['id']); <--- not ideal
$user = auth('api')->user(); <--- returns NULL
var_dump($user);
foreach ($user->notifications as $notification) {
$notification->viewed = true;
$notification->save();
}
$notifications = $user->notifications->toArray();
return [
'status' => 'Success',
'notifications' => $notifications
];
}

I found an interesting part in the lighthouse-graphql-passport-auth docs that discuss setting a [global middleware][3] to insert the logged in user into the $context. This was exactly like what I needed, and after adding the line into the middleware section of lighthouse.php config as mentioned in the docs, I was able to use $context->user() to return the currently logged in user.

Related

Create custom API with strapi

I've a content type called continent. Which we the name suggests contains all the information about each continents. Strapi already created API endpoints for me like
continents/:id
But I want to search the continent by it's name since the general user won't be able to search by id
I've created the endpoint
continents/:continent_name
I've also created custom controller following documentation
const { sanitizeEntity } =
requiree('strapi-utils');
module.exports = {
async findOne(ctx) {
const { continent_name } = ctx.params;
const entity = await
strapi.services.continent.findOne({
continent_name
});
return sanitizeEntity(entity, { model:
continents });
And also exposed the API to public
But doesn't seem to anything
Just returns error
How am I supposed to do it
For your use case, you don't need to extend the model controller. You can just pass the continent name as a query param . For example, your url could be something like base_url/continent?continent_name=Asia.
For the code mentioned in the question, there is an issue, the model name should be strapi.models.continent and not continents. Also in the first line requiree('strapi-utils'), you have an extra e in the require. I am assuming that was just a typo.

How to chain express-validator based on query values?

I am trying to find a solution to chain conditions based on the query values passed to the route.
Task1:
// if value for query A = noIdNeeeded, then i do not need to search for a second queryB
/endpoint?queryA=noIdNeeded
Task2:
// if value for query A = idNeeded, then i need to ensure second queryB exists
/endpoint?queryA=idNeeded&queryB=SomeId
I am having trouble with the writing parameter for Task 2.
For task 1 i have use this logic and works fine [query('page').exists().notEmpty().isIn(seoPageTypes),]
So far I have seen there is an if clause that we can use probably (link) however implementing this has been a challenge due to lack of examples and no prior experience.
If anyone can guide on how to do this correctly or any hint is much appreciated.
Make sure you have a new version of express-validator installed.
The following should do your job.
query('queryA').exists().notEmpty().isIn(seoPageTypes),
query('queryB')
.if(query('queryA').equals('idNeeded'))
.exists().notEmpty().withMessage('queryB must exist'),
Another approach is to use a custom validator
query('queryA').exists().notEmpty().isIn(seoPageTypes)
.custom((value, {req}) => {
if (value === "idNeeded" && !req.query.queryB) {
throw new Error('queryB must exist');
}
return true;
}),
Use what suits you more :)

MongoDB update object and remove properties?

I have been searching for hours, but I cannot find anything about this.
Situation:
Backend, existing of NodeJS + Express + Mongoose (+ MongoDB ofcourse).
Frontend retrieves object from the Backend.
Frontend makes some changes (adds/updates/removes some attributes).
Now I use mongoose: PersonModel.findByIdAndUpdate(id, updatedPersonObject);
Result: added properties are added. Updated properties are updated. Removed properties... are still there!
Now I've been searching for an elegant way to solve this, but the best I could come up with is something like:
var properties = Object.keys(PersonModel.schema.paths);
for (var i = 0, len = properties.length; i < len; i++) {
// explicitly remove values that are not in the update
var property = properties[i];
if (typeof(updatedPersonObject[property]) === 'undefined') {
// Mongoose does not like it if I remove the _id property
if (property !== '_id') {
oldPersonDocument[property] = undefined;
}
}
}
oldPersonDocument.save(function() {
PersonModel.findByIdAndUpdate(id, updatedPersonObject);
});
(I did not even include trivial code to fetch the old document).
I have to write this for every Object I want to update. I find it hard to believe that this is the best way to handle this. Any suggestions anyone?
Edit:
Another workaround I found: to unset a value in MongoDB you have to set it to undefined.
If I set this value in the frontend, it is lost in the REST-call. So I set it to null in the frontend, and then in the backend I convert all null-values to undefined.
Still ugly though. There must be a better way.
You could use replaceOne() if you want to know how many documents matched your filter condition and how many were changed (I believe it only changes one document, so this may not be useful to know). Docs: https://mongoosejs.com/docs/api/model.html#model_Model.replaceOne
Or you could use findOneAndReplace if you want to see the document. I don't know if it is the old doc or the new doc that is passed to the callback; the docs say Finds a matching document, replaces it with the provided doc, and passes the returned doc to the callback., but you could test that on your own. Docs: https://mongoosejs.com/docs/api.html#model_Model.findOneAndReplace
So, instead of:
PersonModel.findByIdAndUpdate(id, updatedPersonObject);, you could do:
PersonModel.replaceOne({ _id: id }, updatedPersonObject);
As long as you have all the properties you want on the object you will use to replace the old doc, you should be good to go.
Also really struggling with this but I don't think your solution is too bad. Our setup is frontend -> update function backend -> sanitize users input -> save in db. For the sanitization part, we use a helper function where we integrate your approach.
private static patchModel(dbDocToUpdate: IModel, dataFromUser: Record<string, any>): IModel {
const sanitized = {};
const properties = Object.keys(PersonModel.schema.paths);
for (const key of properties) {
if (key in dbDocToUpdate) {
sanitized[key] = data[key];
}
}
Object.assign(dbDocToUpdate, sanitized);
return dbDocToUpdate;
}
That works smoothly and sets the values to undefined. Hence, they get removed from the document in the db.
The only problem that remains for us is that we wanted to allow partial updates. With that solution that's not possible and you always have to send everything to the backend.
EDIT
Another workaround we found is setting the property to an empty string in the frontend. Mongo then also removes the property in the database

How can i pass input argument when writing loopback-testing

I am writing a test driven development for my strongloop API code with the help of loopback-testing .
Here they do not have any detailed document on this, so i am stuck with case of argument passing with the API call
Example i have a below case,
Method : PUT
URL : /api/admin/vineyard/<vineyard_id>
i need to pass the below arguments with this URL
1. 'vineyard_id' is a id of vine, it should be an integer .
2. in header = 'token'
3. in body = '{'name':'tastyWine','price':200}'
How can i pass these three arguments with this API ?
I can easily handle ,if there is only two types of arguments
Example :
Method : POST
`/api/user/members/<test_username>/auth'`
arguments : test_username and password
I can handle this like this ,
lt.describe.whenCalledRemotely('POST',
'/api/user/members/'+test_username+'/auth', {
'password': test_passwords
},
But how can i handle the above case , Many thanks for your answers for this example.
I'm not entirely sure what your specific problem is, but I will attempt to walk through everything you should need.
I am assuming you are using the predefined prototype.updateAttributes() method for your model as described here.
Next assumption is that you want to use the built-in authentication and authorization to allow the user to call this method. Given that assumption, you need something like this in your test code:
var vineyard_id = 123; //the id of the test item you want to change
var testUser = {email: 'test#test.com',password: 'test'};
lt.describe.whenCalledByUser(testUser, 'PUT', '/api/admin/vineyard/'+vineyard_id,
{
'name':'tastyWine',
'price':200
},
function () {
it('should update the record and return ok', function() {
assert.equal(this.res.statusCode, 200);
});
}
);
If you are using the out-of-the-box user model, you should be fine, but if you extended the model as is commonly done, you may need something like this early on in your test file:
lt.beforeEach.withUserModel('user');
Also, be aware of a few (currently incomplete) updates to will allow for better handling of built-in model extensions: Suggestions #56, Add support for non-default models #57, and givenLoggedInUser() function throws error #59.

Couchdb and Sofa Help

I'm new to Couchdb just a few week back I git clone the couchdb app called sofa [what and app] . Over the week It was going great but suddenly today I stumble upon something.
Here what I meant
when I browse the Sofa app and try to create a Post without the title
it prompt with and alert box "The document could not be saved: The database could not be created, the file already exists." which was weird as looking at the source I found that the require (in validate_doc_update.js return its custom json error) something like in this format {"forbidden" : message }) with forbidden as key
v.forbidden = function(message) {
throw({forbidden : message})
};
v.require = function() {
for (var i=0; i < arguments.length; i++) {
var field = arguments[i];
message = "The '"+field+"' field is required.";
if (typeof newDoc[field] == "undefined") v.forbidden(message);
};
};
in validate_doc_update.js
if (newDoc.type == 'post') {
if (!v.isAuthor()) {
v.unauthorized("Only authors may edit posts.");
}
v.require("created_at", "author", "body", "format", "title");
inspecting the response state that the json returned was found to be different from the json had it would have been return by the above require function in validate_doc_update.js
here is the json
{"error":"file_exists","reason":"The database could not be created, the file already exists."}
This make be believe that the validation in validation_doc_update.js only execute during updating of document
to Prove this point I try to update a document without the title, expecting that it would return the error but surprisingly the document just got saved
so Here are my Question regarding all the Point I mention above
Does validate_doc_update.js "validate" work only during updation of document
if YES
then
how can I manage to succeed in updating a post without the error [Weird bypassing the Validation Completely] . + How can execute validation on create of a document
if NO
then
What is the Error {"error":"file_exists","reason":"The database could not be created, the file already exists."} that is prevent a document to be saved
Can anyone please share light on all the questions listed here
Yes, the validate_doc_update functions are run only when updating documents (include creation and deletion).
The function you show here will allow a document without a title as long as its type is not "post". If you could include the actual request you attempted, I could confirm it.
Finally, the ""The database could not be created" is because you are attempting to create the database (by doing PUT /dbname/ instead of PUT /dbname/docid, I would guess) when it already exists. Again, if you would include the actual request, I could confirm that too.

Resources