"AxiosError: options must be an object: "ERR_BAD_OPTION_VALUE" - javascript

I am trying to pass an array as a parameter in an axios get. This is a react application using current packages.
I have used the code per solution to other similar issues. I have tried applying two different methods/syntax forms, as can been seen from the commented code.
Here is the code:
import Qs from "qs";
import axios from "axios";
console.log(
"ContactContainer - useEffect[currentUser] - Before check of currentUser.contactRequestsTo.length > 0 ",
currentUser.contactRequestsTo.length
);
const userIds = JSON.stringify(currentUser.contactRequestsTo);
//const userIds = currentUser.contactRequestsTo;
console.log("ContactContainer - useEffect[currentUser] - userIds ", userIds);
if (currentUser.contactRequestsTo.length > 0) {
(async () => {
//const contactRequestsToDetails = await axios.get(`${getUserDetailsRoute}`,{
// params: {
// arrayOfUserIds: userIds
// },
// paramsSerializer: (params) => Qs.stringify(params, {arrayFormat: 'repeat'})
//});
const instance = axios.create({
paramsSerializer(params) {
return Qs.stringify(params, { arrayFormat: "brackets" });
},
});
const contactRequestsToDetails = await instance.get(getUserDetailsRoute);
console.log(
"ContactContainer - useEffect[currentUser] - contactRequestsToDetails = ",
contactRequestsToDetails
);
})();
}
Some of the package versions as follows:
"axios": "^1.2.1",
"mongoose": "^6.8.0",
"qs": "^6.11.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
The raw array, currentUser.contactRequestsTo, is:
ContactContainer - useEffect[] - currentUser.contactRequestsTo =
Array(2)
0:"63b476a79c42d2b60fc39cbc"
1:"63b476ea9c42d2b60fc39ccd"
length:2
[[Prototype]]:Array(0)
I tried to use the raw array as an input that then applied, JSON.Stringify to the array to get the UserIds shown below:
userIds ["63b476a79c42d2b60fc39cbc","63b476ea9c42d2b60fc39ccd"]
I also used the two formats, one is commented out and the other is as shown.
In all cases I received the same error.
Uncaught (in promise)
code:"ERR_BAD_OPTION_VALUE"
message:"options must be an object"
name:"AxiosError"
stack:"AxiosError: options must be an object\n at Object.assertOptions (http://localhost:8080/static/js/main.d2f30e44.js:2:356049)\
I see that other users have experienced the same error and have applied the changes as I interpreted the solutions.
Is there something wrong with my syntax or some other issue that would cause this error?

