Spotify api not searching? - javascript

I'm working on a project in codecademy that lets you make a playlist through spotify, but when I type something in the search nothing pops out.I assume its something with the spotify api.
I was seeing that spotify changed their endpoints but I'm not really sure. Nothing in the console says any error. Any help?
Spotify.js
const clientId = '**';
const redirectUri = 'http://localhost:3000'
let accessToken;
const Spotify = {
getAccessToken() {
if (accessToken){
return accessToken;
}
//check for access token match
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if(accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
const expiresIn = Number(expiresInMatch[1]);
//clears params, allowing to grab new access token when it expires
window.setTimeout(() => accessToken ='', expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
search(term){
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {headers: {
Authorization: `Bearer ${accessToken}`
}
}).then(response => {
return response.json();
}).then(jsonResponse => {
if (!jsonResponse.tracks){
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artist[0].name,
album: track.album.name,
uri: track.uri
}))
})
},
savePlayList(name, trackUris){
if(!name || !trackUris.length){
return;
}
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}`};
let userId;
return fetch('https://api.spotify.com/v1/me', {headers: headers}
).then(response => response.json()
).then(jsonResponse => {
userId = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`,
{
headers: headers,
method: 'POST',
body: JSON.stringify({name: name})
}).then(response => response.json()
).then(jsonResponse => {
const playListId = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playListId}/tracks`, {
headers: headers,
method: 'POST',
body: JSON.stringify({uris: trackUris})
})
})
})
}
}
export default Spotify;

I found that spotify api changed the json response. I removed the [0] in artist: track.artist[0].name to artist: track.artists.name and this worked.

Related

how to use Access-Tokens for CRUD REACT JS

Given is an application for managing users. Following help files are used for this purpose:
AuthenticationAction.js
ManagementAction.js
AuthenticationAction.js is used for authentication:
export function authenticateUser(userID, password) {
console.log("Authenticate")
return dispatch => {
dispatch(getAuthenticateUserPendingAction());
login(userID, password).then(userSession => {
const action = getAuthenticationSuccessAction(userSession);
dispatch(action);
}, error => {
dispatch(getAuthenticationErrorAction(error));
}).catch(error => {
dispatch(getAuthenticationErrorAction(error));
})
}
}
function login(userID, password) {
const hash = Buffer.from(`${userID}:${password}`).toString('base64')
const requestOptions = {
method: 'POST',
headers: {
'Authorization': `Basic ${hash}`
},
};
return fetch('https://localhost:443/authenticate', requestOptions)
.then(handleResponse)
.then(userSession => {
return userSession;
});
}
function handleResponse(response) {
console.log(response)
const authorizationHeader = response.headers.get('Authorization');
return response.text().then(text => {
if (authorizationHeader) {
var token = authorizationHeader.split(" ")[1];
}
if (!response.ok) {
if (response.status === 401) {
logout();
}
const error = response.statusText;
return Promise.reject(error);
} else {
let userSession = {
/* user: data, */
accessToken: token
}
return userSession;
}
});
}
ManagementAction.js is there for the Crud Functions.
export function createUser(userID, username, password) {
console.log("Create a User")
return dispatch => {
dispatch(getShowUserManagementAction());
createaUser(userID, username, password).then(userSession => {
const action = getShowUserManagementActionSuccess(userSession);
dispatch(action);
}, error => { dispatch(getShowUserManagementErrorAction(error)); }).catch(error => { dispatch(getShowUserManagementErrorAction(error)); })
}
}
function createaUser(userID, username, password) {
const token = "whatever"
const requestOptions = {
method: 'POST',
headers: { 'Authorization': `Basic ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ userID: userID, userName: username, password: password })
};
console.log(requestOptions)
return fetch('https://localhost:443/user/', requestOptions)
.then(handleResponse)
.then(userSession => {
return userSession;
});
}
question:
Now if I want to use the createaUser function instead of the hardcoded token value with the accestoken I created in login, how do I get the accesstoken and how do I have to rewrite the createaUser function ?
you can store the token you created in the local storage like this:
AuthenticationAction.js
let userSession = {
/* user: data, */
accessToken: token
}
localStorage.setItem("token", userSession.accessToken)
and you can access it as below:
ManagementAction.js
function createaUser(userID, username, password) {
const token = localStorage.getItem("token")
const requestOptions = {
method: 'POST',
headers: { 'Authorization': `Basic ${token}`, 'Content-Type': 'application/json' },
then
so you can send your token value with the request

Why i got the output as "\"userName\"" and not like that "userName"?

Why i got output "\"userName\"" and not as "userName" ?
in my example i try to do an get api that attach to it some data and that data comes from the async-storage.
when i console the output so its shows the data like that :
"\"userName\""
but it should output "userName" .
what is the way to fix that issue ?
so what is wrong in my way ?
const getSaveUserTokenData = async (data) => {
const url =
'https://URL/userName,userToken,PlatformType,DeviceID?' +
'userName=' +
data.userName +
'&userToken=' +
data.googlToken +
'&PlatformType=' +
data.platformId +
'&DeviceID=' +
data.deviceId;
await fetch(
url,
{
method: 'GET',
headers: {
Authorization: data.azureToken,
},
}
)
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
};
You can write this way as well:
fetchFunction = async () => {
let data = {
userName: 'jon',
userToken: 'bee22',
PlatformType: 'os-ios',
DeviceID: '222222'
}
const url = `https://yoururl.com/?userName=${encodeURIComponent(data.userName)}&userToken=${encodeURIComponent(data.userToken)}&PlatformType=${encodeURIComponent(data.PlatformType)}&DeviceID=${encodeURIComponent(data.DeviceID)}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
const json = await response.json();
console.log(json);
}
If your variable names are the one you want in your url, try this:
const getData = async () => {
let userName = 'jon';
let userToken = 'bee22';
let PlatformType = 'os-ios';
let DeviceID = '222222';
const queryString = Object.entries({
userName,
userToken,
PlatformType,
DeviceID,
})
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&');
const response = await fetch(
'https://url...?' + queryString
);
};
};
Note: user token should not be in the url but usually in the headers of your request, like so:
fetch(someUrl, {
headers: {
Authorization: userToken
}
});
fetch(`https://yoururl.com/${userName}`,
{ method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default'
})
.then(function(response) {
//your code here
});
A more generic solution would be something like this:
async function get(route, ...params) {
const url = `${route}${params.map(p => `/${p}`)}`;
const response = await fetch(url, { method: "GET", headers });
if (!response.ok)
throw new Error(`Http error status: ${response.status}`);
return response.json();
}
by using ...params you can pass a generic amount of parameters that will then be combined with:
const url = `${route}${params.map(p => `/${p}`)}`;
In your case you would call the method like this:
const result = await get("https://url..", "jon", "bee22", "os-ios", "222222");

Why is my playlist ID undefined when my app tries to save a playlist to my Spotify account?

I am working on the Codecademy Jammming project and I have gotten everything to work correctly except that I am unable to save my playlist to my Spotify account. When I try to run the below code, for some reason I get an error and it tells me the playlistId is undefined.
let accessToken;
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
// check for access token match
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
const expiresIn = Number(expiresInMatch[1]);
//This clears the parameters, allowing us to grab a new access token when it expires
window.setTimeout(() => accessToken = '', expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
search(term) {
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
headers: {Authorization: `Bearer ${accessToken}`}
}).then(response => {
return response.json();
}).then(jsonResponse => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
});
},
savePlaylist(name, trackUris) {
if (!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = {Authorization: `Bearer ${accessToken}`};
let userId;
return fetch('https://api.spotify.com/v1/me', {headers: headers}
).then(response => response.json()
).then(jsonResponse => {
userId = jsonResponse.id;
return fetch(`https://cors-anywhere.herokuapp.com/http://api.spotify.com/v1/users/${userId}/playlists`,
{
headers: headers,
method: 'POST',
body: JSON.stringify({name: name})
}).then(response => response.json()
).then(jsonResponse => {
console.log(jsonResponse);
const playlistId = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/playlists/${playlistId}/tracks`, {
headers: headers,
method: 'POST',
body: JSON.stringify({uris: trackUris})
});
});
});
}
};
I've tried logging the jsonResponse to the console and I don't see an id property (screenshot). Anyone have any idea why this is happening? I compared my code to the code used for their version of the site and it looks to be pretty much identical. Any help would be much appreciated.
Based on your code snippet, it appears you are making a call to the wrong endpoint to create the playlist
return fetch('https://cors-anywhere.herokuapp.com/http://api.spotify.com/v1/users/${userId}/playlists'
Perhaps try the below based on the documentation.
return fetch(https://api.spotify.com/v1/users/${userId}/playlists'

Issue with API Module

I'm working on a project that works with the Spotify API to allow users to search for and add songs/playlists to their account. I'm coding it all in react, and I get to the point where there's no compiler errors, the only error being:
Warning: Each child in an array or iterator should have a unique "key" prop.
Yet nothing on the application seems to work, you can't add songs to playlist, search the playlist, etc. I'm trying to figure out if something is wrong with my API module, because that's the only thing I can think of that has an issue and doesn't throw any errors. The module follows below, or you can view the repo And here is the Spotify API Authorization Document That we were told to use
function Spotify() {
function getAccessToken() {
if(accessToken !== '') {
return accessToken;
} else if (window.location.href.match(/access_token=([^&]*)/) != null){
accessToken = window.location.href.match(/access_token=([^&]*)/);
expiresIn = window.location.href.match(/expires_in=([^&]*)/);
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 function search(term) {
accessToken=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.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);
}
}
function savePlaylist(name, trackURIs) {
if (name === undefined || trackURIs === undefined) {
return;
} else {
let userAccessToken = getAccessToken();
let headers = {Authorization: userAccessToken};
let userId = findUserId(headers);
let playlistID;
fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": 'application/json'
},
body: {
name: name
}
}).then(response => {return response.json()}
).then(playlist => {
playlistID = playlist.id;
});
}
}
function findUserId(headers) {
accessToken = getAccessToken();
let id;
fetch(`https://api.spotify.com/v1/me`, {headers: headers}
).then(response => {return response.json()}
).then(jsonResponse => {
id = jsonResponse[0].id;
});
return id;
}
}
}
export default Spotify;
There are some problems with your code:
The handleTermChange method in SearchBar.js reads the value from the input incorrectly, it should be: this.setState({term: event.target.value});
The search button is only a link, you have to bind the onClick event, something like this: <a onClick={this.search}>SEARCH</a>
Something is not right in Spotify.js, currently the getAccessToken method encapsulates everything else.
If you inspect your playlist with React Dev Tools, you can see, that there is no id property on track in TrackList.js, so it will be always null. This is why you get the warning.

Need insight on how to build javascript fetch API

I am creating a web app with django rest framework as backend and react as frontend. The react app i generated from create-react-app. To make api call, i use fetch Api. I see some repetition in my api call that need to a reafactor. But i know which pattern is better here.
Here is the code:
Api.js
let _options = {headers: new Headers({'Content-Type': 'application/json'})}
const _url = {
'users': '/api/users/',
'obtain-token': '/api/obtain-token/',
'verify-token': '/api/verify-token/',
'refresh-token': '/api/refresh-token/'
}
const _fetch = (request) => {
return fetch(request)
.then(res => {
if ( ! res.ok) {
let err = {
'status': res.status,
'statusText': res.statusText,
'data': {}
}
return res.json().then(
data => {
err.data = data
return Promise.reject(new Error(JSON.stringify(err)))
},
() => Promise.reject(new Error(JSON.stringify(err)))
)
}
return res.json()
})
.catch(err => {
return Promise.reject(err)
})
}
export const obtainToken = (username, password) => {
const credential = {username, password}
let options = _options
options.method = 'POST'
options.body = JSON.stringify(credential)
const request = new Request(_url['obtain-token'], options)
return _fetch(request)
}
export const verifyToken = (token) => {
let options = _options
options.method = 'POST'
options.body = JSON.stringify({token})
const request = new Request(_url['verify-token'], options)
return _fetch(request)
}
export const refreshToken = (token) => {
let options = _options
options.method = 'POST'
options.body = JSON.stringify({token})
const request = new Request(_url['refresh-token'], options)
return _fetch(request)
}
export const getUser = (username, token='') => {
let options = _options
options.method = 'GET'
if (token) options.headers.append('Authorization', `JWT ${token}`)
const request = new Request(`${_url['users']}/${username}`, options)
return _fetch(request)
}
What i like from this code is, when i need to request a resource, i just run a function with needed parameter without knowing it is get or post or else, and i dont need to pass header configuration and url.
// example api call
let token = '4346h9r7yt47t9...'
verifyToken(token)
.then(data => {
// server response json available here
token = data.token
})
.catch(err => {
// handle network error and bad response here
console.log(err)
})
Im follwing #challenger solution. And manage to get it shorter with this:
// Change _build function name to makeRequest and export it
const makeRequest = (name, _url='', body=undefined, headers={}) => {
let [method, url] = source[name]
let options = {
headers: new Headers({
'Content-Type': 'application/json',
...headers,
})
}
options.method = method
if (body) options.body = JSON.stringify(body)
return _fetch(url+_url, options)
}
export default makeRequest
But, of course to call this function i need to supply the appended url, and a custom header if needed. But as this project goes it's proved that appended url (use by user detail resource) and costum header (use by resource that need authentication) is needed.
let source = {
'getUser': (..._p) => ['GET', `/api/users/${_p[0]}`, undefined, {'Authorization': `JWT ${_p[1]}`}],
'obtainToken': (..._p) => ['POST','/api/obtain-token/', {username:_p[0], password:_p[1]}],
'verifyToken': (..._p) => ['POST','/api/verify-token/', {token:_p[0]}],
'refreshToken': (..._p) => ['POST', '/api/refresh-token/',{token:_p[0]}]
}
const requests = {}
Object.keys(source).forEach(key => {
requests[key] = (...params) => {
let [method, url, body, headers ={}] = source[key](...params)
let options = {
headers: new Headers({
'Content-Type': 'application/json',
...headers,
})
}
options.method = method
if (body) options.body = JSON.stringify(body)
return _fetch(new Request(url, options))
}
})
export default requests
it needs more testing and review..
let source = {
'getUser': ['GET', '/api/users/'],
'obtain-token': ['POST','/api/obtain-token/'],
'verify-token': ['POST','/api/verify-token/'],
'refresh-token':['POST', '/api/refresh-token/']
}
let _build = (name, _url, body, headers) => {
let [method, url] = source[name]
let options = {
headers: new Headers({
'Content-Type': 'application/json',
...headers,
})
}
options.method = method
if (body) options.body = JSON.stringify(body)
return new Request(url+_url, options)
}
and then
export const obtainToken = (username, password) => {
const credential = {username, password}
const request = _build('obtainToken','', credential, {})
return _fetch(request)
}
export const verifyToken = (token) => {
const request = _build('verifyToken', '',{token}, {})
return _fetch(request)
}
export const refreshToken = (token) => {
const request = _build('refreshToken', '', {token}, {})
return _fetch(request)
}
export const getUser = (username, token='') => {
let headers = {'Authorization': `JWT ${token}`}
const request = _build('getUser',`/${username}`, undefined, headers)
return _fetch(request)
}
even more, instead of...
return new Request(url+_url, options)
in the _build function you can replace it with
return _fetch(new Request(url+_url, options))
which allows you to have this:
export const obtainToken = (username, password) => {
const credential = {username, password}
return _build('obtainToken','', credential, {})
}
export const verifyToken = (token) => {
return _build('verifyToken', '',{token}, {})
}
export const refreshToken = (token) => {
retun _build('refreshToken', '', {token}, {})
}
export const getUser = (username, token='') => {
let headers = {'Authorization': `JWT ${token}`}
return _build('getUser',`/${username}`, undefined, headers)
}

Categories