Angular2 custom request - android

I am using a custom HTTP request class for adding a Authorization Header to all of my requests, this works fine on almost every android device. Wired thing now is that I got some customer complaints that they are getting the 'No internet connection' error although they have a working network connection (other apps work and the errors are transmitted to the Sentry servers also).
As I am using Sentry error tracking I found out that these customers are all getting the error because the timeout error is thrown after 10 seconds for the first request at app start.
I guessed that something has to be wrong with this request so I built an alpha version for a limited number of users to track down the error (I send the options of every request to Sentry), but the requests look fine.
Next guess was that something is wrong with cordova-plugin-nativestorage on these devices but as I am catching them it should at lease return an empty token. No clue how to fix it right now. Any advice is appreciated!
export class CustomRequest {
apiToken: string = '';
constructor(private http: Http) { }
protected request(options: any): Observable<any> {
// If Native Storage doens't find a token, return an empty
let errorNativeStorage$ = function (): Observable<any> {
return Observable.of({ Token: '' });
};
// Get Token form Native Storage
let token$ = Observable.fromPromise(NativeStorage.getItem('JWT'))
.catch(errorNativeStorage$);
// Handle request errors
let genericError$ = function (error: Response | any): Observable<any> {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
Raven.captureException(error, { extra: { errorMsg: errMsg } });
return Observable.of({
Errors: { General: 'No internet connection.' }
});
};
// the request
let request$ = (options) => {
return this.http.request(new Request(options))
.retryWhen(error => error.delay(1000))
.timeout(10000, new Error('timeout'))
.map((res: Response) => res.json())
.catch(genericError$);
};
// get the token and build request
return token$
.map(jwt => {
if (options.body) {
if (typeof options.body !== 'string') {
options.body = JSON.stringify(options.body);
}
options.body = options.body.replace(/ /g, '').replace(/\r?\n|\r/g, '');
}
options.headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded, application/json'
});
if (jwt.Token) {
options.headers.append('Authorization', `Bearer ${jwt.Token}`);
}
Raven.captureMessage('request options', { level: 'info', environment: 'live', extra: options });
return options;
})
.switchMap(options => request$(options));
}
}
I am using:
Ionic 2.0.0-beta.11
Angular 2.0.0-rc.4
Most recent version of NativeStorage plugin from github
Devices with the error (only two examples, there are more):
Samsung SM-N910F (Webview: Chrome Mobile 53.0.2785, Android 6.0.1)
Samsung SM-G800F (Webview: Chrome Mobile 53.0.2785, Android 5.1.1)

If somebody's interested: The root cause was that people that upgraded Android somehow lost the chrome webview app and Angular was not working without one (of course). I solved it by packaging the crosswalk-webview in my app!

Related

Ionic 5 - Can't display data from API

Note: Total Ionic newbie here.
I have the following:
Ionic 5 (Capacitor) app with Angular 11.
Express backend (localhost:3000)
I can fetch data from an API call and display in the browser, but not on the emulated Android device. I don't know how to check for console errors in Android Studio.
This image can explain the situation better.
I think this is due to CORS. I tried to follow the Ionic page on this but no resolution.
Here is my Express code:
const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;
const allowedOrigins = [
"capacitor://localhost",
"ionic://localhost",
"http://localhost",
"http://localhost:8080",
"http://localhost:8100",
"http://192.168.2.25:8100",
];
// For parsing JSON in request body
app.use(express.json());
// MySQL connection details - for POC sake.
// In PROD, these are typically saved in .env variables
// Ref: https://www.linkedin.com/pulse/storing-database-credentials-securely-siddhesh-jog
var mysql = require("mysql");
var connection = mysql.createConnection({
host: "____________________________.us-east-2.rds.amazonaws.com",
user: "admin",
password: "*****************",
database: "poc",
});
const corsOptions = {
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
console.log(origin);
callback(new Error("Origin not allowed by CORS"));
}
},
};
// Enable preflight requests for all routes
app.options("*", cors(corsOptions));
// Connect to MySQL
connection.connect(function (err) {
if (err) throw err;
console.log("Connected!");
});
// Dashboard - GET
app.get("/dashboard", cors(corsOptions), (req, res) => {
rows = [];
connection.query(
"select label_id, value from poc_fct",
function (err, result) {
if (err) throw err;
res.json(result);
}
);
});
app.listen(port, () => {
console.log(`CORS-enabled web server listening at http://localhost:${port}`);
});
Any help will be greatly appreciated.
What finally worked for me was changing the API endpoint from http://localhost:3000/data to http://192.168.2.25:3000/data, where 192.168.2.25 is the local IP address of the host where the Express server is running.
Few notes for anyone else who might have this issue in the future:
This isn't a CORS issue. I commented out app.use(cors)
This isn't a HTTP/HTTPS issue
Changing the emulator's proxy to 10.0.2.2:3000 did not work