The config for paramsSerializer changed in v1.0.0. It is now an object with encode, serialize and indexes properties.
If you want to add square brackets to your query parameter arrays, use the following. Also, don't serialise the array to JSON
(async () => {
const currentUser = {
contactRequestsTo: [
"63b476a79c42d2b60fc39cbc",
"63b476ea9c42d2b60fc39ccd"
],
};
const getUserDetailsRoute = "https://echo.zuplo.io/"; // just for the demo
const contactRequestsToDetails = await axios.get(getUserDetailsRoute, {
params: {
arrayOfUserIds: currentUser.contactRequestsTo, // the actual array
},
paramsSerializer: {
indexes: false, // empty brackets like `arrayOfUserIds[]`
},
});
console.log(contactRequestsToDetails.data.url);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.2.2/axios.min.js" integrity="sha512-QTnb9BQkG4fBYIt9JGvYmxPpd6TBeKp6lsUrtiVQsrJ9sb33Bn9s0wMQO9qVBFbPX3xHRAsBHvXlcsrnJjExjg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
See https://github.com/axios/axios#request-config
It's not clear why you feel you need to use paramsSerializer at all since indexes: false is the default.

Related

Cannot update the nested Array of the boolean

I am trying to update an array to show the router is up by using a true or false statement.
But I am getting a response back that "record is updated" but I am not able to see the change in MongoDB collection.
This is my function I am running
exports = async function({ body }) {
const data = JSON.parse(body.text());
const ACAS_Mission = data.ACAS_Mission;
const terminal = data.terminals[0].terminal;
const router = data.terminals[0].XLESS.router;
const comstat = context.services
.get("mongodb-atlas")
.db("Comstat")
.collection("comstat");
// Find the document
const filter = { ACAS_Mission, "terminals.terminal": terminal };
const document = await comstat.findOne(filter);
console.log("Document:", JSON.stringify(document));
// Update the document
const updateFilter = { ACAS_Mission, "terminals.terminal": terminal };
const update = { $set: { "terminals.$[t].XLESS.router": router } };
const options = { arrayFilters: [{ "t.terminal": terminal }] };
const result = await comstat.updateOne(updateFilter, update, options);
console.log("Update result:", JSON.stringify(result));
return { message: "Record updated." };
};
and this is the document I am trying to update
MongoDB Document
The JSON Body that I am passing when I do a PUT
{
"ACAS_Mission": "xx53583",
"terminals": [
{
"terminal": "SNN573330",
"XLESS": {
"router": true
}
}
]
}
As you wrote in your comment, MongoDB processes the request, but there are no changes. That means that nothing in your datebase matched your query (as one may conclude from matchedCount being 0).
Check your query: Your screenshot shows that terminals contains an array containing another array containing an object. You are searching for an array directly containing an object, so adding [braces] around the statement might work.
Was able to change the JSON body and a few function fixes and it works!
{
"ACAS_Mission": "xx53583",
"terminals": [
[
{
"terminal": "SNN573330",
"NodeID": 251,
"XLESS": {
"router": false
}
}
]
]

NextJs - Additional keys were returned from getStaticPaths

I'm working on a client's project with NextJs,
On the blog section we have different paths, blog/[:category], blog/[:category]/[:post] and blog/author/[:author] And to get this done I'm using getStaticPaths and getStaticProps.
I first fetch all the posts and authors from ContentfulAPI and then loop into them to create a valid path to pass it into the paths array
Ps: It works when I hard code every path individually..
thats my function:
export const getStaticPaths = async () => {
const posts = await DataController.getEntriesByContentType(
"componentBlog",
);
const blogPosts = posts.items.map(item => {
return {params: {blog_post: [item.fields.category.replace(/\s+/g, '-').replace(/'/g, '').toLowerCase(), item.fields.slug]}}
})
const authors = await DataController.getEntriesByContentType(
"author",
);
const authorPaths = authors.items.map(item => {
return {params: {blog_post: ['author', item.fields.slug]}}
})
return {
paths: [
blogPosts,
authorPaths,
],
fallback: false,
}
}
And i get this error when I try to access a blog link :
error - Error: Additional keys were returned from `getStaticPaths` in page "/blog/[...blog_post]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { blog_post: ... } }
Keys that need to be moved: 0, 1, 2, 3, 4, 5, 6, 7, 8.
at C:\Workspace\phoenix-v2\next\new-phoenix\node_modules\next\dist\build\utils.js:518:23
at Array.forEach (<anonymous>)
at Object.buildStaticPaths (C:\Workspace\phoenix-v2\next\new-phoenix\node_modules\next\dist\build\utils.js:492:17) at processTicksAndRejections (internal/process/task_queues.js:95:5) {
type: 'Error',
page: '/blog/[...blog_post]'
}
I'm not sure why I run into this error..
thank you for helping!
You're currently passing an array with the params, use the spread operator (...) to reduce it. Docs
return {
paths: [...blogPosts, ...authorPaths],
...
};

mongoosejs - find() using nested objects

question is possibly a duplicate but I haven't found anything that provides an appropriate answer to my issue.
I have an ExpressJS server which is used to provide API requests to retrieve data from a MongoDB database. I am using mongoosejs for the MongoDB connection to query/save data.
I am building a route that will allow me to find all data that matches some user input but I am having trouble when doing the query. I have spent a long while looking online for someone with a similar issue but coming up blank.
I will leave example of the code I have at the minute below.
code for route
// -- return matched data (GET)
router.get('/match', async (req, res) => {
const style_data = req.query.style; // grab url param for style scores ** this comes in as a string **
const character_data = req.query.character; // grab url param for character scores ** this comes in as a string **
// run matcher systems
const style_matches = style_match(style_data);
res.send({
response: 200,
data: style_matches
}); // return data
});
code for the query
// ---(Build the finder)
const fetch_matches_using = async function(body, richness, smoke, sweetness) {
return await WhiskyModel.find({
'attributes.body': body,
'attributes.richness': richness,
'attributes.smoke': smoke,
'attributes.sweetness': sweetness
});
}
// ---(Start match function)---
const style_match = async function (scores_as_string) {
// ---(extract data)---
const body = scores_as_string[0];
const richness = scores_as_string[1];
const smoke = scores_as_string[2];
const sweetness = scores_as_string[3];
const matched = [];
// ---(initialise variables)---
let match_count = matched.length;
let first_run; // -> exact matches
let second_run; // -> +- 1
let third_run; // -> +- 2
let fourth_run; // -> +- 3
// ---(begin db find loop)---
first_run = fetch_matches_using(body, richness, smoke, sweetness).then((result) => {return result});
matched.push(first_run);
// ---(return final data)---
return matched
}
example of db object
{
_id: mongoid,
meta-data: {
pagemd:{some data},
name: whiskyname
age: whiskyage,
price: price
},
attributes: {
body: "3",
richness: "3",
smoke: "0",
sweetness: "3",
some other data ...
}
}
When I hit the route in postman the JSON data looks like:
{
response: 200,
data: {}
}
and when I console.log() out matched from within the style match function after I have pushed the it prints [ Promise(pending) ] which I don't understand.
if I console.log() the result from within the .then() I get an empty array.
I have tried using the populate() method after running the find which does technically work, but instead of only returning data that matches it returns every entry in the collection so I think I am doing something wrong there, but I also don't see why I would need to use the .populate() function to access the nested object.
Am I doing something totally wrong here?
I should also mention that the route and the matching functions are in different files just to try and keep things simple.
Thanks for any answers.
just posting an answer as I seem to have fixed this.
Issue was with my .find() function, needed to pass in the items to search by and then also a call back within the function to return error/data. I'll leave the changed code below.
new function
const fetch_matches_using = async function(body, richness, smoke, sweetness) {
const data = await WhiskyModel.find({
'attributes.body': body,
'attributes.richness': richness,
'attributes.smoke': smoke,
'attributes.sweetness': sweetness
}, (error, data) => { // new ¬
if (error) {
return error;
}
if (data) {
console.log(data)
return data
}
});
return data; //new
}
There is still an issue with sending the found results back to the route but this is a different issue I believe. If its connected I'll edit this answer with the fix for that.

graphql passing dynamic data to mutation

haven't used graphql or mongodb previously. What is the proper way to pass objects for the update mutation?
Since the only other way i see to pass multiple dynamically appearing parameters is to use input type which is appears to be a bit ineffective to me (in terms of how it looks in the code, especially with bigger objects), i just pass the possible values themselves. however in this case i need to dynamically construct updateObject, which again, going to get messy for the bigger models.
for example now i did:
Mutation: {
updateHub: async (_, { id, url, ports, enabled }) => {
const query = {'_id': id};
const updateFields = {
...(url? {url: url} : null),
...(ports? {ports: ports} : null),
...(enabled? {enabled: enabled} : null)
};
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
any advise on the better way to handle this?
thanks!
It appears your code could benefit from using ES6 spread syntax -- it would permit you to deal with an arbitrary number of properties from your args object without the need for serial tertiary statements.
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = { ...restArgs };
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
If for some reason you need to explicitly set the undefined properties to null in your object, you could possibly use some a config obj and method like defaults from the lodash library as shown below:
import { defaults } from 'lodash';
const nullFill = { url: null, ports: null, enabled: null }; // include any other properties that may be needed
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = defaults(restArgs, nullFill);
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
Also, FWIW, I would consider placing the dynamic arguments that could be potentially be updated on its own input type, such as HubInput in this case, as suggested in the graphql docs. Below I've shown how this might work with your mutation. Note that because nothing on HubInput is flagged as requird (!) you are able to pass a dynamic collection of properties to update. Also note that if you take this appraoch you will need to properly destructure your args object initially in your mutation, something like { id, input }.
input HubInput {
url: String
ports: // whatever this type is, like [String]
enabled: Boolean
// ...Anything else that might need updating
}
type UpdateHubPayload {
success: Boolean
message: String
hub: Hub // assumes you have defined a type Hub
}
updateHub(id: Int, input: HubInput!): UpdateHubPayload

How to clone a node to another path based on a reference value from the initial path on Google Cloud Functions?

I am trying clone an "original" node's data (as soon as I create the data) to a path that is based on the original node's path.
This is my data structure:
root: {
doors: {
111111111111: {
MACaddress: "111111111111",
inRoom: "-LBMH_8KHf_N9CvLqhzU", // I will need this value for the clone's path
ins: {
// I am creating several "key: pair"s here, something like:
1525104151100: true,
1525104151183: true,
}
}
},
rooms: {
-LBMH_8KHf_N9CvLqhzU: {
ins: {
// I want the function to clone the same data here:
1525104151100: true,
1525104151183: true,
}
}
}
My cloud function is now like this:
exports.updateRoom = functions.database.ref('/doors/{MACaddress}/ins').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
const roomPushKey = change.before.ref.parent.child('/inRoom');
console.log(roomPushKey); // this is retrieving all the info about the ref "inRoom" but not its value...
Question 1) How can I get to this ref/node's value?
My code goes on by trying to get the value like this.
roomPushKey.once('child_added').then(function(dataSnapshot) {
let snapVal = dataSnapshot.val();
console.log(snapVal);
});
Question 2 (which I think is basically question 1 rephrased): How can I get the snapVal outside the then. method's scope?
return change.after.ref.parent.parent.parent.child('/rooms')
.child(snapVal).child('/ins').set(afterData);
// snapVal should go above
});
Error message: ReferenceError: snapVal is not defined
The following should work.
const admin = require("firebase-admin");
....
....
exports.updateRoom = functions.database.ref('/doors/{MACaddress}').onWrite((change, context) => {
const afterData = change.after.val(); // data after the write
const roomPushKey = afterData.inRoom;
const ins = afterData.ins;
const updates = {};
updates['/rooms/' + roomPushKey] = ins;
return admin.database().ref().update(updates);
}).catch(error => {
console.log(error);
//+ other rerror treatment if necessary
});
Here are some explanations:
You get the roomPushKey by reading the "data after the write" as an object: roomPushKey = afterData.inRoom. You don't need to do roomPushKey.once('child_added').then()
Once you have the roomPushKey, you create a new child node in the rooms node by using update() and creating an object with square brackets notation which allow you to assign the id of the node (i.e. roomPushKey).
Note that you could also do:
return admin.database().ref('/rooms/' + roomPushKey).set(ins);
Note also that you have to import firebase-admin in order to be able to do return admin.database().ref()...
Finally, I would suggest that you have a look at the three following videos from the Firebase team: youtube.com/watch?v=7IkUgCLr5oA&t=517s & youtube.com/watch?v=652XeeKNHSk&t=27s & youtube.com/watch?v=d9GrysWH1Lc. A must for anyone starting coding for Cloud Functions.

Categories

Resources