I'm going to insert the whole module in case you need to see other aspects of the code. The call in question is the addTracks method. The project is to allow the person to search the spotify library, create a playlist of songs, then add the playlist to their account. Everything works fine, besides the tracks actually saving to the account, I get a 401 error on the API, but both Chrome and FireFox also label it as a GET call, instead of as a POST. The error is an authentication error, but I should be authorized correctly, the only odd thing for authorization is the scope, which is taken care of in the redirect in getAccessToken(). What am I missing here? In case you need it: Spotify add track documentation
let accessToken;
let expiresIn;
const clientId = '86f8f621d81a4ce18bd21da9fd2da2b1';
const redirectURI = 'http://localhost:3000/';
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
} else if (window.location.href.match(/access_token=([^&]*)/) != null) {
accessToken = window.location.href.match(/access_token=([^&]*)/)[1];
expiresIn = window.location.href.match(/expires_in=([^&]*)/)[1];
window.setTimeout(() => accessToken = '', expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
} else {
window.location = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectURI}`;
}
},
async search(term) {
if (accessToken === undefined) {
this.getAccessToken();
}
try {
let response = await fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`
}
});
if (response.ok) {
let jsonResponse = await response.json();
let tracks = jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
return tracks;
}
} catch (error) {
console.log(error);
}
},
async savePlaylist(name, trackURIs) {
if (accessToken === undefined) {
this.getAccessToken();
}
if (name === undefined || trackURIs === undefined) {
return;
} else {
let userId = await this.findUserId();
let playlistID;
fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": 'application/json'
},
body: JSON.stringify({
name: name
})
}).then(response => {
return response.json()
}).then(playlist => {
playlistID = playlist.id;
this.addTracks(playlistID, trackURIs, userId);
});
}
},
addTracks(playlistID, trackURIs, userId) {
console.log(trackURIs);
fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playlistID}/tracks`), {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": 'application/json'
},
body: JSON.stringify({
uris: trackURIs
})
}
},
findUserId() {
if (accessToken === undefined) {
this.getAccessToken();
}
let userId;
return fetch(`https://api.spotify.com/v1/me`, {
headers: {
Authorization: `Bearer ${accessToken}`
}
}).then(response => {
return response.json()
}).then(jsonResponse => {
userId = jsonResponse.id;
return userId;
});
}
};
export default Spotify;
I'm beginner but probably you should check bracket in fetch() method in addTracks()
addTracks(playlistID, trackURIs, userId) {
console.log(trackURIs);
fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playlistID}/tracks`->)<-, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": 'application/json'
},
body: JSON.stringify({
uris: trackURIs
})
}
},
correct
addTracks(playlistID, trackURIs, userId) {
console.log(trackURIs);
fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playlistID}/tracks`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": 'application/json'
},
body: JSON.stringify({
uris: trackURIs
})
})
},
Related
Minimum reproducible code:
index.ts:
import * as admin from "firebase-admin"
import fetch, { Headers } from "node-fetch";
interface BarPayload {
topic: string,
token: string,
}
exports.bar = functions.https.onCall(async (data, context) => {
if (data != null) {
const payload: BarPayload = {
topic: data.topic,
token: data.token,
}
const url = `https://${location}-${project}.cloudfunctions.net/subscribeToTopic`
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
topic: payload.topic,
token: payload.token,
}),
})
}
return null;
});
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
const payload = req.body as BarPayload;
fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
res.sendStatus(299)
}
}).catch(error => {
console.error(error);
res.sendStatus(299)
})
return Promise.resolve();
})
I'm running bar in Flutter and I see the timeout error in Logs Explorer:
textPayload: "Function execution took 60051 ms. Finished with status: timeout"
But if I change my subscribeToTopic from HTTP function to a callable function, then it works fine. For example:
exports.subscribeToTopic = functions.https.onCall(async (data, context) => {
fetch('https://iid.googleapis.com/iid/v1/' + data.token + '/rel/topics/' + data.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
}).then(response => {
if (response.status < 200 || response.status >= 400) {
console.log('Error = ' + response.error);
}
}).catch(error => {
console.error(error);
})
return null;
});
(I know I'm making some trivial mistake, and I'm new to Typescript. Any help would be appreciated :)
You should not do return Promise.resolve(); in the HTTPS Cloud Function:
HTTPS Cloud Functions shall be terminated with with send(), redirect() or end();
return Promise.resolve(); is executed before the asynchronous call to fetch is complete.
The following should do the trick (untested):
export const subscribeToTopic = functions.https.onRequest(async (req, res) => {
try {
const payload = req.body as BarPayload;
const response = await fetch('https://iid.googleapis.com/iid/v1/' + payload.token + '/rel/topics/' + payload.topic, {
method: 'POST',
headers: new Headers({
'Authorization': 'key=AA...Wp9',
'Content-Type': 'application/json'
})
});
if(response.status < 200 || response.status >= 400) {
res.status(299).send();
}
} catch (error) {
res.status(400).send();
}
})
However I don't understand why you separate your business logic in two Cloud Functions. Why don't you directly fetch https://iid.googleapis.com within the bar Callable Cloud Function?
I am trying to call removeCouponCode method which exist in same file but execution says its not defined i am not sure what is missing here.
any thoughts ?
Below is the file which i am editing & trying to make changes.
not sure what is missing.
please have a look at let me know what is missing
import React, { useState, useEffect, useMemo } from 'react';
import SessionContext from 'react-storefront/session/SessionContext';
import PropTypes from 'prop-types';
import fetch from 'react-storefront/fetch';
import get from 'lodash/get';
import { EventTracking, AnalyticsErrors } from '../analytics/Events';
import Cookies from 'js-cookie';
import constants from '../constants/configs';
import errorHandler from '../constants/apiHelpers/errorHandler';
import Helper from '../constants/helper';
import configs from '../constants/configs';
const { session, actions } = useContext(SessionContext);
const initialState = {
signedIn: false,
cart: {
items: [],
shipping_methods: [],
shippingCountry: null,
shippingMethodCode: null,
freeShipingMethod: null,
freeShippingSelected: false
},
customer: {
store_credit: {},
wishlist: { items_count: 0, items: [] },
offline: true
},
errMsg: ''
};
const initialStatusState = {
cartLoadingStatus: false,
setShippingMethodStatus: false
};
async redemptionCode({ couponCode, ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/redemptionCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ couponCode, ...otherParams })
});
const result = await response.json();
if (response.ok) {
if(get(result, 'cart.prices.discounts', {}) === null){
removeCouponCode(...otherParams); //says undefined removeCouponCode here
}
}
},
async removeCouponCode({ ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/removeCouponCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ ...otherParams })
});
},
You didn’t reference your function and reference your function and try or you can either modify your code as follows by declaring it as a function,
async function removeCouponCode({ ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/removeCouponCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ ...otherParams })
});
}
async function redemptionCode({ couponCode, ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/redemptionCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ couponCode, ...otherParams })
});
const result = await response.json();
if (response.ok) {
if(get(result, 'cart.prices.discounts', {}) === null){
await removeCouponCode(...otherParams);
}
}
}
You did not declare them as function. So you define them either this way
async function redemptionCode({ couponCode, ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/redemptionCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ couponCode, ...otherParams })
});
const result = await response.json();
if (response.ok) {
if(get(result, 'cart.prices.discounts', {}) === null){
removeCouponCode(...otherParams); //says undefined removeCouponCode here
}
}
},
async function removeCouponCode({ ...otherParams }) {
let tempSession = { ...session };
const response = await fetch('/api/cart/removeCouponCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ ...otherParams })
});
},
Or this way
const redemptionCode = async ({ couponCode, ...otherParams }) => {
let tempSession = { ...session };
const response = await fetch('/api/cart/redemptionCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ couponCode, ...otherParams })
});
const result = await response.json();
if (response.ok) {
if(get(result, 'cart.prices.discounts', {}) === null){
removeCouponCode(...otherParams); //says undefined removeCouponCode here
}
}
},
const removeCouponCode = async ({ ...otherParams }) => {
let tempSession = { ...session };
const response = await fetch('/api/cart/removeCouponCode', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ ...otherParams })
});
},
Both should work!
It seems that you define this function after calling it. I think you should first define it and then call it..and you can use the arrow function.
I know that method: DELETE, changes the isActive from true to false.
But how can I revert it back from false to true?
Here's my code to revert it, but I don't know what it lacks:
let params = new URLSearchParams(window.location.search)
let courseId = params.get('courseId')
let token = localStorage.getItem('token')
fetch(`http://localhost:3000/api/courses/${courseId}`, {
method: "PUT",
headers:{
'Authorization': `Bearer ${token}`
}
})
.then(res => res.json())
.then(data => {
if(data === true){
window.location.replace('./courses.html')
}else{
alert("Something went wrong.")
}
})
This is my routes:
router.put('/:courseId', auth.verify, (req, res) => { let courseId = req.params.courseId CourseController.unArchive({courseId}).then(resultFromUnArchive => res.send(resultFromUnArchive)) })
This is my controller:
module.exports.unArchive = (params) => {
let updateActive2 = {
isActive: true
}
return Course.findOneAndUpdate(params.courseId, updateActive2).then((course, err) => {
return (err) ? false : true
})
}
I've tried this and it doesn't work
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
isActive:true
})
I'm trying to make a post call to the backend server, but I keep running into this error:
TypeError: Failed to fetch
I've looked over the code a bunch of times but can't seem to find the issue. Here is the code:
async doLogin() {
if(!this.state.email || !this.state.password) {
return
}
this.setState({
buttonDisabled : true
})
try {
let res = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: this.state.email,
password: this.state.password
})
})
console.log(res)
let result = await res.json()
console.log(result)
if(result && result.success) {
UserStores.isLoggedIn = true
UserStores.email = result.email
alert(result.msg)
} else if(result && result.success === false) {
this.resetForm()
alert(result.msg)
}
} catch(e) {
console.log('doLogin error: ', e)
this.resetForm()
}
}
This is an example response payload:
{
"success": true,
"email": "mfultz956#gmail.com",
"msg": "Login Verified!"
}
Login Call - Network Tab
Login Call - Headers
change it to :
let res = await fetch('http://localhost:your_api_server_port/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: this.state.email,
password: this.state.password
})
})
I'm new in javascript. I've a async function getListBar. Inside getListBar i use return result of getAccount like a input of function fetch( you can see user.access_token) . Code run correct but i don't want call getAccount everytime i use getListBar. So how can i get result of getAccount and save it ?
I've tried many ways but promise very difficult to me , i don't know how to save result of it
async function getAccount() {
try {
let response = await fetch(apiAuthen,
{
method: 'POST',
headers: {
Accept: '*/*',
'Authorization': 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA==',
'Content-Type': 'application/x-www-form-urlencoded',
'grant_type': 'password',
},
body: qs.stringify({
'grant_type': 'password',
'username': 'abc',
'password': 'abc',
'client_id': 'abc',
})
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
async function getListBar() {
try {
const user = await getAccount().then(user => { return user });
let response = await fetch(apiBar,
{
headers: {
'Authorization': 'Bearer ' + user.access_token
}
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
getAccount will return a Promise like this and i want save access_token in it
Promise {_40: 0, _65: 0, _55: null, _72: null}
_40: 0
_55: {access_token: "41b369f2-c0d4-4190-8f3c-171dfb124844", token_type: "bearer", refresh_token: "55867bba-d728-40fd-bdb9-e8dcd971cb99", expires_in: 7673, scope: "read write"}
_65: 1
_72: null
__proto__: Object
If it is not possible to simply store a value in the same scope that these functions are defined, I would create a Service to handle getting the user. Preferably in its own file
AccountService.js
class AccountService {
getAccount = async () => {
if (this.user) {
// if user has been stored in the past lets just return it right away
return this.user;
}
try {
const response = await fetch(apiAuthen, {
method: 'POST',
headers: {
Accept: '*/*',
Authorization: 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA==',
'Content-Type': 'application/x-www-form-urlencoded',
grant_type: 'password'
},
body: qs.stringify({
grant_type: 'password',
username: 'abc',
password: 'abc',
client_id: 'abc'
})
});
const responseJson = await response.json();
this.user = responseJson.data; // store the user
return this.user;
} catch (error) {
console.log(`Error is : ${error}`);
}
// you should decide how to handle failures
// return undefined;
// throw Error('error getting user :(')
};
}
// create a single instance of the class
export default new AccountService();
and import it where needed
import AccountService from './AccountService.js'
async function getListBar() {
try {
// use AccountService instead
const user = await AccountService.getAccount().then(user => { return user });
let response = await fetch(apiBar,
{
headers: {
'Authorization': 'Bearer ' + user.access_token
}
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
You will still be calling getAccount each time in getListBar but it will only fetch when AccountService has no user stored.
Now i write in the different way
export default class App extends Component {
constructor() {
super();
this.state = {
accessToken: '',
users: [],
listBar: []
}
}
//Get Account
Check = () => {
getAccount().then((users) => {
this.setState({
users: users,
accessToken: users.access_token
});
}).catch((error) => {
this.setState({ albumsFromServer: [] });
});
}
//Get Account
getAccount() {
return fetch(apiAuthen,
{
method: 'POST',
headers: {
Accept: '*/*',
'Authorization': 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA===',
'Content-Type': 'application/x-www-form-urlencoded',
'grant_type': 'password',
},
body: qs.stringify({
'grant_type': 'password',
'username': 'abc',
'password': 'abc',
'client_id': 'abc',
})
}).then((response) => response.json())
.then((responseJson) => {
this.setState({
users: responseJson.data,
accessToken: responseJson.data.access_token
});
return responseJson.data
})
.catch((error) => {
console.error(error);
});
}
//Get List Bar
getListBarFromServer() {
return fetch(apiBar, {
headers: {
'Authorization': 'Bearer ' + this.state.accessToken
}
}).then((response) => response.json())
.then((responseJson) => {
console.log(this.getListBarFromServer()) <---- Just run if console
this.setState({ listBar: responseJson.data });
return responseJson.data
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getAccount();
this.getListBarFromServer();
}
render() {
return (
<View style={{ top: 100 }}>
<FlatList data={this.state.listBar} renderItem={({ item }) => {
return (
<View>
<Text>{item.bar_id}</Text>
</View>
)
}}>
</FlatList>
</View>
)
}
}
It's just run when i console.log(this.getListBarFromServer()) .Please explain to me why?