Create reducer about user state - ngrx

I'm trying to apply reflux/ngrx on my current front-end project.
I want to take advantage of this in order to change a slight functionality: Change current user related tasks in order to use a single user state.
Current user related tasks: Currently, I'm using an traditional model in order to achieve user login process... UserService is able to check user credentials. Once it's been checked I store user information on an AppService:
export class LoginComponent implements OnInit {
private fb: FormBuilder;
private form:FormGroup;
private commty: UsersService;
private router: Router;
private appState: AppState;
private alerts: Array<Object>;
constructor()
{
this.alerts = [];
}
ngOnInit():void {
this.form = this.fb.group({
user: ['', Validators.required],
passwd: ['', Validators.minLength(6)]
});
}
public checkPasswd():void {
this.clearAlerts();
this.commty.checkPasswd(this.form.value.mail, this.form.value.passwd)
.subscribe(
(result: any) => {
this.appState.user = result;
this.router.navigate(['/app']);
},
(error: any) => {
this.addAlert(error.message);
}
);
}
private addAlert(message: string): void {
this.alerts.push({type: 'danger', msg: message});
}
public closeAlert(index): void {
this.alerts.splice(index, 1);
};
private clearAlerts(): void {
this.alerts.splice(0, this.alerts.length);
}
}
I'm a bit confused about how to move this code in order to use reflux/ngrx. I'ce read a bit about this topic, nevertheless I'm not quite able to figure out how to move my code. Up to now, I've created an single Store and User interfaces:
store.interface.ts:
export interface IStore {
user: IUser
sources: ISourceRedux;
}
user.interfcae.ts:
export interface IUser {
id: string;
name: string;
username: string;
customer: string;
}
The next step I think I need to do is to create reducers. This step is which I don't quite understand how build this code. Up to now
user.initialstate.ts:
export function initialUserState(): IUser {
return {
id: '',
name: '',
username: '',
customer: '',
sources: []
};
};
user.reducer.ts
export class User {
private static reducerName = 'USER_REDUCER';
public static reducer(user = initialUserState(), {type, payload}: Action) {
if (typeof User.mapActionsToMethod[type] === 'undefined') {
return user;
}
return User.mapActionsToMethod[type](user, type, payload);
}
// ---------------------------------------------------------------
// tslint:disable-next-line:member-ordering
private static mapActionsToMethod = {};
}
Which reducers I should create in order to:
Check credentials.
If credentials are right get this user and update User state store.
If credentials are wrong inform the process has failed.
Perhaps I'm merging concepts... I need some lights...
EDIT
public connect(user: string, currentPasswd: string, extraHttpRequestParams?: any): Observable<UserDTO> {
return this.checkPasswdWithHttpInfo(id, currentPasswd, extraHttpRequestParams)
.map((response: Response) => {
if (response.status === 204) {
return undefined;
} else {
return response.json();
}
}).catch((error: any) => {
if (error.status >= 500) {
return Observable.throw(new Error(error.status));
}
else { //if (error.status >= 400) {
const body = error.json() || '';
const code = body.error || JSON.stringify(body);
const message = body.message || JSON.stringify(body);
return Observable.throw(ApiError.create(code, message));
}
});
}

Ok so this is the next question of your "Integrate ngrx into my code" =).
What you're looking for is : https://github.com/ngrx/effects
The idea behind effects is that an effect let you catch an Action, do side effect (API call or whatever) and you can then dispatch another Action (often success or error).
Flow example to connect a user :
--| [from component] Dispatch action USER_CONNECT
--| [from user.effect.ts]
----| Catch action ofType('USER_CONNECT')
----| Do what you need to do (API call for ex)
----| When the response comes back :
------| If success : Dispatch USER_CONNECT_SUCCESS
------| If error : Dispatch USER_CONNECT_ERROR
Of course when you dispatch either USER_CONNECT_SUCCESS or USER_CONNECT_ERROR you can pass additional data in the payload (for example user information or the error).
Here's a full example :
#Injectable()
export class UsersEffects {
constructor(
private _actions$: Actions,
private _store$: Store<IStore>,
private _userService: UserService,
) { }
#Effect({ dispatch: true }) userConnect$: Observable<Action> = this._actions$
.ofType('USER_CONNECT')
.switchMap((action: Action) =>
this._userService.connect(action.payload.username, action.payload.password)
.map((res: Response) => {
if (!res.ok) {
throw new Error('Error while connecting user !');
}
const rslt = res.json();
return { type: 'USER_CONNECT_SUCCESS', payload: rslt };
})
.catch((err) => {
if (environment.debug) {
console.group();
console.warn('Error catched in users.effects.ts : ofType(USER_CONNECT)');
console.error(err);
console.groupEnd();
}
return Observable.of({
type: 'USER_CONNECT_ERROR',
payload: { error: err }
});
})
);
}
You can take a look into my project Pizza-Sync were I did something similar (except that I don't catch in case of error and do not dispatch if there's an error).

