How to chain express-validator based on query values? - node.js

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 :)

Related

SwiftUI, Xcode 13, Question about .filter'ing CoreData

#FetchRequest(
entity: Client.entity(),
sortDescriptors: [])
private var clients: FetchedResults<Client>
(...)
var searchResults: FetchedResults<Client> {
if searchText.isEmpty {
return clients
} else {
return clients.filter({$0.name!.contains(searchText)}) // Error here!
}
}
(...)
ForEach(searchResults, id: \.self) { client in
(...)
Error
Cannot convert return expression of type '[FetchedResults<Client>.Element]' (aka 'Array<Client>') to return type 'FetchedResults<Client>'
Hi,
I'm not sure how my logic is wrong. Could someone please tell me how to fix searchResults?
Also, is this the more efficient way to filter results or should I filter in the ForEach()? It seems pretty slow when I put the filter in ForEach()
While I know you have a code fix, and it is the correct one, I wanted to answer the question for posterity as I have run into this myself. The logic is wrong simply because your searchResults var is of type FetchedResults<Client>, but a filter returns an Array. So, the error message is telling you exactly the problem. You are trying to return an [Client] as a FetchedResults<Client> so you have a type mismatch.
You have two solutions:
You can filter in the fetch request, which is how you solved it per #Larme suggestion. This is especially helpful if you only need the filtered results in the UI and/or you have a lot of results.
You can filter when you use your fetched results. This is useful when you want your user to be able to determine what is filtered out by their own selections, but you don't know what filtering they will want ahead of time or that regardless of the filtering you are doing, you may need the whole FetchRequest later.

Lighthouse GraphQL - How to return current_user in mutation resolver?

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.

Firestore security rules with set {merge}

In my Firestore data I have the following
ItemID
amountOfItemsToPurchase: 1
itemsLeft: 3
If a user wants to update the items to purchase for the same ItemID or create the document if the ItemID is not present, I use set(, {merge:true}, however in terms of the Firestore Security rules things get complicated.
I've written the following test:
const initialUserDoc = adminFirestore.collection("Users").doc(VALID_USER_ID).collection("Cart").doc(documentID);
await initialUserDoc.set({
"amountOfItemsToPurchase": 1,
"itemsLeft": 3
});
// Get the user's node and grab the example user
const userTestRef = db.collection("Users").doc(VALID_USER_ID).collection("Cart").doc(documentID);
await firebase.assertFails(userTestRef.set({
"amountOfItemsToPurchase": firebase.firestore.FieldValue.increment(1000),
}, {merge: true}));
This test results in the following: Error: Expected request to fail, but it succeeded
What I want for every scenario (update or create), to avoid that the amountOfItemsToPurchase exceeds the itemsLeft, for that I use the following:
request.resource.data.amountOfItemsToPurchase <= request.resource.data.itemsLeft => This will be in the allow create portion.
This rises the following:
Does the allow create or the allow update** will be called? and also why is not taking into account the itemsLeft variable
From your description and the comments, your security rule looks OK.
Let me write it for futher discussion:
match /items/{id} {
allow create, update:
if request.resource.data.amountOfItemsToPurchase < request.resource.data.itemsLeft;
}
First gotcha: use request.resource to be able to read the value of the field after the update.
Second gotcha: when using set, the FieldValue.increment sentinel does not increment but sets the value. You should use the update function to actually increment the value.
Third gotcha: FieldValue.increment with set function and {merge: true} does increment the value!
So in the end the rule works fine as is, I can confirm it works for me.

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.

Resources