Cannot send image in base64 format with XHR to aws S3 in android(react-native)

I got very strange issue: we have scan functionality for documents in our app and as the result scan give's me encoded base64 image with photo. Everything is good on ios platform but when I trying to send my picture on android, I get xhr.status 0 and error. Also, next strange thing is that when I starting debug mode and enable network inspection in react-native-debugger, picture is sending without errors. I was trying it on release app version, installed on my device, but still got an error with status 0
XHR request
export const uploadXHRImage = (url: string, data: IDataUploadImage) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve('Image successfully uploaded to S3');
} else {
reject(localize('failedUploadImage'));
}
}
};
xhr.ontimeout = () => reject(localize('timeoutUploadImage'));
xhr.timeout = UPLOAD_IMAGE_TIMEOUT;
xhr.open('PUT', url);
xhr.setRequestHeader('Content-Type', data.type);
xhr.send(data);
});
};
Add to header:
"Content-Type": 'application/json',
"Connection": "close",
I found the answer: Android can not process xhr send image, while image isn't saved as file in cache or other directory. Also, android needs file:// before data. Example is here:
saveImage = (image: string) => {
if (IS_IOS) {
return `data:image/jpg;base64,${image}`;
}
const tempFileDirectory = `${fs.CachesDirectoryPath}`;
const tempFilePath = `${tempFileDirectory}/${uuidv4()}.jpg`;
fs.writeFile(tempFilePath, image, 'base64');
return `file://${tempFilePath}`;
};

Expo InAppPurchases connectAsync returns undefined

