How use dynamic routes to find people in my mongoose database with node.js - express

app.get('/admin/reservas/:param', function(req, res) {
var param = req.param("param");
console.log(param);
mongoose.model('Something').findOne(
{
id: param
}, function(err, obj) {
res.send(obj);
console.log(obj);
}
);
});
I have this route, and this EJS:
<% for(var i = 0; i < data.length; i++) { %>
<td> <a href= <%="/admin/reservas/" + data[i].id + ""%>><%= data[i].id %></a></td>
<td> <a href= <%="/admin/reservas/" + data[i].name + ""%>><%= data[i].name %></a></td>
<% } %>
And is fine, when i click in the id, i find what the id in the db that i want, but I want to be able to find by the name too.
So i was trying to change the routes:
app.get('/names/:param', function(req, res) {
var param = req.param("param");
console.log(param);
mongoose.model('Something').findOne(
{
id: param,
name: param
}, function(err, obj) {
res.send(obj);
console.log(obj);
}
);
});
but is returning anything, both the id and the name

MongoDB query arguments are by default always a logical and condition. In order to use a logical or there is the $or operator:
app.get('/admin/reservas/:param', function(req, res) {
var param = req.param("param");
console.log(param);
mongoose.model('Something').findOne(
{
"$or": [
{ id: param },
{ name: param }
]
}, function(err, obj) {
res.send(obj);
console.log(obj);
}
);
});
The $or operator takes an array of query documents to consider the conditions of. It is a "short circuit" match where the first condition to evaluate as true makes the statement true.
At least that would be true if not of a specific problem here. See _id and it's id alias is a special value to both MongoDB and Mongoose. By default this will try to "cast" to an ObjectID type but it cannot if the string supplied is invalid, such as "fred" for example.
This would cause an exception as the query arguments are supplied. So the bottom line rule is your cannot "mix types" in an $or condition in this way with something that would not survive the conversion from String. You would have to do it "logically" in a different way by testing the value
for what it is:
app.get('/admin/reservas/:param', function(req, res) {
var param = req.param("param");
console.log(param);
var query = {};
try {
var id = mongoose.mongo.ObjectID(param);
query = { "id": id };
} catch {
query = { "name": param };
}
mongoose.model('Something').findOne(
query, function(err, obj) {
res.send(obj);
console.log(obj);
}
);
});
That means that the query is now built in a way that mongoose will parse it considering the schema "types" to apply then there is no error since the decision of which field to query on was made elsewhere.
Alternately of course you can "bypass" the mongoose method behavior and just use the raw driver:
app.get('/admin/reservas/:param', function(req, res) {
var param = req.param("param");
console.log(param);
var id ="";
try {
id = mongoose.mongo.ObjectID(param);
}
mongoose.model('Something').collection.findOne(
{
"$or": [
{ id: id },
{ name: param }
]
}, function(err, obj) {
res.send(obj);
console.log(obj);
}
);
});
But of course you need to do the type conversion yourself and not only for "testing" otherwise even a correct "String" value representing an ObjectID would not work.

Related

Detox get length of element

Hi i'm using detox and i would like to know how can I get the number of matches to
one element(length).
For example "card" match three times, how can I get the three.
const z = await element(by.id("card"))
https://github.com/wix/Detox/blob/master/docs/APIRef.Expect.md
https://github.com/wix/Detox/blob/master/docs/APIRef.Matchers.md
They don't support it in the API /:
z output:
Element {
_invocationManager: InvocationManager {
executionHandler: Client {
isConnected: true,
configuration: [Object],
ws: [AsyncWebSocket],
slowInvocationStatusHandler: null,
slowInvocationTimeout: undefined,
successfulTestRun: true,
pandingAppCrash: undefined
}
},
matcher: Matcher { predicate: { type: 'id', value: 'card' } }
}
A workaround could be
async function getMatchesLength(elID) {
let index = 0;
try {
while (true) {
await expect(element(by.id(elID)).atIndex(index)).toExist();
index++;
}
} catch (error) {
console.log('find ', index, 'matches');
}
return index;
}
then you can use
const length = await getMatchesLength('card');
jestExpect(length).toBe(3);
Here is my solution in typescript:
async function elementCount(matcher: Detox.NativeMatcher) {
const attributes = await element(matcher).getAttributes();
// If the query matches multiple elements, the attributes of all matched elements is returned as an array of objects under the elements key.
https://wix.github.io/Detox/docs/api/actions-on-element/#getattributes
if ("elements" in attributes) {
return attributes.elements.length;
} else {
return 1;
}
}
Then you can use it like this:
const jestExpect = require("expect");
jestExpect(await elementCount(by.id("some-id"))).toBe(2);

Strapi graphql mutation Syntax Error: Unterminated string