Related

Firestore Angular2 Retrieve documents based on current user

I have started developing a mobile app using IONIC, ANGULAR against Google Firestore. This app will consume mostly documents based on the current user and most of my queries I will need to pass in this user. However, I am experiencing issues getting documents from firestore using the following code from my service to the page:
user-profile.service.ts
async get(){
// await this.afAuth.user.subscribe(currentUser => {
// if(currentUser){
// this.userId = currentUser.uid;
// console.log("User Current ID: " + this.userId);
console.log("PP:" +this.afAuth.auth.currentUser.uid)
return this.userProfilesCollection.doc<UserProfile>(this.afAuth.auth.currentUser.uid).snapshotChanges().pipe(
map(doc => {
const id = doc.payload.id;
const data = doc.payload.data();
return{id, ...data };
}
)
);
}
landing-page.page.ts
export class LandingPage implements OnInit {
userProfile : UserProfile;
constructor(
private authService : AuthService,
private loadingController : LoadingController,
private userProfileService: UserProfileService,
private router : Router
) {
}
ngOnInit() {
this.loadUserProfile();
}
async loadUserProfile() {
const loading = await this.loadingController.create({
message: 'Loading user profile'
});
await loading.present();
this.userProfileService.get().then(res=>{
console.log(res);
loading.dismiss();
})
// this.userProfileService.get().then(
// res =>
// {
// loading.dismiss();
// res.subscribe(c=>
// {
// this.userProfile = {
// cellphone: c.data.cellphone,
// dateOfBirth: c.data.dateOfBirth,
// email: c.data.email,
// firstname: c.data.firstname,
// gender: c.data.gender,
// id: c.data.id,
// lastname: c.data.lastname,
// telephone: c.data.telephone,
// userId: c.data.userId,
// website: c.data.website
// };
// });
// }
// );
}
}
Does anyone have a simple example how to achieve this, and I need to use the load profile to navigate across the application as the currently logged in user will be able to manage their profile and the list items (documents) linked to them?

How to test asynchonous functions using sinon?

