Handing errors i.e., 401, 403, etc. for Ember Test - ruby-on-rails

Searching for suggestions on handling errors in an Ember test? Found a closed issue from last year, Allowing rejected promises in tests, which indicated that rejections cannot be conveniently handled. Is this still the case?
Right now, in our application, we are using ajax errors to return an unauthorized error if the user is logged out of our main Rails application.
Route
import Ember from 'ember';
import {isUnauthorizedError, isForbiddenError} from 'ember-ajax/errors';
export default Ember.Route.extend({
userService: Ember.inject.service('user'),
activate: function(){
this.get('userService').facility().then(
(data) => {
this.get('controller').set('facility', data.data);
}, (response) => {
if (isUnauthorizedError(response)) {
this.transitionTo('report.failure.401');
} else if (isForbiddenError(response)) {
this.transitionTo('report.failure.403');
} else {
}
}
),
Acceptance Test
test('generates 401 error', function(assert) {
visit('/reporting');
httpStubs.stubUser(server, {}, 401);
httpStubs.stubFacility(server, {});
httpStubs.stubReport(server, '/api/reports/summary.json', []);
click('button.btn-filter')
andThen(function(){
assert.equal(currentURL(), '/users/login');
});
Report Stub
stubReport(server, url, data, status) {
let statusCode = (status) ? status : 200;
server.get(url, function() {
return [
statusCode,
{'Content-Type': 'application/json'},
JSON.stringify(data)
];
});
}

Related

How to delay http responses on errors in Angular 7

I am working on an Angular7 project and have some issues about error handling on http requests.
Here is my Login Component with two functions
export class LoginComponent implements OnInit, OnDestroy {
emailLogin1() {
this.authService.emailLogin1(this.loginForm.value).pipe(delay(1000)).subscribe(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
);
}
emailLogin2() {
this.authService.emailLogin2(this.loginForm.value).pipe(delay(1000)).subscribe(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
);
}
}
Here is my AuthService with two functions.
export class AuthService {
constructor(private http: HttpClient) {
}
emailLogin1(values): any {
return this.http.post(environment.server + '/auth/emailLogin', values).pipe(
tap(
(response) => {
localStorage.setItem('token', response['token']);
},
(error) => {
console.log(error);
}
)
);
}
emailLogin2(values): any {
return this.http.post(environment.server + '/auth/emailLogin', values).pipe(
tap(
(response) => {
localStorage.setItem('token', response['token']);
}
),
catchError((error) => {
console.log(error);
throw error;
})
);
}
}
When I make a request to the server, if response status is successful, it waits for 1000 ms and then shows the result as expected. But if response returns an error, delay(1000) function not working and error block working immediately. I tried with and without catchError. Both working exactly the same.
The delay operator will only work on events sent through the observable via "next" notifications (in your case, this is a "success"). When an error occurs, it is sent as an "error" notification, and it skips right past your delay operator. If you want to delay the error, you should catch it, introduce a delay, and then re-throw it:
emailLogin1() {
this.authService.emailLogin1(this.loginForm.value).pipe(
delay(1000), // only affects "success"
catchError(error => interval(1000).pipe( // only affects "error"
mergeMap(() => throwError(error)) // re-throw error after our delay
)),
).subscribe(
(response) => {
console.log(response);
},
(error) => {
console.log(error);
}
)
}

Javascript post request callback, redirect from .NET MVC controller

I'm integrating PayPal checkout with an e-com solution, where upon PayPal successfully creating PayPal order/payment, I carry out some server side processing which eventually returns a RedirectResult (with a URL for payment failed or success accordingly) from my controller, back to the client/frontend.
I have the following code below, and was expecting it to redirect automatically, but no redirect occurrs.
paypal.Buttons({
createOrder: function (data, actions) {
return actions.order.create({
intent: "CAPTURE",
purchase_units: [{
amount: {
value: '5.20',
}
}]
});
},
onApprove: function (data, actions) {
return actions.order.capture().then(function (details) {
return fetch('/umbraco/surface/PayPalPayment/process', {
method: 'post',
redirect: 'follow',
body: JSON.stringify({
OrderID: data.orderID,
PayerID: data.payerID,
}),
headers: {
'content-type': 'application/json'
}
});
}).catch(error=>console.log("Error capturing order!", error));
}
}).render('#paypal-button-container');
If I explicitly redirect with the code below, then the action carries out.
onApprove: function (data, actions) {
return actions.order.capture().then(function (details) {
return fetch('/umbraco/surface/PayPalPayment/process', {
method: 'post',
redirect: 'follow',
body: JSON.stringify({
OrderID: data.orderID,
PayerID: data.payerID,
}),
headers: {
'content-type': 'application/json'
}
}).then(function () { window.location.replace('https://www.google.co.uk') });
}).catch(function (error) {
console.log("Error capturing order!", error);
window.location.replace('https://www.bbc.co.uk');
});
}
Basically, I'm wondering why fetch redirect does not follow the Redirect that is returned form my controller. Controller redirect for full completeness:
return new RedirectResult("/checkout/thank-you") ;
Let me try to rephrase your question
You want to know why the browser did not redirect after you made a fetch - even though fetch api response
was a RedirectResult
The reason is simple, you made a request in fetch, which means you are making ajax request (hence browser will not change)
you set the redirect to follow, which means after the first request (i.e after get response from
/umbraco/surface/PayPalPayment/process), it will follow to the url /checkout/thank-you
so, what you get in the then() will be the response of /checkout/thank-you
so overall, it did follow the response but maybe not the way you expected (follow within the ajax request, not browser changing the page)
If what you want is a redirect to specific page, after the success call to /umbraco/surface/PayPalPayment/process
Then do:
Modify your backend to return JsonResult of the url instead of RedirectResult
return Json(new {redirectUrl = "/checkout/thank-you"});
use then to redirect
// other code omitted
.then(function (response) { return response.json(); })
.then(function (data) {window.location.replace(data.redirectUrl)});

Service Worker w offline.html Backup Page

I can't get the offline.html page to display. I keep getting the The FetchEvent for "https://my-domain.com" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow".
Here's the snippet of my service-worker.js which should return the offline.html when the network is unavailable.
self.addEventListener('fetch', function(event) {
if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
if(event.request.url.includes("my-domain.com")){
console.log(event.request);
event.respondWith(
caches.match(event.request).then(function(resp) {
return resp || fetch(event.request).then(function(response) {
let responseClone = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
return response;
});
}).catch(function() {
return caches.match("/offline.html");
})
);
}
}
});
Below is the console.log of my network request (page refresh when offline)
Request {method: "GET", url: "https://my-domain.com", headers: Headers, destination: "unknown", referrer: "", …}
bodyUsed:false
cache:"no-cache"
credentials:"include"
destination:"unknown"
headers:Headers {}
integrity:""
keepalive:false
method:"GET"
mode:"navigate"
redirect:"manual"
referrer:""
referrerPolicy:"no-referrer-when-downgrade"
signal:AbortSignal {aborted: false, onabort: null}
url:"https://my-domain.com"
__proto__:Request
I got this working / found the fix. It was related to a redirected response security issue in the browser. From the Chromium Bugs Blog, Response.redirected and a new security restriction.
Solution: To avoid this failure, you have 2 options.
You can either change the install event handler to store the response generated from res.body:
self.oninstall = evt => {
evt.waitUntil(
caches.open('cache_name')
.then(cache => {
return fetch('/')
.then(response => cache.put('/', new Response(response.body));
}));
};
Or change both handlers to store the non-redirected response by setting redirect mode to ‘manual’:
self.oninstall = function (evt) {
evt.waitUntil(caches.open('cache_name').then(function (cache) {
return Promise.all(['/', '/index.html'].map(function (url) {
return fetch(new Request(url, { redirect: 'manual' })).then(function (res) {
return cache.put(url, res);
});
}));
}));
};
self.onfetch = function (evt) {
var url = new URL(evt.request.url);
if (url.pathname != '/' && url.pathname != '/index.html') return;
evt.respondWith(caches.match(evt.request, { cacheName: 'cache_name' }));
};

auth0 invalid token toward Rails API

I'm trying to set up authentication using the Auth0 lock along with a React single page app and a Ruby on Rails API.
import React from 'react';
import Auth0Lock from 'auth0-lock';
var Login = React.createClass({
componentWillMount: function() {
this.lock = new Auth0Lock('*************', '****.eu.auth0.com', {
allowedConnections: ['facebook']
});
this.lock.on('authenticated', this._doAuthentication.bind(this));
},
showLock: function() {
this.lock.show();
},
_doAuthentication(authResult) {
console.log('Bearer '+authResult.idToken);
var request = require("request");
var options = { method: 'POST',
url: 'http://localhost:3000/authenticate',
headers: { authorization: 'Bearer '+authResult.idToken } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
console.log(authResult);
this.setToken(authResult.idToken)
},
login() {
this.lock.show()
},
loggedIn() {
return !!this.getToken()
},
setToken(idToken) {
localStorage.setItem('id_token', idToken)
},
getToken() {
return localStorage.getItem('id_token')
},
logout() {
localStorage.removeItem('id_token');
},
render: function() {
return (
<div className="login-box">
<button className="btn btn-success" onClick={this.showLock}>Sign In</button>
</div>);
}
});
export default Login;
This code comes from the Auth0 documentation. I also configured knock on the Rails API. Still, whenever I click on the "Connect via Facebook" button, I get the following:
- my token is generated (It's a valid token)
- Request is sent, with the correct authorization header
- Rails returns a 401
I made sure Rails receives the header as "Bearer [MY TOKEN]", still getting a 401.
Did I miss something ? Is anything else required ?
Ok, finally found out: my secret was not 64base encoded, which means the JWT.base64url_decode in my knock.rb was not necessary. I removed it, and voila.

React Native IOS App crashes when no network conection

On the simulator it does not crash and Alerts the error, but in production it is crashes as soon as fetch request suppose to be made and it is impossible to reopen the app until network connection is back (I turn on/off airplane mode for the testing)
here are the snippets of my code
componentWillMount: function(){
NetInfo.isConnected.addEventListener('change',this.handleConnectivityChange)
NetInfo.isConnected.fetch().done((data) => {
this.setState({
isConnected: data
})
console.log('this.state.isConnected: ', this.state.isConnected);
})
},
handleConnectivityChange: function(){
var connected = this.state.isConnected ? false : true
this.setState({isConnected: connected})
console.log('this.state.isConnected11: ', this.state.isConnected);
},
....
goToList: function(replace, listview){
console.log('this.state.isConnected: ', this.props.isConnected);
if (!this.props.isConnected){
AlertIOS.alert('Error', 'Please check your network connectivity')
this.props.removeFetching()
return
}
....
fetch(url)
.then((response) => response.json())
.then((responseData) => {
....
.catch((error) => {
StatusBarIOS.setNetworkActivityIndicatorVisible(false)
AlertIOS.alert('Error', 'Please check your network connectivity')
this.props.removeFetching()
})
.done()
I spent a lot of time trying to find a way to catch exceptions when using fetch() but I was unable to get it working (e.g. using .catch() or a try/catch blog didn't work). What did work was to use XMLHttpRequest with a try/catch blog instead of fetch(). Here's an example I based off of: https://facebook.github.io/react-native/docs/network.html#using-other-networking-libraries
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
var responseJson = JSON.parse(request.responseText);
// *use responseJson here*
} else {
console.warn('error');
}
};
try {
request.open('GET', 'https://www.example.org/api/something');
request.send();
} catch (error) {
console.error(error);
}

Resources