I am relatively new to Javascript and promises so I am not sure how to ensure that the code follows a certain order in this scenario.
Fundamentally, I need to retrieve the credentials from the database before setting them. How do I ensure that the retrieval code happens before the setting code - is there a way to use async and await to do this?
Code that needs to be completed first starts with **admin.firestore().collection('credentials').get().then(async (snapshot) => {**
Code that needs to come second is
**client_id: Credentials.client_id,
client_secret: Credentials.client_secret**
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`
});
admin.firestore().collection('credentials').get().then(async (snapshot) => {
await snapshot.docs.forEach(doc => {
console.log(JSON.stringify(doc.data().client_id));
// Credentials.client_id = JSON.stringify(doc.data().client_id);
console.log(JSON.stringify(doc.data().client_secret));
// Credentials.client_secret = JSON.stringify(doc.data().client_secret);
let client_id = JSON.stringify(doc.data().client_id);
let client_secret = JSON.stringify(doc.data().client_secret);
const regex = /(\w)+/g;
let m;
let n;
while ((m = regex.exec(client_id)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
Credentials.client_id = match;
console.log(`Found match, group ${groupIndex}: ${match}`);
});
}
while ((n = regex.exec(client_secret)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (n.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `n`-variable.
n.forEach((match, groupIndex) => {
Credentials.client_secret = match;
console.log(`Found match, group ${groupIndex}: ${match}`);
});
}
});
});
class Credentials {
constructor(client_id, client_secret) {
this.client_id = client_id;
console.log('Id in class ' + this.client_id);
this.client_secret = client_secret;
console.log('Secret in class ' + this.client_secret);
}
}
/**
* ----------------------Below section of code found at: LINK TO GIT REPOSTITORY---------------------------------
*/
// Spotify OAuth 2 setup
const SpotifyWebApi = require('spotify-web-api-node');
const Spotify = new SpotifyWebApi({
client_id: Credentials.client_id,
client_secret: Credentials.client_secret,
redirectUri: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`
});
I am getting either an [object Promise] or undefined when the program runs. But on the console logs, a few seconds after it logs the data that I need. Really stuck on this one - anyone know how to solve this? (code below is its current state which leads to the [object Promise] situation.
client_id: Promise.resolve(admin.firestore().collection('credentials').get().then(async (snapshot) => {
await snapshot.docs.forEach(doc => {
JSON.stringify(doc.data().client_id);})})),
client_secret: (admin.firestore().collection('credentials').get().then(async (snapshot) => {
await snapshot.docs.forEach(doc => {
JSON.stringify(doc.data().client_secret);})})),
You are already retrieving the the credentials first with this line
admin.firestore().collection('credentials').get()
This function returns a Promise
which you can either continue on with then
, as you already have, or use async/await
to retrieve the value from the function.
Use it as a Promise
with then
.
await admin.firestore().collection('credentials').get().then((snapshot => {
// Credentials have been retrieved.
// Continue here to use the credentials.
});
Or use it in a async/await
function.
async function getCredentials() {
const snapshot = await admin.firestore().collection('credentials').get();
return snapshot;
}
Both will return a Promise
but the choice is up to you.
Now there are a couple of things that have to be changed in your code, either being in the wrong position or using it in a faulty manner.
You've created a class
called Credentials. While the class definition is correct the instantiation is not. To create a new instance of a class
use the new
keyword. A class
is virtually a blueprint of an object which can be created at any point in the code with the new
keyword.
const client_id = 'yourclientid';
const client_secret = 'yourclientsecret';
const credentials = new Credentials(client_id, client_secret);
// {
// client_id: 'yourclientid',
// client_secret: 'yourclientsecret'
// }
Because admin.firestore().collection('credentials').get()
is an asynchronous function it will not stop the processing of the file. The rest of file will be executed without waiting for the retrieval of the credentials. But you want to do something with the credentials after they've been retrieved. You can do that with the then
method of the Promise
. then
is called whenever a Promise
has resolved
. Meaning that it is finished waiting and can continue.
So the Spotify
part has to be inside the then
method whenever the credentials have been retrieved.
admin.firestore().collection('credentials').get().then((snapshot) => {
// We are in the then method, so credentials have been retrieved.
snapshot.docs.forEach(doc => {
// Check the credentials...
// ...
// Create your credentials object.
const credentials = new Credentials(client_id, client_secret);
// Create new Spotify session.
const Spotify = new SpotifyWebApi({
client_id: credentials.client_id,
client_secret: credentials.client_secret,
redirectUri: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`
});
});
});
Not that it will matter a whole lot, but it is good practice to put your require
statements on the top of the page. This makes it clear for you, or any other that reads code, what files or dependencies are included in this file.
// Put me at the top of the page.
const SpotifyWebApi = require('spotify-web-api-node');
This is all what I can tell from looking through your code. You are on the right track but still need to read up more on how Promises
work and more importantly why and when do you use them.
Create the Spotify
session inside the then
method callback and use the new
keyword to create an instance of your Credentials
class.