I would just like to ask for suggestions and ideas about my current issue , I have a method which is getUserRoles which is fetching the logged-in user roles list.
The problem I have is that there are instances that the getUserGeneralDetails from the getUserRoles that is causing infinity , it is calling the getUserGeneralDetails infinitely and I dont understand why and what causes it.
Maybe somone can enlighten me with some problem with the code snippet I have below. Would be appreciatd, Thanks.
#method
//In this method, we are just fetching the logged-in user roles list.
public getUserRoles(): Promise<string[]> {
if (this.authUser === null) {
this.setAuthUser()
}
return new Promise((resolve, reject) => {
this.userService
.getUserGeneralDetails(this.authUser.nameid , this.accountId = 0)
.pipe(catchError((error: any, caught: any) => {
reject(error);
return caught;
}),
map((res: any) => res.data && res.data.userAccountDto
.filter(account => account.accountName === res.data.associatedAccount)
.map(account => account.userRoleDto.roleName)
)).subscribe((role: string[]) => {
resolve(role);
});
});
}
#code that calls the getUserRoles method
this.authService
.loginWithCredentials(username, password)
.then((r: any) => {
localStorage.removeItem("previousUrl");
if (r.isSuccess) {
this.authService.getUserRoles().then((userRoles: string[]) => {
localStorage.setItem("userRoles", JSON.stringify(userRoles));
})
this.route.navigateByUrl('/');
}else{
if(r.errors.length > 0){
if(r.errors[0].description.indexOf('not found') !== -1){
errMsg = ERR_FORM_MSG.INVALID_LOGIN;
}
}
this.apiErrMsg = errMsg;
}
})
.catch((e): any => {
this.apiErrMsg = e.message
? e.message
: errMsg;
})
.finally(() => {
this.hasSubmit = false;
this.closeLoader();
});
}
#user details
getUserGeneralDetails(id: number , accountId:number): Observable<any> {
const params = new HttpParams()
.set('id', id.toString())
.set('accountId', accountId.toString());
return this.httpRequestService.get<any>(`${apiBaseUrl}/detail` , params);
}
Related
I'm new to a course of Angular with rxjs and our group have received a challenge.
They've sent us a chunk of code and said that althought it works fine, the code is not clean and the function "getUserLogin" has some duplicity in comparison with the "login" method. So, the challenge is to identify it and remove this duplicity.
Here is the code:
login(): Observable<UserModel> {
if (!sessionStorage["userCredentials"]){
return this.http.get<UserModel>(`${environment.apiLogin}/user/login?sistema=${enviroment.system}`,
{ withCredentials: true })
.pipe(
tap((res: UserModel) => {
sessionStorage["userCredentials"] = JSON.stringify(res);
})
);
} else {
return Observable.of(<UserModel>JSON.parse(sessionStorage["userCredentials"]));
}
}
getUserLogin(): Observable<string> {
if (sessionStorage["userCredentials"]){
const userData = <UserModel>JSON.parse(sessionStorage["userCredentials"])
return Observable.of(userData.login);
}
return Observable.of("");
}
Any help in solving this challenge would be very appreciated. Thanks in advance!
Something like this:
login(): Observable<UserModel> {
const cred = this.getUserCredentialsFromStorage();
if (!cred){
return this.http.get<UserModel>(`${environment.apiLogin}/user/login?sistema=${enviroment.system}`,
{ withCredentials: true })
.pipe(
tap((res: UserModel) => {
sessionStorage["userCredentials"] = JSON.stringify(res);
}));
}
return Observable.of(cred);
}
getUserCredentialsFromStorage(): UserModel {
if (sessionStorage["userCredentials"]) {
const userData = <UserModel>JSON.parse(sessionStorage["userCredentials"]);
return userData;
}
return null;
}
getUserLogin(): Observable<string> {
const cred = getUserCredentialsFromStorage();
return Observable.of(cred ? cred.login : '');
}
Note that in refactoring this, you need to pay careful attention to types.
Both getUserLogin and login parse the sessionStorage object to get a UserModel, then do different things with it. One returns an Observable<UserModel> and the other extracts the login string and returns an Observable<string>. So we can refactor out a function that parses the UserModel and have both functions call that.
From the question, the only thing I can infer is that there are duplicate checks for sessionStorage value in both the functions.
IMO below should be the revised code
login(): Observable<UserModel> {
this.getUserLogin().mergeMap(res => {
if (res === '') {
return this.http.get<UserModel>(`${environment.apiLogin}/user/login?sistema=${enviroment.system}`,
{ withCredentials: true })
.pipe(
tap((res: UserModel) => {
sessionStorage["userCredentials"] = JSON.stringify(res);
this.userData(sessionStorage["userCredentials"])
})
);
} else {
this.userData(sessionStorage["userCredentials"])
}
})
}
getUserLogin(): Observable<string> {
if (sessionStorage["userCredentials"]) {
this.userData(sessionStorage["userCredentials"])
}
return Observable.of("");
}
userData(data) {
const userData = <UserModel>JSON.parse(data)
return Observable.of(userData.login);
}
I am trying to send an http.post request for each element of an array, my method works well, but when I subscribe, it does it for each of the requests, if someone could help me optimize this, I will I would really appreciate it, here I leave the snippets of my code.
component.ts
saveExclusion() {
this.indForm.value.Centers.forEach(element => {
for (const days of this.exclusionDays) {
delete days.horadesde;
delete days.horahasta;
delete days.id;
for (const key in days) {
if (days[key] === true) {
days[key] = true;
}else if (days[key] === false) {
delete days[key];
}
}
}
const valueForm = this.indForm.value;
valueForm.ResourceId = this.idResource;
valueForm.TimeZoneId = 'America/Santiago';
valueForm.CenterId = element;
this.exclusionFunc = false;
this.apiFca.saveNew(valueForm, this.exclusionDays)
.pipe(last()).subscribe((res: any) => {
console.log(res)
if (res === '200') {
this.successMessage = true;
this.exclusionDays = [];
this.indForm.reset();
this.ngOnInit();
setTimeout(() => {
this.successMessage = false;
}, 3000);
}
}, err => {
console.log('error', err);
});
});
}
service.ts
saveNew(exclusionData, daysBlock) {
let reason = '';
const dt = new Date();
const n = dt.getTimezoneOffset();
const tz = new Date(n * 1000).toISOString().substr(14, 5);
if (exclusionData.OtherReason) {
reason = exclusionData.ExclusionReason + ' ' + exclusionData.OtherReason;
} else {
reason = exclusionData.ExclusionReason;
}
if (exclusionData.ExclusionType !== 'Partial' ) {
daysBlock = [];
}
const data = {Exclusion: new ExclusionClass(
[],
reason,
exclusionData.ExclusionType,
exclusionData.Repetition,
exclusionData.CenterId,
exclusionData.ProfessionalName,
exclusionData.ResourceId,
daysBlock,
exclusionData.TimeZoneId,
'Exclude',
exclusionData.Unit,
exclusionData.ValidFrom + 'T' + exclusionData.ValidTimeFrom + ':00-' + tz,
exclusionData.ValidTo + 'T' + exclusionData.ValidTimeUntil + ':59.999-' + tz
)};
if (exclusionData.CenterId === '') {
delete data.Exclusion.CenterId;
}
return this.http
.post("url", data)
.pipe(
map((res: any) => {
return res.code;
})
);
}
greetings, and I look forward to your comments, thanks.
I'm not fully confident in my rxjs knowledge but it looks like, because of .pipe(last()), you are only watching the last request? I'd recommend you only set success if all completed without error, like
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
console.log(res);
},
err => {
console.log(err);
},
() => {
this.successMessage = true;
// etc. etc. etc.
});
or maybe instead of using this.successMessage use something like this.saveState$ that would be the a BehaviorSubject object initialized with 'idle' (or some enum thereof) that your saveExclusion() function manages. That way, the beginning of your saveExclusion() function could
set const saveState$ = this.saveState$
assert that saveState$.getValue() === 'in process' or if not, do something about it,
saveState$.next('in process');
and you could change your subscribe line to
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
if (res !== '200') {
saveState$.next('unexpected result')
} },
err => {
console.log(err);
saveState$.next('error');
},
() => {
if (saveState$.getValue() === 'in process') {
saveState$.next('success');
} }
);
And then you can subscribe to your component's saveState$ as well (though outside of the component you'd want to provide saveState$.asObservable() so values can't be injected by outside code). This affords some elegant event-driven code in your component initialization:
saveState$.pipe(filter(val => val === 'error'))
.subscribe(functionToTellYourUserThereWasAnError);
// if successful, we want other code to know, but immediately change it back to 'idle' even if other code errors
saveState$.pipe(filter(val => val === 'success')
.subscribe(val => saveState$.next('idle'));
// upon success, reset me
saveState$.pipe(filter(val => val === 'success'))
.subscribe(
val => {
this.exclusionDays = [];
// etc. etc.
// setTimeout not needed because set to 'idle' in a different thread.
}
)
Plus, I think your template could reflect and change the UI in response to changes in saveState$ as well, so your save button can be enabled/disabled based on whether or not saveState is 'idle', etc.
I'm trying to delete all the users in my auth and database using firebase functions. Here's my code for that:
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "----"
});
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '--';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
adminDeleteAllUsers();
}
}
});
//Admin control
function adminDeleteAllUsers() {
deleteAllUsers(' ');
return null;
}
function deleteAllUsers(nextPageToken: any) {
admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
})
.catch((error: any) => {
console.log('Error deleting user:', error);
});
db.collection('users').doc(userUid).delete();
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch((error: any) => {
console.log('Error listing users:', error);
});
}
When the function get executed, no user is deleted. It's like the function never worked. Am I missing something?
Update:
I'm not sure if this is the way to do it, but it's still not working. I tried to handle promises correctly, but I'm not sure if what I'm doing is correct or not.
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '---';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
return adminDeleteAllUsers();
}
return;
}
return;
});
//Admin control
function adminDeleteAllUsers() {
return deleteAllUsers(' ');
}
function deleteAllUsers(nextPageToken: any) {
return admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
return admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
return db.collection('users').doc(userUid).delete();
})
.catch((error: any) => {
console.log('Error deleting user:', error);
return;
});
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
return;
})
.catch((error: any) => {
console.log('Error listing users:', error);
return;
});
}
After logging in the scheduleRefresh() function is called and it will continuously refresh the token. The problem occurs when I refresh the page, or when an authenticated user tries accessing the web page again. startupTokenRefresh is called on startup, the token is refreshed, but the token is never scheduled to be refreshed again like its suppose to. If I refresh the page after I know the token has expired I get a token_not_provided error but I can see in the network console the token is being refreshed a couple of seconds after this error or the page has loaded. If I refresh again the request to my api is made fine but again the token does not refresh after its expired.
What am I doing wrong?
app.component.ts (on app start up)
platform.ready().then(() => {
storage.ready().then(() => storage.get('token'))
.then(token => {
storage.set('token', token);
authService.token = token;
authService.authNotifier.next(true);
authService.checkToken();
authService.startupTokenRefresh();
});
authService.authenticationNotifier().subscribe((authed) => {
if (authed) {
this.rootPage = TabsPage;
} else {
authService.logout();
this.rootPage = LoginPage;
}
});
}
auth.service.ts
jwtHelper: JwtHelper = new JwtHelper();
token;
refreshSubscription: any;
authNotifier: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);
authenticationNotifier(): Observable<boolean> {
return this.authNotifier;
}
refresh(): Observable<any> {
console.log("in refresh()");
let URL = `${myApi}/refresh?token=${this.token}`;
return this.authHttp.get(URL)
.map((rsp) => {
this.token = rsp.json().token;
this.storage.ready().then(() => this.storage.set('token', this.token));
this.authNotifier.next(true);
return rsp.json().token;
},
err => {
this.authNotifier.next(false);
this.logout();
console.log(err);
})
.share();
}
checkToken() {
if (this.token === '' || this.token === null || this.token === undefined) {
this.authNotifier.next(false);
this.logout();
}
}
public scheduleRefresh() {
if (this.token === '' || this.token === null || this.token === undefined) {
this.authNotifier.next(false);
this.logout();
}
else {
// If the user is authenticated, use the token stream provided by angular2-jwt and flatMap the token
let source = this.authHttp.tokenStream.flatMap(
token => {
let jwtIat = this.jwtHelper.decodeToken(this.token).iat;
let jwtExp = this.jwtHelper.decodeToken(this.token).exp;
let iat = new Date(0);
let exp = new Date(0);
let delay = (exp.setUTCSeconds(jwtExp) - iat.setUTCSeconds(jwtIat));
return Observable.interval(delay);
});
this.refreshSubscription = source.subscribe(() => {
this.refresh().subscribe((res) => console.log('-> Refreshed...'),
(error) => console.log('Refresh error: ' + JSON.stringify(error)))
});
}
}
public startupTokenRefresh() {
if (this.token === '' || this.token === null || this.token === undefined) {
this.authNotifier.next(false);
this.logout();
}
else {
// Get the expiry time to generate a delay in milliseconds
let now: number = new Date().valueOf() / 1000;
let jwtExp: number = this.jwtHelper.decodeToken(this.token).exp;
let iat: number = this.jwtHelper.decodeToken(this.token).iat;
let refreshTokenThreshold = 10; //seconds
let delay: number = jwtExp - now;
let totalLife: number = (jwtExp - iat);
(delay < refreshTokenThreshold ) ? delay = 1 : delay = delay - refreshTokenThreshold;
// Use the delay in a timer to // run the refresh at the proper time
return Observable.timer(delay * 1000);
});
// Once the delay time from above is reached, get a new JWT and schedule additional refreshes
source.subscribe(() => {
this.refresh().subscribe(
(res) => {
console.log('-> Refreshed on startup');
this.scheduleRefresh();
},
(error) => console.log('-> Refresh error:' + JSON.stringify(error)))
});
}
}
public unscheduleRefresh() {
console.log("unsched");
if (this.refreshSubscription) {
this.refreshSubscription.unsubscribe();
}
}
login.ts
onLogin() {
this.authService.login(this.loginForm.value.username, this.loginForm.value.password)
.subscribe(
(response) => {
this.storage.ready().then(() => this.storage.set('token', response.token));
this.authService.token = response.token;
this.authService.authNotifier.next(true);
},
error => {
console.log(error);
this.loginError = true;
this.authService.authNotifier.next(false);
},
() => {
console.log("login success");
this.authService.scheduleRefresh();
this.navCtrl.push(TabsPage);
},
);
}
}
I am quite new to typescript and have following issue.
I have following code where I can't reach console.log('uid....' + uid); when the uid is null
deleteUser(email: string): Promise<User> {
let uid: string;
let p = new Promise<User>((resolve) => {
this.loginService.getUserIdByEmail(email).then((getUserResponse) => {
**********************************
******** do not reach here when getUserResponse is NULL**********************
************************
uid = getUserResponse;
console.log('uid....' + uid);
if (uid !== null) {
let actions = [
this.siteUserTableService.deleteByRowKey(uid),
this.userSiteTableService.deleteById(uid),
];
Promise.all(actions.map(this.reflect)).then(values => {
this.tableService.deleteById(uid).then((deleteResponse) => {
this.loginService.deleteUserByEmail(email);
});
});
}
}).catch((err) => {
// Something wrong
});
});
return p;
};
getUserIdByEmail(email: string): Promise<string> {
let searchParams = {
query: 'select UID from accounts where profile.email = "' + email + '"'
};
let p = new Promise<string>((resolve, reject) => {
login.accounts.search(searchParams).then((response) => {
if (response.errorCode === 0 && response.results.length > 0) {
resolve(response.results[0].UID);
} else {
console.log('No login user...');
reject('');
}
});
});
};
As I am not getting a return value from getUserIdByEmail function (return null) I cannot proceed with deleteUser function.
So how can I proceed even with null return value.
Thanks