Handle Google API Response - javascript

I want to get the comments from a YouTube video and do some further work with them. I started with the implementation example from the docs for "CommentThreads: list" and continued from there on.
Using a main function I want to call the execute function and use the response it should return. It should look kinda like the following:
function execute() {
return gapi.client.youtube.commentThreads.list({
"part": [
"snippet"
],
"fields":[
"items/snippet/topLevelComment/snippet(authorDisplayName,authorProfileImageUrl,publishedAt,textDisplay)"
],
"videoId": "XXXXX"
}).then(function(response) {
console.log(response.result);
},
function(err) {
console.error("Execute error", err);
});
}
function main(){
let response = execute();
console.log(response);
}
And the output in the console looks like this:
{
"Ca": 2,
"yb": null,
"Yj": null,
"Sn": null,
"KB": false,
"xw": false
}
Another way I tried to solve this is to not return gapi.client.youtube.commentThreads.list but the response in the successful promise function like so:
function execute() {
gapi.client.youtube.commentThreads.list({
"part": [
"snippet"
],
"fields":[
"items/snippet/topLevelComment/snippet(authorDisplayName,authorProfileImageUrl,publishedAt,textDisplay)"
],
"videoId": "XXXXX"
}).then(function(response) {
console.log(response.result);
return reponse.result;
},
function(err) {
console.error("Execute error", err);
});
}
But here I only get an undefined.
I am just learning JavaScript and the use of the API.
EDIT:
I should also add that the console.log(response.result); in the execute function does print the information I want. But as soon as I return it and want to use it in the main() function it changes.

The problem seems to that when using the gapi that it returns a promise that I want to return. Promised are not evaluated immediately which means that it continues to execute the following functions, returning a value that has no response yet.
I solved the issue by using the async/await functionality to work with promises, which seemed to be more understandable and elegant in my eyes. I also changed some variables to constants as they did not need to be non-constants. My result looks like the following:
await function execute() {
let response = await gapi.client.youtube.commentThreads.list({
"part": [
"snippet"
],
"fields":[
"items/snippet/topLevelComment/snippet(authorDisplayName,authorProfileImageUrl,publishedAt,textDisplay)"
],
"videoId": "XXXXX"
});
return response.result;
}
async function main(){
let response = await execute();
console.log(response);
}
The reason why we also have to use async and await in the main() function is that by definition async functions always return promises.
To learn more about async/await I found this article whose explanation I liked: https://javascript.info/async-await
They also have an article about promises: https://javascript.info/promise-basics

Related

Resolve promise inside javascript map function

I have gone through several questions and posts regarding how to accomplish this but none have worked. I have tried an array (no pun intended) of possible solutions involving
Promise.*, thenable, await and none have worked.
I have an array of payments that I extract from a Prisma db call, the object looks like this:
[{
"id": 1,
"amount": 100,
"payment_data": [{
"credits": 2,
"subscription_id": 534
}, {
"credits": 3,
"subscription_id": 469
}],
"customerId": 14
}]
I am trying to run another database query using the subscription_id under each payment_data for which I built a simple function:
const get_subscription = async function (subscription_id) {
return await db.subscriptions.findUnique({
where: {
id: Number(subscription_id)
}
})
}
And then I execute it inside a .map like this:
const mapped_payments = payments.map((payment) => ({
...payment,
payment_data: payment.payment_data.map(function (data) {
data.subscription = get_subscription(data.subscription_id).then((resp => resp))
return data
})
}))
The problem is no matter what I have tried this never resolves.
If I console log mapped_payments the subscription object shows as Promise {<pending>} and entirely empty when I return the response in express.
I am sure this is a simple solution but I have moved all possible solutions (Promise.*, thenable, await) into the function, into the .map and into the res.send but none work.
Any help is appreciated!
You need to use async map callback to await the get_subscription function, Also it would be better to use Promise.all to handle all mapped promises.
const mapped_payments = await Promise.all(payments.map(async (payment) => ({
...payment,
payment_data: await Promise.all(payment.payment_data.map(async function (data) {
data.subscription = await get_subscription(data.subscription_id)
return data
}))
})))