I always get Syntax Error: Unterminated string when I try to update my database using javascript strapi sdk. this.chapter.content is a html string generated by ckeditor. How can I escape this string to update my database using graphql?
async updateChapter() {
const q = `
mutation {
updateChapter(input: {
where: {
id: "${this.$route.params.chapterId}"
},
data: {
content: "${this.chapter.content.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/(?:\r\n|\r|\n)/g, '\n')}"
title: "${this.chapter.title}"
}
}) {
chapter{
title
id
content
}
}
}
`;
const res = await strapi.request("post", "/graphql", {
data: {
query: q
}
});
this.chapter = res.data.chapter;
}
Technically you could use block string notation to get around this issue. However, you really should supply dynamic input values using variables instead of string interpolation. This way you can easily provide any of sort of values (strings, numbers, objects, etc.) and GraphQL will parse them accordingly -- including strings with line breaks.
const query = `
mutation MyMutation ($chapterId: ID!, $content: String!, $title: String!) {
updateChapter(input: {
where: {
id: $chapterId
},
data: {
content: $content
title: $title
}
}) {
chapter{
title
id
content
}
}
}
`
const variables = {
chapterId: '...',
content: '...',
title: '...',
}
const res = await strapi.request("post", "/graphql", {
data: {
query,
variables,
},
})
Note that $chapterId may need to be of the type String! instead if that's what's called for in the schema. Since variables can also be input object types, instead of providing 3 different variables, you could also provide a single variable to be passed to the input argument instead:
const query = `
mutation MyMutation ($input: SomeInputObjectTypeHere!) {
updateChapter(input: $input) {
chapter{
title
id
content
}
}
}
`
const variables = {
input: {
where: {
id: '...',
},
data: {
content: '...',
title: '...',
},
},
}
Again, just replace SomeInputObjectTypeHere with the appropriate type in your schema.
Another solution maybe help
Code with issue: For example mainReason and actionTaken fields are text inputs and data contains some white spaces. This action give error: Unterminated string
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason: "${ticketData.mainReason}"
actionTaken: "${ticketData.actionTaken}"
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}
Fix this problem with JSON.stringify method
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason:${JSON.stringify(ticketData.mainReason)}
actionTaken:${JSON.stringify(ticketData.actionTaken)}
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}

Vue.js | Filters is not return

I have a problem.
I am posting a category id with http post. status is returning a data that is true. I want to return with the value count variable from the back. But count does not go back. Return in function does not work. the value in the function does not return from the outside.
category-index -> View
<td>{{category.id | count}}</td>
Controller File
/**
* #Access(admin=true)
* #Route(methods="POST")
* #Request({"id": "integer"}, csrf=true)
*/
public function countAction($id){
return ['status' => 'yes'];
}
Vue File
filters: {
count: function(data){
var count = '';
this.$http.post('/admin/api/dpnblog/category/count' , {id:data} , function(success){
count = success.status;
}).catch(function(error){
console.log('error')
})
return count;
}
}
But not working :(
Thank you guys.
Note: Since you're using <td> it implies that you have a whole table of these; you might want to consider getting them all at once to reduce the amount of back-end calls.
Filters are meant for simple in-place string modifications like formatting etc.
Consider using a method to fetch this instead.
template
<td>{{ categoryCount }}</td>
script
data() {
return {
categoryCount: ''
}
},
created() {
this.categoryCount = this.fetchCategoryCount()
},
methods: {
async fetchCategoryCount() {
try {
const response = await this.$http.post('/admin/api/dpnblog/category/count', {id: this.category.id})
return response.status;
} catch(error) {
console.error('error')
}
}
}
view
<td>{{count}}</td>
vue
data() {
return {
count: '',
}
},
mounted() {
// or in any other Controller, and set your id this function
this.countFunc()
},
methods: {
countFunc: function(data) {
this.$http
.post('/admin/api/dpnblog/category/count', { id: data }, function(
success,
) {
// update view
this.count = success.status
})
.catch(function(error) {
console.log('error')
})
},
},

How to chain get and map the result with lodash?

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!
I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

How do I operate the m.withAttr tutorials code?

A contrived example of bi-directional data binding
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
m.render("body", [
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
]);
}
};
https://lhorie.github.io/mithril/mithril.withAttr.html
I tried the above code does not work nothing.
It was the first to try to append the following.
m.mount(document.body, user);
Uncaught SyntaxError: Unexpected token n
Then I tried to append the following.
var users = m.prop([]);
var error = m.prop("");
m.request({method: "GET", url: "/users/index.php"})
.then(users, error);
▼/users/index.php
<?php
echo '[{name: "John"}, {name: "Mary"}]';
Uncaught SyntaxError: Unexpected token n
How do I operate the m.withAttr tutorials code?
Try returning m('body', [...]) from your controller.
view: function (ctrl) {
return m("body", [
...
]);
}
render should not be used inside of Mithril components (render is only used to mount Mithril components on existing DOM nodes).
The example is difficult to operate because it's contrived, it's not meant to be working out-of-the-box. Here's a slightly modified, working version:
http://jsfiddle.net/ciscoheat/8dwenn02/2/
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
return [
m("input", {
oninput: m.withAttr("value", controller.user.name),
value: controller.user.name()
}),
m("h1", controller.user.name())
];
}
};
m.mount(document.body, user);
Changes made:
m.mount injects html inside the element specified as first parameter, so rendering a body element in view will make a body inside a body.
Changed the input field event to oninput for instant feedback, and added a h1 to display the model, so you can see it changing when the input field changes.
Using m.request
Another example how to make an ajax request that displays the retrieved data, as per your modifications:
http://jsfiddle.net/ciscoheat/3senfh9c/
var userList = {
controller: function() {
var users = m.prop([]);
var error = m.prop("");
m.request({
method: "GET",
url: "http://jsonplaceholder.typicode.com/users",
}).then(users, error);
return { users: users, error: error };
},
view: function(controller) {
return [
controller.users().map(function(u) {
return m("div", u.name)
}),
controller.error() ? m(".error", {style: "color:red"}, "Error: " + controller.error()) : null
];
}
};
m.mount(document.body, userList);
The Unexpected token n error can happen if the requested url doesn't return valid JSON, so you need to fix the JSON data in /users/index.php to make it work with your own code. There are no quotes around the name field.