I have a class called PostController, and I trying to test the following function create:
class PostController {
constructor(Post) {
this.Post = Post;
}
async create(req, res) {
try {
this.validFieldRequireds(req);
const post = new this.Post(req.body);
post.user = req.user;
...some validations here
await post.save();
return res.status(201).send(message.success.default);
} catch (err) {
console.error(err.message);
const msg = err.name === 'AppError' ? err.message :
message.error.default;
return res.status(422).send(msg);
}
}
My test class is:
import sinon from 'sinon';
import PostController from '../../../src/controllers/posts';
import Post from '../../../src/models/post';
describe('Controller: Post', async () => {
it.only('should call send with sucess message', () => {
const request = {
user: '56cb91bdc3464f14678934ca',
body: {
type: 'Venda',
tradeFiatMinValue: '1',
... some more attributes here
},
};
const response = {
send: sinon.spy(),
status: sinon.stub(),
};
response.status.withArgs(201).returns(response);
sinon.stub(Post.prototype, 'save');
const postController = new PostController(Post);
return postController.create(request, response).then(() => {
sinon.assert.calledWith(response.send);
});
});
});
But I'm getting the following error:
Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure
"done()"
is called; if returning a Promise, ensure it resolves.
(D:\projeto\mestrado\localbonnum-back-end\test\unit\controllers\post_spec.js)
Why?
Most probably it's because misuse of sinon.stub.
You've
sinon.stub(Post.prototype, 'save');
without telling what this stub will do, so in principle this stub will do nothing (meaning it returns undefined).
IDK, why you don't see other like attempt to await on stub.
Nevertheless, you should properly configuture 'save' stub - for example like this:
const saveStub = sinon.stub(Post.prototype, 'save');
saveStub.resolves({foo: "bar"});

Object data null sms still sent twilio authy

Im trying to implement the authy-node phone verification with firebase functions and my app in react-native the message is sent to the correct mobile phone but for some reason the data I get back from the api is null any ideas out there
My Api firebase functions
import * as functions from 'firebase-functions';
const authy = require('authy')('mySecret');
export const getCode = functions.https.onCall((data, context) => {
const {
number, countryCode
} = data;
return authy.phones().verification_start(number, countryCode, { via:
'sms', locale: 'en', code_length: '4' }, (err: any, res: any) => {
if (err) {
throw new functions.https.HttpsError(err);
}
return res;
});
});
and this is my call from my app
export default class test extends Component {
constructor() {
super();
}
componentWillMount() {
const getCode = firebase.functions().httpsCallable('getCode');
getCode({number: 'theCorrectNumber', countryCode: '44'})
.then(function (result) {
const data = result;
console.log(data)
}).catch( function (error){
console.log(error)
})
}
render() {
return (
<View/>
);
}
}
Twilio developer evangelist here.
From what I can see in the Authy Node library that I'm assuming you're using, making a request to the API does not return a Promise. Instead it is built with request and responds to asynchronous requests using callbacks only. You do deal with the callback, but you are returning the result of calling the asynchronous function, which is null, rather than the result from the callback.
Perhaps including a callback as part of the function call would work better:
import * as functions from 'firebase-functions';
const authy = require('authy')('mySecret');
export const getCode = functions.https.onCall((data, callback) => {
const { number, countryCode } = data;
return authy
.phones()
.verification_start(
number,
countryCode,
{ via: 'sms', locale: 'en', code_length: '4' },
callback
);
});
You can then use it like this:
export default class test extends Component {
constructor() {
super();
}
componentWillMount() {
const getCode = firebase.functions().httpsCallable('getCode');
getCode({ number: 'theCorrectNumber', countryCode: '44' }, (err, res) => {
if (err) {
throw new functions.https.HttpsError(err);
}
const data = res;
console.log(data);
});
}
render() {
return <View />;
}
}
Let me know if that helps at all.

Angular 2 private variables disappear

I have the following code which is a simple service that goes back to the server to fetch some data:
import { Injectable } from '#angular/core';
import { Action } from '../shared';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Authenticated } from '../authenticated';
import 'rxjs/Rx';
#Injectable()
export class ActionsService {
private url = 'http://localhost/api/actions';
constructor(private http: Http, private authenticated : Authenticated) {}
getActions(search:string): Observable<Action[]> {
let options = this.getOptions(false);
let queryString = `?page=1&size=10&search=${search}`;
return this.http.get(`${this.url + queryString}`, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(response: Response) {
let body = response.json();
return body || { };
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
if (error.status == 403) {
this.authenticated.logout();
}
return Observable.throw(errMsg);
}
private getOptions(addContentType: boolean) : RequestOptions {
let headers = new Headers();
if (addContentType) {
headers.append('Content-Type', 'application/json');
}
let authToken = JSON.parse(localStorage.getItem('auth_token'));
headers.append('Authorization', `Bearer ${authToken.access_token}`);
return new RequestOptions({ headers: headers });
}
}
Everything works as expected except for handleError. As soon as getActions receives an error from the server it goes into the this.handleError method which again works fine up until the section where this.authenticated.logout() should be called. this.autenticated is undefined and I am not sure if it is because "this" is referring to another object or if ActionSerivce's local variables are made null when and http exception occurs. The authenticated local variable is properly injected (I did a console.log in the constructor and it was there).
The problem is that you are not binding the this context in your callback function. You should declare your http call like this for example:
return this.http.get(`${this.url + queryString}`, options)
.map(this.extractData.bind(this)) //bind
.catch(this.handleError.bind(this)); //bind
Another option could be to pass an anonymous function and call the callback from there:
return this.http.get(`${this.url + queryString}`, options)
.map((result) => { return this.extractData(result)})
.catch((result) => { return this.handleError(result}));
And yet another option is to declare your callback functions a little differently, you can keep your http call the way you had it before:
private extractData: Function = (response: Response): any => {
let body = response.json();
return body || { };
}
private handleError: Function = (error: any): any => {
//...
}

Angular2 Call login function from service in component and return error

I'm trying to call a service HTTP method and eventually return an error message but after a week of trying many things (Promises, Observables, ...) I can't get it to work. I hope anybody can help me out?
I'm kind of new to Angular2 and working alone on this project, with no one else around me with any Angular expertise. I did get a 3-day training course.
Component
#Component({
templateUrl: 'build/pages/login/login.html'
})
export class LoginPage {
error: string;
constructor(private navController: NavController, private auth: AuthService) {
}
private login(credentials) {
// Method calling the login service
// Could return an error, or nothing
this.error = this.auth.login(credentials);
// If there is no error and the user is set, go to other page
// This check is executed before previous login methode is finished...
if (!this.error && this.auth.user) {
this.navController.setRoot(OverviewPage);
}
}
}
AuthService
#Injectable()
export class AuthService {
private LOGIN_URL: string = "http://localhost:8080/rest/auth";
private USER_URL: string = "http://localhost:8080/rest/user";
private contentHeader: Headers = new Headers({
"Content-Type": "application/json"
});
errorMessage: string;
user: User;
constructor(private http: Http) {
}
login(credentials) {
let contentHeader = new Headers({
"Content-Type": "application/json"
});
this.http.post(this.LOGIN_URL, JSON.stringify(credentials), { headers: contentHeader })
.map(res => res.json())
.catch(this.handleError)
.subscribe(
data => this.handleLogin(data),
err => this.handleError
);
// could return an errorMessage or nothing/null
return this.errorMessage;
}
private handleLogin(data) {
let token = data.token;
this.getAccount(token);
}
private getAccount(token) {
let authHeader = new Headers({
"Content-Type": "application/json",
"X-Auth-Token": token
});
this.http.get(this.USER_URL, { headers: authHeader })
.map(res => res.json())
.catch(this.handleError)
.subscribe(
data => this.setUser(data),
err => this.errorMessage = err
);
}
private setUser(data) {
this.user = new User(data.naam, data.voornaam);
}
private handleError(error) {
// this.errorMessage is not saved?
if (error.status === 401) {
this.errorMessage = '401';
} else if (error.status === 404) {
this.errorMessage = '404';
} else {
this.errorMessage = 'Server error';
}
return Observable.throw(error.json() || 'Server error');
}
}
I think your problem is that your login method is returning a flat value (errorMessage). Since the login method is making an asynchronous request that value will not be initialized, it will always return null. If I were to set this up I would have the login method return an Observable.
Then to make things a bit more complicated it appears you want to make a consecutive call after login to get the logged in user. If you don't want your login method to emit until you've completed both calls you have to combine them somehow. I think switch can do this.
#Injectable()
export class AuthService {
private LOGIN_URL: string = "http://localhost:8080/rest/auth";
private USER_URL: string = "http://localhost:8080/rest/user";
private contentHeader: Headers = new Headers({
"Content-Type": "application/json"
});
user: User;
constructor(private http: Http) {
}
login(credentials) {
let contentHeader = new Headers({
"Content-Type": "application/json"
});
let response:Observable<Response> = this.http.post(this.LOGIN_URL, JSON.stringify(credentials), { headers: contentHeader });
//Take response and turn it into either a JSON object or
//a string error.
//This is an Observable<any> (any is returned by json())
let jsonResponse = response.map(res => res.json())
.catch(err => this.handleError(err));
//Take JSON object and turn it into an Observable of whatever the
//login request returns
//This is an Observable<Observable<any>> (Observable<any> is returned
//by handleLogin
let userResponse = jsonResponse.map(
data => this.handleLogin(data)
);
//Switch to the observable of the login request
//This is an Observable<any>, we will switch to the Observable<any>
//returned by handleLogin
let finalResponse = userResponse.switch();
//Hide actual response value from user. This will return an
//observable that will emit null on success and an error message
//on error
//Again, an Observable<any> since we're mapping to null
return finalResponse.map(res => null);
}
//We need to return this call as an observable so we can wire it into
//our chain
private handleLogin(data) {
let token = data.token;
return this.getAccount(token);
}
private getAccount(token) {
let authHeader = new Headers({
"Content-Type": "application/json",
"X-Auth-Token": token
});
let loginResponse = this.http.get(this.USER_URL, { headers: authHeader })
.map(res => res.json())
.catch((err) => this.handleError(err));
loginResponse.subscribe(
data => this.setUser(data)
);
return loginResponse;
}
private setUser(data) {
this.user = new User(data.naam, data.voornaam);
}
private handleError(error) {
let errorMessage = "Uninitialized";
if (error.status === 401) {
errorMessage = '401';
} else if (error.status === 404) {
errorMessage = '404';
} else {
errorMessage = error.json() || 'Server error';
}
return Observable.throw(errorMessage);
}
}
Now in your login component you will need to listen asynchronously to the response. This won't happen immediately (probably pretty quick with localhost, but may take a while in the real world) so I've added a loginDisabled that you can use to prevent the user from hitting the login button twice while waiting for the login request to be fulfilled.
#Component({
templateUrl: 'build/pages/login/login.html'
})
export class LoginPage {
error: string;
loginDisabled:boolean = false;
constructor(private navController: NavController, private auth: AuthService) {
}
private login(credentials) {
// Method calling the login service
// Could return an error, or nothing
this.loginDisabled = true;
this.auth.login(credentials).subscribe(
rsp => {
//On success, navigate to overview page
this.navController.setRoot(OverviewPage);
}, err => {
//On failure, display error message
this.error = err;
this.loginDisabled = false;
});
}
}
No promises this is all correct (I don't have anything to test it against) but it should be the right general direction.

Resources