NativeScript Firebase plugin execution order - angularjs

I'm learning NativeScript/Angular 2 and would need to get help with this issue.
In order to implement a multi-role login system within the Firebase platform I thought about this solution
Login the user through Firebase authentication
Query the /stores/ path for a store which has a merchantEmail field same as the e-mail that has just logged in
If I find it, I set the store ID inside a BackendService service which uses getString/setString to store tokens, then route to a MerchantDashboardComponent
If I don't find it, just route to a BuyerDashboardComponent
This is part of my code in the login.service:
login (email: string, password: string) {
return firebase.login({
type: firebase.LoginType.PASSWORD,
email: email,
password: password
}).then(
(result: any) => {
firebase.query(
(_result) => { // Here I set BackendService.storeID
Inside the .query() callback I am assigning the tokens I need in the application.
This is the method I'm using in my login.component:
doLogin () {
this.isAuthenticating = true;
if (!this.validateEmail()) {
alert("Please insert a valid email");
return false;
}
this.loginService.login(this.email, this.password).then(
() => {
this.isAuthenticating = false;
if (BackendService.loginError)
alert(BackendService.loginError)
else if (BackendService.storeID != '') {
this.router.navigate(['/merchant-dashboard'], {clearHistory: true});
}
else {
this.router.navigate(['/home/categories'], {clearHistory: true});
}
}
);
}
Everything works except for the fact that the Merchant gets routed to the Buyer dashboard. I've managed to discover that the execution order is not what I expected to be, in fact:
firebase.login() gets executed and returns a Promise
.then() handler is executed inside the doLogin() method
Only after this, the firebase.query() method completes the callback and my tokens are available, but doLogin() has already navigated the user because storeID is still empty when I need it
I hope I've been clear as much as possible.
Thanks for your attention.
Greetings,
Davide

So, the problem was in the login service method.
I now return the Promise generated by firebase.query(), which causes then() calls to chain in the correct order.

Yep that was exactly was I was going to propose to wrap it in a promise and create a chain.
example code
return new Promise<any>((resolve, reject) => {
firebase.login({ loginArguments })
.then((result: any) => {
var onQueryEvent = function (result) {
};
return firebase.query(
onQueryEvent,
"/owner",
{
// query arguments follows here
}
).then(res => {
return res;
})
})
.then(finalResult => {
console.log(finalResult);
try {
resolve(finalResult);
} catch (e) {
reject(e);
}
})
});

Related

SvelteKit form get request

I am trying to set up a simple endpoint in SvelteKit that reads the input given in a form and returns the result of an SQL query. In my first attempt I used the form actions and wrote the following code in the +page.server.js file:
export const actions = {
default: async (event) => {
let form_input = await event.request.formData();
let query = {
text: "select * from ux_return_shipments($1, $2, $3, $4)",
values: [form_input.get('beg-dep-date') || null, form_input.get('end-dep-date') || null, form_input.get('beg-arr-date') || null, form_input.get('end-arr-date') || null
}
try {
const result = await event.locals.pool.query(query);
return result.rows;
} catch (e) {
console.log(e);
}
}
};
I am now trying to set up the same process using a GET request instead of a POST one but I am having difficulties setting up the endpoint. I tried to replace the above code with this template but it looks like the endpoint is not getting activated since I see no activity server side:
export function GET({ url }) {
console.log(url);
return new Response("Test response");
};
What am I doing wrong? I see that using this code for an API endpoint (+server.js file) works correctly. I also checked the form element and the URL looks correct.
In case someone has the same problem I managed to solve it using this template:
export const load = async (event) => {
return ...
};
Using the load function I was able to set up a get endpoint and pass some data to the frontend using a return.

How to use navigator.credentials to store passwords in a React application?

In a react app I need to access MySQL servers, for which I need the user's credentials. In order to avoid having the user enter them every time a connection is opened I'd like to store the password in the browser's password store/manager.
According to MDN all major browsers should support the Credentials Management API. So I tried it like this:
private requestPassword = (commandId: string, args?: any[]): void => {
if (args && args.length > 0) {
// First check if we already have a password and don't ask for it, if so.
const request: IServicePasswordRequest = args[0];
navigator.credentials.get({
password: true,
mediation: "silent",
} as any).then((credential) => {
if (credential) {
const id = 0;
} else {
// Opens the password dialog.
this.setState({ request }, () => this.dialogRef.current?.open());
}
});
}
};
private closePanel = (e: React.SyntheticEvent, props: IButtonProperties): void => {
// Called when either OK or Cancel was clicked by the user, in the password dialog.
if (props.id === "ok") {
const { request } = this.state;
const options = {
password: {
id: request!.serviceId,
name: request!.user,
password: "root", // Just for test.
},
};
navigator.credentials.create(options as any).then((credential) => {
if (credential) {
navigator.credentials.store(credential).then((value) => {
console.log("Success: " + value);
}).catch((e) => {
console.log("Error: " + e);
});
}
});
}
this.dialogRef.current?.close();
};
However there are several problems with that:
The password member (as documented on the CredentialContainer.create() page is unknown to Typescript. I worked around that with an any cast. The returned credential is a PasswordCredential structure, and the content looks fine.
When storing the credentials, the success branch is taken but the promise value is null. Not sure if that's relevant at all.
When I call navigator.credentials.get I never get any credential back. And in fact I'm not surprised. Shouldn't it be necessary to pass in id and user name to find credentials? But the API doesn't allow that.
So, what's the correct approach here?

Angular 5 chain service observables then return observable to component

I have two rest calls I need to make. Rest call 2 depends on rest call 1. And I want to store each result in the service before proceeding. Then I want to return an observable to the component that calls rest call 1 so the component can subscribe in case of issues.
code from service:
login(username: string, password: string): Observable<AuthAccess> {
// rest call 1
return this.getAuthClient()
.flatMap(res => {
this.client = res.body;
// rest call 2
return this.authenticate(username, password)
.flatMap(access => {
this.userAccess = access.body;
return Observable.of(this.userAccess);
});
});
}
I can get this to chain correctly, but the component that is calling this and subscribing to the call will show an undefined response. Any help on this would be greatly appreciated as I cannot find responses on this exact use case.
Live working example.
Use a map operator (and the lettable operatos sintax), instead of chain a new flatMap.
login(username: string, password: string): Observable<any> {
// rest call 1
return this.getAuthClient()
.pipe(flatMap(res => {
this.client = res.body;
// rest call 2
return this.authenticate(username, password)
.pipe(map(access => {
this.userAccess = access.body;
return this.userAccess;
}));
}));
}

Why is my service running before anything else?

I'm new to services, factories, etc; so there's still a lot I don't understand.
I have a ui-grid. When a row is selected, I want to use that data as a param in a service to get REST data to populate another grid.
This is what I think it should be doing:
gridOne is registered => Row Selected => Send selectedRow.id to Service => Service GETs data => data populates grid 2
This is what its actually doing:
Service GETs data => Error because selectedRow.id is not defined.
01| $scope.gridOne.onRegisterApi = function(gridApi){
02| $scope.gridOneApi = gridApi
03| $scope.gridOneApi.selection.on.rowSelectionChanged(null, function(row){
04| $scope.gridOneSelectedRow = $scope.gridOneApi.selection.getSelectedRows()[0]
05|
06| // v---Breakpoint on this line triggered before any grid is built---v
07| myService.getAllObjects($scope.gridOneSelectedRow.id).then(response => {
08| $scope.grid2.data = response
09| }
10| })
11| }
My service looks like this:
app.service('myService', function ($http) {
return {
get: getObjects
}
function getOjects(id) {
let url = `http://${domain}/object/${id}`
return $http.get(url).then(response => {
return response
}).catch(error => {
return error
})
}
}
Why is the service function running before everything else?
if you are writing a service, you should not return a object/function/something, your implementation function is used as a constructor to newed up and create instance of your service.
so if you want to use a service, a sample for your myService will be
app.service('myService', function ($http) {
function getOjects(id) {
//you should properly replace your domain, may be with the same domain by default if you start from object/...
let url = `http://${domain}/object/+ id`
return $http.get(url).then(response => {
return response.data
}).catch(error => {
return error
})
}
this.getAllObjects = getOjects;
})
in case of factory
app.factory('myService', function ($http) {
function getOjects(id) {
//you should properly replace your domain, may be with the same domain by default if you start from object/...
let url = `http://${domain}/object/+ id`
return $http.get(url).then(response => {
return response.data
}).catch(error => {
return error
})
}
return {getAllObjects: getOjects};
})
and in the injecting end you don't need to change the code the way you are using it to load data, just wrote the code in sync of use
and also, I wnder, why you are trying to select before the data is loaded, and inside the handler of row selection you want to call the data when now row is present at all, if the data is not loaded, right? I hope you are loading data for another grid grid2 on selection of a row of grid1, and then want to load corresponding data related to that row to grid2.
Feel free to comment, whatever doubt you still have.
Good Luck :)

Authentication in Angular 2, handling the observables

I just started with a Angular 2 project and am trying to get authentication up and running. Inspired by this tutorial I decided to do the following:
Create a custom RouterOutlet class (extending it) to handle the authentication logic whenever a url is called.
I succeeded in this custom class, but am still not sure how to check if a user is authenticated. My situation is as follows, I need to query a get call to a external API, for my development proces it is as follows:
getAdmin() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError)
}
This API call returns true or false. I was wondering what would be the best option to use this information? Should I for example call the following function each time a URL should be checked?:
isAdmin() {
this.getAdmin().subscribe(
data => this.authenticationResult = data,
error => console.log("Error: ", error),
() => return JSON.parse(this.authenticationResult._data);
}
I can't get this up and running because my observable is undefined when using the function I gave as example.
The "problem" is that your method is asynchronous so you need to be careful the way and when you use it.
If you want to use within the activate method of your custom RouterOutlet, you need to leverage observables and reactive programming.
I don't know exactly the way you want to check admin roles:
activate(instruction: ComponentInstruction) {
return this.userService.getAdmin().flatMap((isAdmin) => {
if (this.userService.isLoggIn()) {
if (this._canActivate(instruction.urlPath, isAdmin) {
return Observable.fromPromise(super.activate(instruction));
} else {
this.router.navigate(['Forbidden']);
return Observable.throw('Forbidden');
}
} else {
this.router.navigate(['Login']);
return Observable.throw('Not authenticated');
}
}).toPromise();
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}
In order to optimize the request, you could lazily (and only once) call the request to check if the user is admin or not:
isAdmin:boolean;
getAdmin() {
if (this.isAdmin) {
return Observable.of(this.isAdmin);
} else {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError);
}
}
Another approach will be also to load this hint when authenticating the user. This way, the implementation of the activate method would be simplier:
activate(instruction: ComponentInstruction) {
if (this.userService.isLoggIn()) {
if (this.userService.isAdmin()) {
return super.activate(instruction);
} else if (this._canActivate(instruction.urlPath, isAdmin) {
return super.activate(instruction);
} else {
this.router.navigate(['Forbidden']);
}
} else {
this.router.navigate(['Login']);
}
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}
I would consider to call getAdmin() somehow as first Step of your app, store the result in a SessionService object which you move around using Dependency Injection. This way any time you need to check the result of getAdmin you can ask the SessionService instance.
I hope this helps

Resources