Call a js function() inside a pipeline using $function in mongoose/MongoDB

I need to call a function declared outside my pipeline to test some data in the query.
This is my code so far:
let requestMatcher = {
$expr: {
$function: {
body: function (modelType) {
testFunction()
return name === 'chiller'
},
args: ['$name'],
lang: 'js'
}
}
}
let pipeline = [{ $match: requestMatcher }, { $limit: 10 }]
const data = await Model.aggregate(pipeline)
const total = await Model.countDocuments(requestMatcher)
return res.status(200).send({
body: {
data,
total
}
})
testFunction() is just a console.log nothing fancy.
Aside from that testFunction(), everything else works in my pipeline.
When this code is run, I get "testFunction is not defined" which I know it's not defined inside mongo, but I would like to use some functions and variables to validate data while being queried
Am I doing something wrong here? Or $function isn't supposed to work like that?
Is there another way that I can achieve something similar with mongoose?

Define response structure in Adonisjs with Middleware

I want to define the response structure of my requests in the simplest way, and the first thing that comes in my mind to do this is a middleware.
My endpoints are returning the response content correctly:
{{base_url}}/users returns a list of users:
{
[
{
"id": 44,
"name": "some name"
[...]
}
]
}
What I want to do (in all requests) is to add the fields status and data (or any other I'd like to add), like this:
{
"status": 200,
"data": [
{
"id": 44,
"name": "some name"
[...]
}
]
}
I've created a middleware that waits for the resolution but I'm not able to get the content nor add some property to it.
[...]
async handle ({request, response}, next) {
await next()
const content = response._lazyBody.content
content.status = response.response.statusCode
}
[...]
I know this will not work but I want something similar to this. I've looked in Adonis docs and forum, but no answers fit to my needs.
Any help will be welcome
You can extend Response By extending the core. The simplest way is to create a file inside start folder and name it hooks.js and copy and paste the content below inside it:
const { hooks } = use('#adonisjs/ignitor')
const Response = use('Adonis/Src/Response')
hooks.after.providersBooted(() => {
Response.macro('customJson', function (status, data) {
this.status(status).json({
status,
data
})
})
})
this piece of code extends the Response module and add customJson method to it which takes two arguments, status and data, and send them back to the client.
And here you can see how to use it:
Route.get('/users', async ({ response }) => {
let status = ''// whatever you want
let data = ''// whatever you want
return response.customJson(status, data)
})

Javascript async function console log the returned data

How can I console log - or do any thing with the data returned from inside an async function?
example:
JS FILE:
async function getData(){
try {
$.getJSON('./data.json', (data) => {
return data;
});
} catch(error) {
console.log("error" + error);
} finally {
console.log('done');
}
}
console.log(getData());
JSON FILE:
{
"stuff": {
"First": {
"FirstA": {
"year": [2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017],
"Categories": ["Suspension", "Electrical", "Performance", "Motor"]
},
"FirstB": {
"year": [2007, 2008, 2009, 2010, 2011, 2012],
"Categories": ["Suspension", "Electrical", "Performance", "Motor"]
}
},
"Second": {
"SecondA": {
"year": [2002, 2003, 2004, 2005, 2006],
"Categories": ["Suspension", "Electrical", "Performance", "Motor"]
},
"SecondB": {
"year": [2007, 2008, 2009, 2010, 2011, 2012],
"Categories": ["Suspension", "Electrical", "Performance", "Motor"]
}
}
}
}
How can I return / get access to all the information in the JSON file and work with it. for example I'd like to take the "First" and "Second" and add them to a div. Same with "FirstA" and "FirstB" and "SecondA" and "SecondB"...and so on.
Right now as it stands, I get Promise {: undefined}
Any help would be greatly appreciated.
---------UPDATE---------
If I run the console log inside the function I then can see the json data, but I need access to the data outside the function.
Serge
Two issues:
To set the resolution value of the promise created by the async function, you have to use a return statement from the async function itself. Your code has a return in the getJSON callback (which is ignored), not the async function itself.
To get the resolution value of an async function, you must await it (or consume its promise in the older way, via then, etc.).
For #1, you could return the result of awaiting getJSON:
async function getData() {
try {
return await $.getJSON('./data.json').promise();
}
catch (error) {
console.log("error" + error);
}
finally {
console.log('done');
}
}
And for #2, you'd need to either await your function (this would, in turn, need to be inside an asyncfunction):
console.log(await getData());
...or consume its promise via then:
getData().then(data => {
console.log(data);
});
Side note: Your getData hides errors, turning them into resolutions with the value undefined, which is generally not a good idea. Instead, ensure that it propagates the error:
catch (error) {
console.log("error" + error);
throw error;
}
Then, naturally, ensure that anything using getData either handles or propagates the error, making sure something somewhere does handle it (otherwise, you get an "unhandled rejection" error).
Re your comment
how would I access the "stuff" in the json file from the log outside the function?
The async result / resolution value of getData is the object the JSON defined (it's no longer JSON, it's been parsed). So you'd use .stuff on it, e.g.:
// In an `async` function
console.log((await getData()).stuff);
// Or using `then`:
getData().then(data => {
console.log(data.stuff);
});
I use following way. Create a async function like following:
private consoleLogAsync = async (message: string, other?: any) => {
await new Promise((resolve) => setTimeout(resolve, 0)).then(() => console.info(message, other));
};
Use it in your async function where you want to log e.g.:
await this.consoleLogAsync('myMessage', myObject);

Mocha 'before each hook' error message

I have the following message, just before a failing test
Below is my piece of code
before(function(done) {
function afterListening() {
customFormats(ZSchema);
done();
}
if (app.server.listening) return afterListening();
app.on('listening', afterListening);
});
describe('/a1/{exampleID}', function() {
describe('get', function() {
it('should respond with 200 Return an array of shelter...', function(done) {
/*eslint-disable*/
var schema = {
"type": [
"object",
"null"
],
"properties": {
"meta": {
"type": [
"object",
"null"
],
"properties": {
"resource": {
"type": [
"string",
"null"
],
"description": "fully qualified URL call"
},
"version": {
"type": [
"string",
"null"
],
"description": "version of API being called"
},
"response": {
"type": [
"integer",
"null"
],
"format": "int32",
"description": "status code"
},
"required": [
"version",
"resource"
]
}
}
};
/*eslint-enable*/
api.get('/a1/example/64442')
.set('Content-Type', 'application/json')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
validator.validate(res.body, schema).should.be.true;
done();
});
});
});
});
Error
1) example "before each" hook:
Error: timeout of 200ms exceeded. Ensure the done() callback is being
called in this test.
at null. (lib/runnable.js:170:19)
This error came only when I run the test case on my machine.If I run the same test case in another machine the test case is passing. why it happens.It's weird to see like this.
Any help, please!!
You generally receive timeout error, when you are not returning a promise as expected.
Done is the older implementation of the same, but no longer required. If you are using a recent version of Mocha, you should be able to just return the promise instead of calling Done().
Here are details on mocha-as-promised - which was integrated with default Mocha, since version 1.18.0.
Example of how you should return a promise:
it("should be fulfilled with 5", function () {
return promise.should.become(5);
});
In case of beforeeach - implementation should look like this:
beforeEach(() => {
const {col1, comments} = mongoose.connection.collections;
return col1.drop(() => {
// Do something after collection is dropped
});
});
Notice return for the action taken within beforeeach.
Can you please share your beforeEach with us so we can help?
Without looking, it might be because your connection to whatever thing you're connecting to isn't responding on time or isn't responding at all.
Might wanna check your connection to places where your unit tests are connecting (chances are it's the database, because it's in beforeEach() and beforeEach() doesn't require a done() to be called inside it.
Increase your timeout to something like 10 seconds or such. If you don't get database errors then, your connection is slow. If you still get timeout errors even at 10 seconds, you might have no connection at all.

Categories