I'm having issues trying to get the InAppPurchases to work in my React Native app using Expo. https://docs.expo.io/versions/latest/sdk/in-app-purchases
This is a bare workflow app that I ejected from a standard Expo app.
I made sure to follow the install instructions here carefully: https://docs.expo.io/bare/installing-unimodules/
I did test if unimodules was properly installed:
import { Constants } from "react-native-unimodules";
useEffect(() => {
alert("installed: " + JSON.stringify(Constants.systemFonts));
}, []);
The code above worked.
I'm using react-native-unimodules version 0.11.0.
Here's my code:
useEffect(() => {
(async function init() {
try {
const connect_res = await connectAsync();
alert("connect: " + JSON.stringify(connect_res));
} catch (err) {
alert("general error for connect async: " + err);
}
})();
}, []);
This is in the App.js entrypoint file. It always returns undefined for the connect_res so I assume this is the reason why I couldn't get any of the code to work.
Just below connectAsync() I have the following. This one also doesn't return anything:
setPurchaseListener(({ responseCode, results, errorCode }) => {
if (responseCode === IAPResponseCode.OK) {
results.forEach((purchase) => {
if (!purchase.acknowledged) {
alert("purchase successful!");
finishTransactionAsync(purchase, true);
}
});
}
if (responseCode === IAPResponseCode.USER_CANCELED) {
alert("user cancelld!");
} else if (responseCode === IAPResponseCode.DEFERRED) {
alert("user does not have permission to buy");
} else {
alert("something went wrong: " + errorCode);
}
});
Then on my payment screen I have the following:
import { getProductsAsync, purchaseItemAsync } from "expo-in-app-purchases";
This is the code for the payment button:
const makePayment = async () => {
alert("now making payment...");
try {
const items = Platform.select({
ios: ["abc"],
android: ["my-sub-id", "sku-my-sub-id"],
});
alert("items: " + JSON.stringify(items));
const products = await getProductsAsync(items);
alert("products: " + JSON.stringify(products));
if (products.results.length > 0) {
alert("found products!");
await purchaseItemAsync("my-sub-id");
alert("done making payment!");
} else {
alert("no products..");
}
} catch (err) {
alert("error occured while trying to purchase: " + err);
}
};
In this case, getProductsAsync() does return something resembling the format the results should be. But it doesn't return any of the subscriptions I created (I copied the product ID value listed in that column and I supplied it to both getProductsAsync and purchaseItemAsync. I also supplied the url version which basically just has a prefix of sku-:
I also enabled licensing testing for the email I'm using in Google Play:
Do note that I'm uploading the .aab file to Google Play on the internal testing track then I install it using this URL format: https://play.google.com/apps/test/com.myname.appname/versionNumber
But when I open that link, it seems like google play is detecting it as an old version even though its the latest one. The changes I've made shows up though so I'm pretty sure that's the correct install URL.
What else could I be missing?
For anyone having the same problem. all you have to do is add this on your android/app/src/main/AndroidManifest.xml file:
<uses-permission android:name="com.android.vending.BILLING" />
It wasn't mentioned in the docs at the time of this post, and it doesn't get automatically added when you install a module. Might help save you the headache if you assume the same is true for all Expo modules.

ionic3 error on android when calling GET HTTP

In Ionic3 I installed import { HttpClientModule } from '#angular/common/http'; and in my code I'm trying to call below url :
testHTTP(){
this.httpClient.get("https://api.upcitemdb.com/prod/trial/lookup?upc=5425016921463").subscribe(
data => {
console.log('Data : ', data);
this.lookup = data["items"][0].title;
}, err => {
console.log('Error : ', err);
this.lookup = JSON.stringify(err);
});
}
but every time it fails with :
{"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":null,"ok":false,"name":"HttpErrorResponse","message":"Http failure response for (unknown url): 0 Unknown Error","error":{"isTrusted":true}}
When I click on the button calling testHTTP on Chrome, I get a CORS error with this object (but I can deactivate CORS and get the real response).
How do you setup ionic http for the get to work please ?
You should use a XMLHttpRequest. And dont forget the Headers.
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.upcitemdb.com/prod/trial/lookup?upc=5425016921463", true);
//Really important to set this header
xhr.setRequestHeader("Access-Control-Allow-Origin","http://localhost:8100");
xhr.onreadystatechange = function() {
if(xhr.readyState == xhr.DONE) {
let ResponseFromRequest = xhr.response //Response from your request
}
}
xhr.send(null);

Network request in react native fails after two minutes

I am having some trouble using couchdb in react native. See code below :
const urlcouchdb = 'http://192.168.58.1:5984';
export const login = async (name, password) => {
const response = await fetch(`${urlcouchdb}/_session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
password,
}),
}).catch(function(error) {
console.log("error = " + error);
return error;
});
if (
response.headers &&
response.headers.map['set-cookie'] &&
response.headers.map['set-cookie'][0]
) {
await AsyncStorage.setItem(
'cookiecouchdb',
response.headers.map['set-cookie'][0],
);
}
return response.json();
}
At first, I was using my localhost IP (127.0.0.1), and I was getting this error : TypeError: Network request failed.
After some researches, I've figured out I'd better change it to the IP address of system. I thought my problem was solved, because I was not getting the error anymore, but it turned out that I was still getting the same error, but two minutes (approximatly), after doing the request!
It's very annoying to wait two minutes every single time I try to solve it. Do you have any idea why my request fails?
Just to let you know : The name and password I send to login function are correct. Also, I am testing on my android device, using expo.

Categories

Resources