I am new to Adonis JS so extremely sorry for the Stupid Question.
I have the default setup of Adonis JS with Mysql Database and everything working.
I have created a simple usertest route where I am returning JSON of user with ID: 1.
Below is the code for the same
Route.get('/usertest', ({ response }) => {
const User = use('App/Models/User')
let data = User.query().where('id', 1)
.first()
console.log(data)
return response.status(200).json(data)
})
But it is returning and empty object
Raw Response:
{}
Console Log Statement Response:
Promise { <pending> }
I am unable to understand what am I missing here.
Note: I tried let data = User.find(1) but it did not work.
Please help me.
Thanks in advance!!!
Quick note, at least you have to execute the query asynchronously.
I mean, you have to replace:
let data = User.query().where('id', 1)
.first()
by:
let data = await User.query().where('id', 1)
.first()
Of course, this mean you have to precede the function arrow with async:
Route.get('/usertest', async ({ response }) => {
// rest of the code
let data = await User.query().where('id', 1).first()
// rest of the code
})
It is very easy to miss "await" when you start working with async-await frameworks. Just a suggestion on the query whenever you want to find one object it is good practice to use ORM's predefined function. In this case
const user = await User.findBy('id',1)
this will return the first object it finds with the given id. You will find more options and help in knex.js docs http://knexjs.org/#Builder-where
Related
I'm new around here and I'm studying JS! In particular JSON! However, I have come across an exercise that I cannot solve, also because I do not understand what I am doing wrong. I need to extract the information about the planets from the StarWars API. So I do the classic fetch and as a result I get the generic information about the planet in the form of a JSON.
However, I have to extract the planet name and I get stuck, because when I check the PlanetsData variable, it gives me undefined. Ergo the cycle I wrote to extract the names of the planets doesn't work for some reason.
So, my question is:
Why do I get "undefined" for the PlanetsData variable? .. Shouldn't I get the JSON, which displays correctly in the console?
Did I write the cycle correctly?
Thanks to who will answer me!
This is my code:
async function getPlanetsData() {
const planetsData = await fetch ("https://swapi.dev/api/planets").then(data => {
return data.json()}).then(planets => {console.log(planets.results)}) // ---> Here i receive the JSON data
for (let key in planetsData) {
const someInfo = planetsData.results[key].name
console.log(JSON.stringify(someInfo)) } // ---> I don't understand why, but I don't get anything here. There is no response in the console, as if the call did not exist
}
getPlanetsData()
You can write the same function in a different and clearer way,
check the comments to understand the code!
async function getPlanetsData() {
// 1. Fetch and wait for the response
const response = await fetch ("https://swapi.dev/api/planets");
// 2. Handle the response, in that case we return a JSON
// the variable planetsData now have the whole response
const planetsData = await response.json();
// console.log(planetsData); // this will print the whole object
// 3. Return the actual data to the callback
return planetsData;
}
// Function usage
// 4. Call "getPlantesData" function, when it completes we can call ".then()" handler with the "planetsData" that contains your information
getPlanetsData().then(planetsData => {
// 5. Do whatever you want with your JSON object
// in that case I choose to print every planet name
var results = planetsData.results; // result array of the object
results.forEach(result => console.log(result.name));
});
It seems that you have the same issue as : read and save file content into a global variable
Tell us if it does solve your issue or not.
(UPDATE)
To answer explicitly to your questions.
First question:
To get value into variable planetsData you can do this:
async function getPlanetsData() {
const response = await fetch ("https://swapi.dev/api/planets")
const planetsData = await response.json()
for (let key in planetsData) {
const someInfo = planetsData.results[key].name
console.log(JSON.stringify(someInfo))
}
}
getPlanetsData()
Second question:
You didn't write the cycle correctly.
To resolve promises it is preferable to choose between using await and.
let me quickly get to the point. I have a function which fetches some user ids. It works very well.
const allIds = await fetchAllIds(someUrl);
Now the problem comes when I want to do something different. I'd like to loop over those ids and do some async await stuff with them. That is, to mainly fetch some data for each one with axios and modify them accordingly.
allIds.map(async (id) => {
// 1. Fetch some data (try, catch and await)
// 2. Modify the id based on that data
// 3. Return the id, namely replace the old one
});
At the end of my code, I simply return allIds. The problem is that it returns them without waiting for the map function to execute completely. I tried different methods and none of it seems to be working. Could you please help me to make it work or perhaps suggest some other possible solutions? Thanks in advance!
You basically have two problems:
You are ignoring the return value of the map
The map will return an array of Promises and you aren't awaiting them all
So:
const promises = allIds.map(...);
const replacement_ids = await Promise.all(promises);
return replacement_ids;
Use this instead.
const newList = await Promise.all(allIds.map(id=>new Promise(async(res)=>{
// do your async here and get result;
res(result);
})));
On the API(GraphQL) - Getting Started documentation here, it says to query your data using the following:
import { API } from 'aws-amplify';
import * as queries from './graphql/queries';
// Simple query
const allTodos = await API.graphql({ query: queries.listTodos });
console.log(allTodos); // result: { "data": { "listTodos": { "items": [/* ..... */] } } }
However, when I try to apply their code to my javascript code it says that it does not recognize the word await. Online it says I can only use the await keyword inside of a async function. When I take the await keyword out, the promise from the query function does not get settled so it returns the promise first before the data.
I tried setting up an async function before, and posted a stackoverflow post about it. The solution got a little messy, and did not quite work for me. So, I am wondering what is the best way to go about querying data using Graphql? And how do I implement that?
await can be used only with in an async context, so ideally what you would have to do is the following
const allTodos = async () => {
const todos = await API.graphql({ query: queries.listTodos });
return todos
}
here is what I am trying to do with firebase cloud function:
-Listen to any change in one of the documents under 'user' collection.
-Update carbon copies of the userinfo in the relevant documents in both 'comment' and 'post' collections.
Because I will need to query in relevant documents and update them at once, I am writing codes for transaction operations.
Here is the code that I wrote. It returns the error message, 'Function returned undefined, expected Promise or value'.
exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change,context) => {
const olduserinfo=change.before.data();
const newuserinfo=change.after.data();
db.runTransaction(t=>{
return t.get(db.collection('comment').where('userinfo','==',olduserinfo))
.then((querysnapshot)=>{
querysnapshot.forEach((doc)=>{
doc.ref.update({userinfo:newuserinfo})
})
})
})
.then(()=>{
db.runTransaction(t=>{
return t.get(db.collection('post').where('userinfo','==',olduserinfo))
.then((querysnapshot)=>{
querysnapshot.forEach((doc)=>{
doc.ref.update({userinfo:newuserinfo})
})
})
})
})
});
I am a bit confused because as far as I know, 'update' method returns a promise? I might be missing something big but I picked up programming only last November, so don't be too harsh. :)
Any advice on how to fix this issue? Thanks!
EDIT:
Building on Renaud's excellent answer, I created the below code in case someone may need it.
The complication with transaction is that the same data may be stored under different indices or in different formats. e.g. The same 'map' variable can be stored under an index in one collection, and as part of an array in another. In this case, each document returned by querying needs different update methods.
I resolved this issue using doc.ref.path, split, and switch methods. This enables application of different update methods based on the collection name. In a nutshell, something like this:
return db.runTransaction(t => {
return t.getAll(...refs)
.then(docs => {
docs.forEach(doc => {
switch (doc.ref.path.split('/')[0]) { //This returns the collection name and switch method assigns a relevant operation to be done.
case 'A':
t = t.update(doc.ref, **do whatever is needed for this collection**)
break;
case 'B':
t = t.update(doc.ref, **do whatever is needed for this collection**)
break;
default:
t = t.update(doc.ref, **do whatever is needed for this collection**)
}
})
})
})
Hope this helps!
Preamble: This is a very interesting use case!!
The problem identified by the error message comes from the fact that you don't return the Promise returned by the runTransaction() method. However there are several other problems in your code.
With the Node.js Server SDK you can indeed pass a query to the transaction's get() method (you cannot with the JavaScript SDK). However, in your case you want to update the documents returned by two queries. You cannot call twice db.runTransaction() because, then, it is not a unique transaction anymore.
So you need to use the getAll() method by passing an unpacked array of DocumentReferences. (Again, note that this getAll() method is only available in the Node.js Server SDK and not in the JavaScript SDK).
The following code will do the trick.
We run the two queries and transform the result in one array of DocumentReferences. Then we call the runTransaction() method and use the spread operator to unpack the array of DocumentReferences and pass it to the getAll() method.
Then we loop over the docs and we chain the calls to the transaction's update() method, since it returns the transaction.
However note that, with this approach, if the results of one of the two original queries change during the transaction, any new or removed documents will not be seen by the transaction.
exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change, context) => {
const olduserinfo = change.before.data();
const newuserinfo = change.after.data();
const db = admin.firestore();
const q1 = db.collection('comment').where('userinfo', '==', olduserinfo); // See the remark below: you probably need to use a document field here (e.g. olduserinfo.userinfo)
const q2 = db.collection('post').where('userinfo', '==', olduserinfo);
return Promise.all([q1.get(), q2.get()])
.then(results => {
refs = [];
results.forEach(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
refs.push(documentSnapshot.ref);
})
});
return db.runTransaction(t => {
return t.getAll(...refs)
.then(docs => {
docs.forEach(doc => {
t = t.update(doc.ref, { userinfo: newuserinfo })
})
})
})
})
});
Two last remarks:
I am not sure that db.collection('comment').where('userinfo', '==', olduserinfo); will be valid as olduserinfo is obtained through change.before.data(). You probably need to specify one field. This is probably the same for newuserinfo.
Note that you cannot do doc.ref.update() in a transaction, you need to call the transaction's update() method, not the one of a DocumentReference.
I'm struggling to get the actual text of the text box as I need it as a text to store it in a variable rather than comparing it against a value, because I need to add it to the end of the url to call another page.
I tried using the code suggested by ebeal but it didn't do what I want:
var access_token = driver.findElement(webdriver.By.name("AccToken"))
.getAttribute("value")
.then(console.log);
// This outputs the right result but only to the console as I can't save it to a variable
var access_token = driver.findElement(webdriver.By.name("AccToken"))
.getText();
access_token = access_token.then(function(value){
console.log(value);
});
console.log("the new one : " + access_token);
// this one outputs : the new one: Promise::304 {[[PromiseStatus]]: "pending"}
Any idea?
WebdriverJS is purely asynchronous. Meaning, you need to provide a callback and instantiate your variable inside the callback rather than simply assigning the call the results of the function to your variable.
That's why you will always get a promise everytime you console.log your access_token variable. The webdriverjs docs explain a little about how promises work in selenium-webdriver https://code.google.com/p/selenium/wiki/WebDriverJs#Understanding_the_API
You can do the following to assign the text to a variable:
var access_token;
var promise = driver.findElement(webdriver.By.name("AccToken")).getText();
promise.then(function(text) {
access_token = text;
});
I highly recommend WebdriverIO as it takes away from the pain of having to write your own promises. http://webdriver.io/
So this is one thing that I have had to learn the hard way, so I hope this helps:
var access_token = await driver.findElement(webdriver.By.name("AccToken"))
.getAttribute("value")
.then((value) => { return value; });
I'm not sure which version of Webdriver you are using, but you may have some luck using WebdriverIO. Specifically its getText() function which will return a callback with the text so you can use it elsewhere.
http://webdriver.io/api/property/getText.html
client.getText('#elem').then(function(text) {
console.log(text);
});
This should work just fine if you are looking to just get the value. If you are using the new await ES6 syntax no need to "then" the promise.
const { Builder, By, Key, until } = require('selenium-webdriver');
const assert = require('assert');
let access_token = await driver.findElement(By.name("AccToken")).getAttribute("value");
Then you can even just assert:
assert.equal(access_token, "your token value here");
More information can be found at the documentation for selenium-webdriver. Take a look at the Webdriver instance methods for a closer look. Good luck!