subscribe function always going on error observer - javascript

I am following a course on Udemy which uses Angular 2.0 and I'm trying to build it using the latest version. My problem occurs in this function :
checkSession() {
const url = 'http://localhost:8181/checkSession';
const xToken = localStorage.getItem('xAuthToken');
const basicHeader = 'Basic ' + localStorage.getItem('credentials');
const headers = new HttpHeaders({
'x-auth-token' : JSON.stringify(xToken),
'Authorization' : basicHeader
});
console.log(url);
console.log(headers);
return this.http.get(url, {headers: headers});
because when I access it on this function:
ngOnInit() {
const xToken = JSON.stringify(localStorage.getItem('xAuthToken'));
if (xToken) {
this.loginService.checkSession().subscribe(
res => {
console.log("Good")
this.loggedIn = true;
},
error => {
console.log("error = " + error)
this.loggedIn = false;
}
);
}
it always going on the error observer throwing this error:
[object Object]
OnSubmit function:
onSubmit() {
this.loginService
.sendCredential(this.credential.username, this.credential.password)
.subscribe(
res => {
localStorage.setItem('xAuthToken', JSON.stringify(res));
this.loggedIn = true;
const encodedCredentials = btoa(this.credential.username + ':' + this.credential.password);
localStorage.setItem('credentials', encodedCredentials);
// location.reload();
},
error => {
console.log(error);
}
);
Original functions from the course:
checkSession() {
let url = "http://localhost:8181/checkSession";
let headers = new Headers ({
'x-auth-token' : localStorage.getItem('xAuthToken')
});
return this.http.get(url, {headers: headers});
ngOnInit() {
this.loginService.checkSession().subscribe(
res => {
this.loggedIn=true;
},
error => {
this.loggedIn=false;
}
);
onSubmit() {
this.loginService.sendCredential(this.credential.username, this.credential.password).subscribe(
res => {
console.log(res);
localStorage.setItem("xAuthToken", res.json().token);
this.loggedIn = true;
// location.reload();
},
error => {
console.log(error);
}
);
Because of this error, I cannot save the session. The login works like a charm, but for some reason unknown by me, the session is not saved properly.
Network tab:

The response type from server will be text instead of json which will be causing the issue. try setting the header as text
return this.http.get(url, {headers, responseType: 'text'});

Please update function as below
checkSession() {
const url = 'http://localhost:8181/checkSession';
const xToken = localStorage.getItem('xAuthToken');
const basicHeader = 'Basic ' + localStorage.getItem('credentials');
const headers = new HttpHeaders({
'x-auth-token' : JSON.stringify(xToken),
'Authorization' : basicHeader
});
console.log(url);
console.log(headers);
return this.http.get(url, {headers: headers, responseType: 'text'});
}

It looks like you are trying to stringify the variable (xToken) that you get from local storage. The problem is that the get method from local storage returns a string so when you set your HttpHeaders, it will throw an error on the JSON.stringify(xToken) line.

Related

How to set two interceptors in axios

I have two functions that using axios post information to different APIs I created with node and express. Both of them have an interceptor as I get a response from by backend with messages, errors, and other information. Yet when I post the to the second url ("/users/login") the first interceptor still fires off (in the addUser instead of the findUser function) even though it is not in the same function. How do I fix this?
async function addUser(user) {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const interceptorResponse = axios.interceptors.response.use(
(response) => {
if (typeof response.data === "object") {
let success = response.data.registerSuccess;
let errors = response.data.errors;
let data = response.data.data;
let message = response.data.message;
setData(() => {
return { ...data, errors, registerSuccess: success, message };
});
}
return response;
}
);
await axios.post("/users/register", user, config);
axios.interceptors.request.eject(interceptorResponse);
} catch (err) {}
}
async function findUser(user) {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
axios.interceptors.response.use((response) => {
console.log(response);
if (typeof response.data === "object") {
let loginSuccess = response.data.data.loginSuccess;
let message = response.data.message;
console.log(response.data);
setData(() => {
return { ...data, loginSuccess, message };
});
}
return response;
});
await axios.post("/users/login", user, config);
} catch (error) {}
}

TypeError: Incorrect type for the 'headers' field on 'RequestInitializerDict': the provided value is not of type 'variant'

I'm new to JavaScript and I'm trying make a Github API Gateway for IFTTT(cause it can't modify header) with JS on Cloudflare Worker. Here's the code:
async function handleRequest(request) {
var url = new URL(request.url)
var apiUrl = 'https://api.github.com' + url.pathname
var basicHeaders = {
'User-Agent': 'cloudflare',
'Accept': 'application/vnd.github.v3+json'
}
const { headers } = request
const contentType = headers.get('content-type')
const contentTypeUsed = !(!contentType)
if (request.method == 'POST' && contentTypeUsed) {
if (contentType.includes('application/json')) {
var body = await request.json()
if ('additionHeaders' in body) {
var additionHeaders = body.additionHeaders
delete body.additionHeaders
}
var apiRequest = {
'headers': JSON.stringify(Object.assign(basicHeaders,additionHeaders)),
'body': JSON.stringify(body),
}
} else {
return new Response('Error: Content-Type must be json', {status: 403})
}
const newRequest = new Request(apiUrl, new Request(request, apiRequest))
try {
var response = await fetch(newRequest)
return response
} catch (e) {
return new Response(JSON.stringify({error: e.message}), {status: 500})
}
} else {
var apiRequest = {
'headers': JSON.stringify(basicHeaders)
}
const newRequest = new Request(apiUrl, new Request(request, apiRequest))
var response = await fetch(newRequest)
return response
}
}
addEventListener('fetch', async (event) => {
event.respondWith(handleRequest(event.request))
})
And I got this error when I tried to run it:
Uncaught (in promise)
TypeError: Incorrect type for the 'headers' field on 'RequestInitializerDict': the provided value is not of type 'variant'.
at worker.js:1:1245
at worker.js:1:1705
Uncaught (in response)
TypeError: Incorrect type for the 'headers' field on 'RequestInitializerDict': the provided value is not of type 'variant'.
This is an older version which run well but with less flexibility:
async function handleRequest(request) {
var url = new URL(request.url)
var apiUrl = 'https://api.github.com' + url.pathname
var accessToken = 'token '
var apiRequest = {
headers: {
'User-Agent': 'cloudflare',
'Accept': 'application/vnd.github.v3+json'
}
}
const { headers } = request
const contentType = headers.get('content-type')
const contentTypeUsed = !(!contentType)
if (request.method == 'POST' && contentTypeUsed) {
if (contentType.includes('application/json')) {
var body = await request.json()
if ('token' in body) {
accessToken += body.token
delete body.token
}
var apiRequest = {
headers: {
'Authorization': accessToken,
'User-Agent': 'cloudflare',
'Accept': 'application/vnd.github.v3+json'
},
body: JSON.stringify(body),
}
} else {
return new Response('Error: Content-Type must be json', {status: 403})
}
const newRequest = new Request(apiUrl, new Request(request, apiRequest))
try {
var response = await fetch(newRequest)
return response
} catch (e) {
return new Response(JSON.stringify({error: e.message}), {status: 500})
}
} else {
const newRequest = new Request(apiUrl, new Request(request, apiRequest))
var response = await fetch(newRequest)
return response
}
}
addEventListener('fetch', async (event) => {
event.respondWith(handleRequest(event.request))
})
The only difference seems to be apiRequest, but I don't know how to fix it. I tried to claim the variable with var apiRequest = new Object() first but didn't work.
Fix with this:
let apiRequest = new Object
apiRequest.headers = Object.assign(basicHeaders, additionHeaders)
apiRequest.body = JSON.stringify(body)
And the apiRequest will look like this:
{headers:{},body:"{}"}
This seems like what RequestInitializerDict want.

Way to handle refresh token

I am working on login feature and have problem when refresh token.
When token expire making request to refresh token, remove the old token, and save the new token to AsyncStorage.
After login successfully have to function A and B. The function A is using the new token to make its request. the function B say that it need to refresh the token so make request to refresh token ( the request make successfully, token being refresh) but The token that request A is using now invalid - I think it happens due to asynchronous
This is my code that use to refresh token:
axiosInstance.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
if (error.response.status === CODE_TOKEN_EXPIRED) {
try {
const token = await authenticationService.getRefreshToken();
const response = await authenticationService.refreshToken(token);
await authenticationService.removeToken();
await authenticationService.storeToken(response.data.params.access_token);
await authenticationService.storeRefreshToken(response.data.params.refresh_token);
error.config.headers.Authorization = 'Bearer ' + response.data.params.access_token;
error.response.config.headers['Authorization'] = 'Bearer ' + response.data.params.access_token;
return axiosInstance(error.config);
} catch (err) {
console.log(2, err);
await authenticationService.removeToken();
navigationService.navigate('LoginForm');
}
}
return Promise.reject(error);
}
);
Anyone know how to handle which asynchronous call for refresh token?
First would be for you to check if you are changing token to the correct axios instance. It is necessary to change Authorization header on error.response config as you did, but also for main axios instance (if you have one) like so: axios.defaults.headers.common["Authorization"] = "Bearer " + access_token;
If it is multiple parallel requests going on that could possibly need to be postponed after token is refreshed issue and answer gets complex, but check this gist with full refresh logic with axios.
I have implemented the same scenario in fetch API. you can also do this same in axios API. Try this to avoid interceptor concept.
Api.ts
export const api = ({ method, url, body, isProtected = true }) => {
return new Promise((resolve, reject) => {
const payload = {
method,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
};
if (body !== null) {
(payload as any).body = JSON.stringify(body);
}
/**
* "isProtected" is used for API call without authToken
*/
if (isProtected) {
AsyncStorage.getItem(ACCESS_TOKEN).then(accessKey => {
(payload.headers as any).Authorization = `Bearer ${accessKey}`;
fetch(url, payload)
.then((response: any) => {
/*
* 419 status denotes the timeout of authToken
*/
if (response.status == 419) {
// refresh token
AsyncStorage.getItem(REFRESH_TOKEN).then(refreshKey => {
const payloadRef = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + refreshKey
}
};
/*
* This call refresh the authToken using refreshing call to renew the authToken
*/
fetch(URL.baseUrl + "/refresh", payloadRef)
.then((response: any) => response.json())
.then(response => {
/*
* if refresh token expired. redirect to login page
*/
if (response.status !== codes.SUCCESS) {
if (!User.sessionOver) {
User.sessionOver = true;
Alert.alert(
'Alert',
'Session Timeout',
[
{
text: 'Get back to Login',
onPress: () => {
// get to Login page
}
}
],
{ cancelable: false }
);
}
} else if (response.status == codes.SUCCESS) {
/*
* If refresh token got refreshed and set it as authToken and retry the api call.
*/
AsyncStorage.setItem(ACCESS_TOKEN, response.payload.access_key).then(() => {
(payload.headers as any).Authorization = 'Bearer ' + response.payload.access_key;
fetch(url, payload)
.then(response => response.json())
.then(response => {
if (response.status == codes.SUCCESS) {
resolve(response);
}
})
.catch(error => {
reject(error);
});
});
}
});
});
} else {
resolve(response.json());
}
})
.catch(error => {
reject(error);
});
});
} else {
fetch(url, payload)
.then((response: any) => {
response = response.json();
resolve(response);
})
.catch(error => {
reject(error);
});
}
});
};
MovieService.ts
import { api } from '../services/api';
import { URL } from '../config/UrlConfig';
const getMovies = () => {
const method = 'GET';
const url = URL.baseUrl + '/v1/top/movies';
const body = null;
const isProtected = true;
return api({ method, url, body, isProtected });
};
export { getMovies };
Maybe it will helps - https://gist.github.com/ModPhoenix/f1070f1696faeae52edf6ee616d0c1eb
import axios from "axios";
import { settings } from "../settings";
import { authAPI } from ".";
const request = axios.create({
baseURL: settings.apiV1,
});
request.interceptors.request.use(
(config) => {
// Get token and add it to header "Authorization"
const token = authAPI.getAccessToken();
if (token) {
config.headers.Authorization = token;
}
return config;
},
(error) => Promise.reject(error)
);
let loop = 0;
let isRefreshing = false;
let subscribers = [];
function subscribeTokenRefresh(cb) {
subscribers.push(cb);
}
function onRrefreshed(token) {
subscribers.map((cb) => cb(token));
}
request.interceptors.response.use(undefined, (err) => {
const {
config,
response: { status },
} = err;
const originalRequest = config;
if (status === 401 && loop < 1) {
loop++;
if (!isRefreshing) {
isRefreshing = true;
authAPI.refreshToken().then((respaonse) => {
const { data } = respaonse;
isRefreshing = false;
onRrefreshed(data.access_token);
authAPI.setAccessToken(data.access_token);
authAPI.setRefreshToken(data.refresh_token);
subscribers = [];
});
}
return new Promise((resolve) => {
subscribeTokenRefresh((token) => {
originalRequest.headers.Authorization = `Bearer ${token}`;
resolve(axios(originalRequest));
});
});
}
return Promise.reject(err);
});
export default request;

How do I return the contents of this function to the client side?

I have a function that I call using
fetch(http://localhost:8888/.netlify/functions/token-hider?
stateName=' +stateName)
on my client side.
the token-hider function looks like this:
const qs = require("qs");
const fetch = require("node-fetch");
var alertEndpoint = "";
var parkEndpoint = "";
var parksWithAlerts = "";
exports.handler = async function getURLS(event, context, callback)
{
// Get env var values defined in our Netlify site UI
const {api_key, alert_api_url, park_api_url} = process.env;
var stateName =event.queryStringParameters.stateName;
alertEndpoint = `${alert_api_url}${stateName}${api_key}`;
parkEndpoint = `${park_api_url}${stateName}${api_key}`;
getData();
async function getData(alertsArea, alertHeader) {
const [getAlertData, getParkData] = await
Promise.all([fetch(alertEndpoint), fetch(parkEndpoint)] );
var alertResults = await getAlertData.json();
var parkResults= await getParkData.json();
var alertData = alertResults.data;
var parkData = parkResults.data;
parksWithAlerts = parkData.map(park => {
park.alertData = alertData.filter(alert => alert.parkCode ===
park.parkCode);
return park
});
console.log(parksWithAlerts);
}
console.log(callback);
};
how could I return the contents of parksWithAlerts back to the client side after this function is finished?
Try to learn more about callback functions in Javascript.
It is right there in your code, the callback that you are printing is actually suppose to be called after you have executed your code and you can do like this callback(parksWithAlerts);.
While calling the function getURLS you will provide the function which is suppose to get called with args.
Examples : https://www.geeksforgeeks.org/javascript-callbacks/
Here is an example with error handling and returning a response type of JSON
token-hider
import fetch from "node-fetch";
// Get env var values defined in our Netlify site UI
const {api_key, alert_api_url, park_api_url} = process.env;
async function getJson(response) {
return await response.json();
}
const alertEndpoint = stateName => {
return new Promise(function(resolve, reject) {
fetch(`${alert_api_url}${stateName}${api_key}`)
.then(response => {
if (!response.ok) { // NOT res.status >= 200 && res.status < 300
return reject({ statusCode: response.status, body: response.statusText });
}
return resolve(getJson(response))
})
.catch(err => {
console.log('alertEndpoint invocation error:', err); // output to netlify function log
reject({ statusCode: 500, body: err.message });
})
});
}
const parkEndpoint = stateName => {
return new Promise(function(resolve, reject) {
fetch(`${park_api_url}${stateName}${api_key}`)
.then(response => {
if (!response.ok) { // NOT res.status >= 200 && res.status < 300
return reject({ statusCode: response.status, body: response.statusText });
}
return resolve(getJson(response))
})
.catch(err => {
console.log('parkEndpoint invocation error:', err); // output to netlify function log
reject({ statusCode: 500, body: err.message });
})
})
}
exports.handler = function(event, context) {
const stateName = event.queryStringParameters.stateName;
return Promise.all([alertEndpoint(stateName), parkEndpoint(stateName)])
.then(values => {
const [alertData, parkData] = values;
const parksWithAlerts = parkData.map(park => {
park.alertData = alertData.filter(alert => alert.parkCode === park.parkCode);
return park;
});
return {
statusCode: 200,
headers: { 'content-type': 'application/json' },
body: JSON.stringify(parksWithAlerts)
};
})
.catch(error => {
return error;
});
};
NOTE: If you are trying to hide the token, make sure to not deploy this from a public repository on Netlify.
Also, this code has not been tested 100%, so there may be some things to resolve. The response layout and structure is something I use in a few of my lambda functions on Netlify.

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.

Categories