Ionic 2 native Http plugin not returning response - ionic-framework

I am using Ionic 2 HTTP native plugin and running http.post from a provider. The data is received from the API in the provider but does not seem to be being sent back to the page component. I get error:
TypeError: undefined is not an object (evaluating
'this.authProvider.login(formData).then')
PAGE: login.ts
doLogin() {
this.spinner = 'true';
this.authProvider.login(formData).then((result:any) => {
if(result.status == 'isTrue') {
this.storage.set('userId', result.userId);
this.storage.set('userToken', result.token);
this.storage.set('profilePic', result.profilepic);
this.storage.set('userUsername', result.username);
this.navCtrl.setRoot(TabsPage);
}
else {
this.presentToast('Incorrect email or password, try again');
console.log('not a user');
}
this.spinner = 'false';
}, (err) => {
});
}
PROVIDER: authProvider
login(data) {
if (this.platform.is('ios'))
{
this.http2.post(this.apiUrl+'/api/login', data, {})
.then((dataresult) => {
return dataresult; // this outputs ok in console.log, but doesnt
return back to page
//console.log(dataresult);
})
.catch(error => {
});
}
}

You should have to return promise from authProvider,
return new Promise(resolve => {
this.http2.post(this.apiUrl+'/api/login', data, {})
.subscribe(dataresult => {
resolve(dataresult);
});
});

Related

React-query is not updating the state

I recently started to use react query, but I don't quite understand yet how the state works under the hood.
I have a query function that logs in the user:
async function signin(
model: AuthenticationControllerSignInRequest | null
): Promise<any> {
if (model) {
queryClient.invalidateQueries()
const response = await CalyxApi.authApi().authenticationControllerSignIn(
model
);
LocalStorage.set(LOCAL_STORAGE_KEY.AUTH, response.data.authToken);
return response.data.authToken
}
return loadFromStorage()
}
Inside I use loadFromStorage function that gets authToken from local storage.
async function loadFromStorage(): Promise<AuthTokenModel | undefined> {
const storedAuth = LocalStorage.get(LOCAL_STORAGE_KEY.AUTH);
if (storedAuth) {
if (new Date(storedAuth.validUntil) < new Date()) {
LocalStorage.remove(LOCAL_STORAGE_KEY.AUTH)
return undefined;
}
return storedAuth;
} else {
return undefined;
}
}
In my Login components I use the query hook passing in signin function and formik that refetches on submit:
...
const { data: auth, refetch, isLoading: authLoading } = useQuery(['auth', signinModel], () => authActions.signin(signinModel), { enabled: false });
const formik = useFormik({
validateOnChange: false,
validateOnBlur: false,
initialValues: {
email: '',
password: '',
},
validationSchema: loginFormSchema,
onSubmit: async (values) => {
await setSigninModel({
email: values.email,
password: values.password
})
await refetch()
}
});
...
This works just fine. I am able to authenticate the user which should prompt another function that fetches the user from DB:
const { data: auth } = useQuery(['auth'], () => authActions.signin(null))
const userId = auth?.userId;
console.log('useUserActions: ', userId)
async function fetchUser(): Promise<UserModel | undefined> {
if (!userId) {
errorSuccessActions.throwError('USER ID IS UNDEFINED');
return
}
const result = await CalyxApi.userApi().userControllerGetUser(userId)
if (result.data) {
const user = result.data.user
return user
}
errorSuccessActions.throwError('USER NOT FOUND IN DB');
return
}
function useFetchUser(reactQueryOptions?: {}) {
return useQuery<UserModel | undefined>(
["user", userId],
() => fetchUser(), {
...reactQueryOptions, refetchOnWindowFocus: false,
onError: (err) => errorSuccessActions.throwError(err),
onSuccess: (data) => {
queryClient.setQueryData(['user'], data);
},
initialData: () => {
const user: UserModel | undefined = queryClient.getQueryData('user')
if (user) {
return user
}
return undefined
}
})
}
This expects userId that I get from ´auth´ state. Problem is that I actually don't get it after signin function fires. I only get the state updated if I reload the page or i refocus on the tab.
I have a console.log that should log the userId but it always returns undefined. Only when I refocus on the window will it return the userId prompting to fetch the user.
I am not sure what am I missing to get the updated auth state and to get the userId right after I sign in.

Nexjs + SWR: API resolved without sending a response for /api/projects/<slug>, this may result in stalled requests

Since on first render I was not able to get the router.query I am passing the params from getServerSideProps as follows:
export async function getServerSideProps(context) {
return {
props: { params: context.params },
};
}
Then in the function am trying to do the API call but am getting the API stalled error
API resolved without sending a response for
/api/projects/nichole_robel23, this may result in stalled requests.
This is my code:
export default function Project({ params }) {
const { slug } = params;
let [projectData, setProjectData] = useState([]);
let [loading, setLoading] = useState(true);
const { data } = useSWR('http://localhost:3000/api/projects/' + slug);
useEffect(() => {
if (data) {
setProjectData(data.data.project);
setLoading(false);
}
}, [data]);
......
I have global SWRCofig as follows
<SWRConfig value={{ fetcher: (url) => axios(url).then(r => r.data) }}>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
Any way to solve the problem?
You are missing your fetcher–the function that accepts the key of SWR and returns the data, so the API is not being called.
You are also not returning a response correctly from the API–this is most likely a case of not waiting for a promise/async to be fulfilled correctly.
CLIENT
const fetcher = (...args) => fetch(...args).then((res) => res.json());
export default function Home({ params }) {
const { slug } = params;
const [projectData, setProjectData] = useState([]);
const [loading, setLoading] = useState(true);
const { data } = useSWR(`http://localhost:3000/api/projects/${slug}`, fetcher);
useEffect(() => {
if (data) {
setProjectData(data);
setLoading(false);
}
}, [data]);
API
const getData = () => {
return new Promise((resolve, reject) => {
// simulate delay
setTimeout(() => {
return resolve([{ name: 'luke' }, { name: 'darth' }]);
}, 2000);
});
}
export default async (req, res) => {
// below will result in: API resolved without sending a response for /api/projects/vader, this may result in stalled requests
// getData()
// .then((data) => {
// res.status(200).json(data);
// });
// better
const data = await getData();
res.status(200).json(data);
}

Ionic 4 LoadingController

I am trying to add a LoadingController to my Ionic 5 app.
With the below code, the loading spinner is appearing:
async presentLoading() {
const loading = await this.loadingCtrl.create({
message: 'Please wait...',
});
await loading.present();
}
getPosts() {
this.posts = [];
this.presentLoading();
query.get()
.then((docs) => {
docs.forEach((doc) => {
this.posts.push(doc);
})
}).catch((err) => {
console.log(err)
})
}
But I don't know how to dismiss the LoadingController once the posts array has been populated.
Can someone please show me how this is done?
You have to dismiss the controller. For that you will have to keep a reference to it, something like this,
async presentLoading() {
this.loading = await this.loadingCtrl.create({
message: 'Please wait...',
});
await this.loading.present();
}
getPosts() {
this.posts = [];
this.presentLoading();
query.get()
.then((docs) => {
docs.forEach((doc) => {
this.posts.push(doc);
this.loading.dismiss();
})
}).catch((err) => {
console.log(err)
})
}
If you need to get notice when the dismiss occurs, you can listen to onDidDismiss event.
Links:
Ionic Docs - LoadingController

How to call another process when AWS Context callbackWaitsForEmptyEventLoop = false

I'm using the best practice MongoDB with Lambda example from here https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/
I need to publish to SNS but are unable to due to the callbackWaitsForEmptyEventLoop = false, if I uncomment this, it works fine but then my Lambda function just times out and never receive the success callback.
"use strict";
const MongoClient = require('mongodb').MongoClient;
const MONGODB_URI = process.env.MONGODB_URI; // or Atlas connection string
const AWS = require('aws-sdk');
const SNS_TOPICARN = process.env.SNS_TOPICARN;
const sns = new AWS.SNS({ apiVersion: '2010-03-31' });
let cachedDb = null;
function connectToDatabase(uri) {
console.log('=> connect to database');
if (cachedDb) {
console.log('=> using cached database instance');
return Promise.resolve(cachedDb);
}
return MongoClient.connect(uri)
.then(db => {
cachedDb = db;
return cachedDb;
});
}
function queryDatabase(db) {
console.log('=> query database');
return db.collection('items').find({}).toArray()
.then((data) => { return { statusCode: 200, data: data }; })
.catch(err => {
console.log('=> an error occurred: ', err);
return { statusCode: 500, data: null };
});
}
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
console.log('event: ', event);
connectToDatabase(MONGODB_URI)
.then(db => queryDatabase(db))
.then(result => {
console.log('=> returning result: ', result);
var params = {
Message: result.data,
Subject: 'Devices Lost Connection',
TopicArn: SNS_TOPICARN
};
sns.publish(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
});
callback(null, result);
})
.catch(err => {
console.log('=> an error occurred: ', err);
callback(err);
});
};
Problem solved, will leave it here if someone else has the same issue:
I need to do the handler callback in the callback of the second function:
sns.publish(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
callback(null, result);
});

How to test axios interceptors using jest?

I'm trying to test the following code:
import axios from 'axios';
import { history } from './ReduxService';
axios.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code === 'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
}
);
Any advice on how to cover it?
First, modify the code so that you can pass a mocked version of axios in:
import axios, { AxiosInstance } from 'axios';
import { history } from './ReduxService';
export const addResponseInterceptor(client: AxiosInstance) => {
client.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code ===
'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
});
};
Then set up your tests like this:
import { addResponseInterceptor } from './yourInterceptorFile'
import axios from 'axios';
jest.mock('axios', () => {
return {
create: jest.fn(),
interceptors: {
request: {
use: jest.fn(),
eject: jest.fn(),
},
response: {
use: jest.fn(),
eject: jest.fn(),
},
}
};
});
describe('addResponseInterceptor tests', () => {
beforeEach(() => {
(axios.create as jest.Mock).mockReset();
(axios.interceptors.request.use as jest.Mock).mockReset();
(axios.interceptors.request.eject as jest.Mock).mockReset();
(axios.interceptors.response.use as jest.Mock).mockReset();
(axios.interceptors.response.eject as jest.Mock).mockReset();
});
it('should add a response interceptor to the axios instance', () => {
addResponseInterceptor(axios);
expect(axios.interceptors.response.use).toHaveBeenCalled();
});
it('should push to history when an error occurs', async() => {
const historySpy = jest.spyOn(history, 'push');
const axiosClient = axios;
addResponseInterceptor(axios);
const interceptorErrorHandler = (axiosClient.interceptors.response.use as jest.Mock).mock.calls[0][1];
try {
await interceptorErrorHandler({
response: {
status: 408
}
});
//this should not be called--the promise should be rejected
expect(true).toBe(false);
} catch {
expect(historySpy).toHaveBeenCalledWith('/error');
}
});
. . .
});