Could anyone please help me to retreive the authenticated user who initiated a REST call? I'm using Lift with the RestHelper
In my Boot.scala I have the following:
LiftRules.httpAuthProtectedResource.prepend {
case Req(List("api", "incomingCall", incomingCall), _, GetRequest) => Full(AuthRole("admin"))
}
LiftRules.authentication = HttpBasicAuthentication("lift") {
case (username, password, req) => {
User.find(By(User.firstName, username)) match {
case Full(user) if user.password.match_?(password) => {
userRoles(AuthRole("admin"))
User.logUserIn(user) //I tried with and without this line
true
}
case x => {
false
}
}
}
}
LiftRules.dispatch.append(IncomingCallRest)
And my IncomingCallRest.scala file looks like this:
object IncomingCallRest extends RestHelper {
serve {
case "api" :: "incomingCall" :: incomingCall :: _ JsonGet _ => {
val currentUser = User.currentUser openOr User; //<--- On this line I'm trying to access the User, but it returns a blank user
val messageWithUser = (incomingCall, currentUser.userIdAsString)
ChatServer ! messageWithUser
JString(incomingCall)
}
}
}
User.currentUser does not return the authenticated user.
As you might see my code is based on the ChatServer example. I'm making the same call to User.currentUser from ChatIn.scala and there it works.
Any suggestions?
The creator of Lift suggested the following in an old thread:
Sessions are not initialized this early in the HTTP request/response
cycle. However, RequestVars are. My suggestion is to put User into a
RequestVar and then in your API module, read the RequestVar and put it into
a SessionVar.
I changed my code as follows to implement his suggestions:
//IncomingCallRest.scala
object userIdRequestVar extends RequestVar[String]("Default") //This RequestVar is set in Boot.scala
object IncomingCallRest extends RestHelper {
serve {
case "api" :: "incomingCall" :: incomingCall :: _ JsonGet _ => {
val messageWithUser = (incomingCall, userIdRequestVar.is)
ChatServer ! messageWithUser
JString(incomingCall)
}
}
}
//Boot.scala
LiftRules.authentication = HttpBasicAuthentication("lift") {
case (username, password, req) => {
User.find(By(User.firstName, username)) match {
case Full(user) if user.password.match_?(password) => {
userRoles(AuthRole("admin"))
userIdRequestVar.set(user.userIdAsString) //Set the RequestVar
true
}
case x => {
false
}
}
}
}
Related
I have a backend method called LikeExists() to verify if a certain user has liked a certain post.
public async Task<bool> LikeExists(int postId)
{
var post = await _postRepository.GetPostByIdAsync(postId);
var user = await _userRepository.GetUserByUsernameAsync(User.GetUsername());
if (_context.Likes.Where(i => i.PostId == post.Id && i.UserId == user.Id).FirstOrDefault() != null) return true;
return false;
}
The method works fine in Postman, but it does not do the job in Angular. If a user presses a like button I first want to check if this user has already liked this post. If he has, he will unlike it and the like will be deleted from the database. If he hasn't liked it, he will like it and the like will be saved in the database.
likeExists(){
this.postService.likeExists(this.post.id).subscribe((response: boolean) =>{
this.like = response;
});
}
likePost() {
if(this.likeExists){
this.postService.likePost(this.post.id, this.model).subscribe((response: Like) => {
this.likee = response;
console.log(response);
this.toastr.success('Liked');
}, error => {
console.log(error);
this.toastr.error(error.error);
});
} else {
this.postService.deleteLike(this.post.id).subscribe(() => {
this.toastr.success('Unliked');
}, error => {
console.log(error);
})
}
}
The problem is it always enters the if{} clause and never the else{} clause. The method below returns an Observable. I think the problem is that it must return a boolean. How can I make this work?
This is the method in the postService:
likeExists(postId: number) {
return this.http.get(this.baseUrl + 'like/exists/' + postId);
}
Try avoiding nested subscription since it will result in an unreadable and hard to maintain code, use rxjs pipes with operators instead, try something like this:
let id= this.post.id;
let likeExists$ = this.postService.likeExists(id);
likeExists$
.pipe(
switchMap(likeExists => {
if (likeExists) {
// delete like
return this.postService.deleteLike(id);
}
// otherwise addlike
return this.postService.addLike(id);
})
).subscribe(
res=> this.toastr.success('Success'),
err=> this.toastr.error('Failed')
);
or even shorter
let id= this.post.id;
let likeExists$ = this.postService.likeExists(id);
likeExists$
.pipe(switchMap(
liked => liked ? this.postService.deleteLike(id) : this.postService.addLike(id)}))
.subscribe(
res=> this.toastr.success('Success'),
err=> this.toastr.error('Failed')
);
The problem is that this.http.get is asynchronous, which means that likeExists returns before this.like is being set. You need to wait for the value to be returned in your observable. Refactor your code to something along these lines:
likePost() {
// Check to see if like exists and wait for response from server
this.postService.likeExists(this.post.id).subscribe((response: boolean) => {
this.like = response;
if (this.like) {
this.postService.likePost(this.post.id, this.model).subscribe((response: Like) => {
this.likee = response;
console.log(response);
this.toastr.success('Liked');
}, error => {
console.log(error);
this.toastr.error(error.error);
});
} else {
this.postService.deleteLike(this.post.id).subscribe(() => {
this.toastr.success('Unliked');
}, error => {
console.log(error);
})
}
});
}
Also, this is a nice guide to asynchronous concepts in general. And the RxJS docs have a bunch of helpful information to get started.
officeView.component.ts
setSelectedPerson(id:number)
{
this.pservice.getPerson(id);
localStorage.setItem("selectedPerson", JSON.stringify(this.pservice.person));
}
person.service.ts
getPerson(id:number)
{
this.http.get(personUrl + id).subscribe(response => this.person = response )
}
person:Person;
I'm executing setSelectedPerson method from OfficeViewComponent and here's what I'm hoping to happen:
I ask PersonService to fetch the data from api and assign it to it's variable - PersonService.person;
Now that the response is assigned to the service person variable, I'm expecting it to be stringified and saved in localStorage.
But here's what actually happens:
I ask PersonService to fetch the data, PersonService reacts and proceeds with the request, but by the time PersonService.getPerson() is finished, localStorage has already attempted to collect the data from PersonService.person, which - at that time - was unassigned.
I know there is a way to wait until the service method finishes it's work, but I don't know exactly what should I use.
Return the subscription from the service and use it to set data inside it. You don't need any variable inside your service.
officeView.component.ts :
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log('Error :',error.error)
}
)
}
person.service.ts :
getPerson(id:number) : Observable<any>{
return this.http.get(personUrl + id);
}
You're right, you should wait until the result is ready.
By then you can call the setSelectedPerson func.
//service func
getPerson(id:number) {
return this.http.get(personUrl + id);
}
//component func
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data => {
localStorage.setItem("selectedPerson", JSON.stringify(data ));
});
}
The problem with you code is the early subscribe in service itself, Ideally it should be in the component (at the caller)
officeView.component.ts :
setSelectedPerson( id : number ){
this.pservice.getPerson(id).subscribe(
response => {
localStorage.setItem("selectedPerson", JSON.stringify(response));
},error => {
console.log( 'Error :',error.error )
}
)
}
person.service.ts :
getPerson( id : number ) : Observable< any >{
return this.http.get( personUrl + id );
}
person.service.ts
getPerson(id:number) {
return this.http.get(personUrl + id);
person:Person;
and when you calling the HTTP service you should use subscribe
setSelectedPerson(id:number){
this.pservice.getPerson(id).subscribe(data=>{
console.log(data);
});
}
I have read the documentation how to use sync validation.
const validate = values => {
const errors = {}
....
if (!values.age) {
errors.age = 'Required'
} else if (isNaN(Number(values.age))) {
errors.age = 'Must be a number'
} else if (Number(values.age) < 18) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors
}
My need is I want the number '18' is retrieved from server? I need to setup all constants of the validation on server, and read it on page load only then used in this validation section.
Any help would be appreicated. thank you
I don't know if I understand the question correctly.
why don't you initiate validate method on page load after you fetch all constants from server.
validateConstants.js
export const validateConstants = fetch()
validation.js
import { validateConstants } from 'validateConstants'
const validate = values => {
const errors = {}
....
if (!values.age) {
errors.age = 'Required'
} else if (isNaN(Number(values.age))) {
errors.age = 'Must be a number'
} else if (Number(values.age) < validateConstants.age) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors
}
I found an example from angular.io. This example is very similar to my app, with same kind of methods. This example is using Promises, but I'm using Observables. If I use this example as a reference, I have every method working in my app, except the getHero method in the service, and the ngOnInit in the HeroDetailComponent. So I'm wondering if someone can help and convert this method to an observable, because I'm having trouble with the syntax. Here is the codes I need converted to Observable and the plunker
//HeroService
getHero(id: number) { // my id is String
return this.getHeroes()
.then(heroes => heroes.filter(hero => hero.id === id)[0]);
}
//HeroDetailComponent
ngOnInit() {
if (this.routeParams.get('id') !== null) {
let id = +this.routeParams.get('id');
this.navigated = true;
this.heroService.getHero(id)
.then(hero => this.hero = hero);
} else {
this.navigated = false;
this.hero = new Hero();
}
}
So I want something like this:
//HeroService
public getHero(id: string) {
return this.getHeroes()
.subscribe(heroes => this.heroes.filter(hero => heroes.id === id)[0]); //BTW, what does this [0] mean??
}
EDIT: I had to actually retrieve the list directly, it didn't work with return this.heroes as suggested in answers below. Working example:
public getById(id: string) {
//return this.getHeroes() <---- didn't work
return this.http.get('someUrl') // WORKS!
.map(heroes => this.heroes.filter(hero => hero.id === id)[0]);
}
Now I'm still having trouble with my ngOnit, and I can't really understand why!
ngOnInit(){
let id = this._routeParams.get('id');
this.heroService.getById(id)
//console.log("retrieved id: ",id ) <----- gives correct id!
.subscribe(hero => this.hero = hero);
//console.log("hero: ", this.hero); <----- gives undefined!
}
EDIT2, still getting undefined when trying to move to the detail page :( I think you had one bracket to much in your answer, tried to look and get the correct places for the brackets?
ngOnInit(){
let id = this._routeParams.get('id');
this.heroService.getById(id)
.subscribe(heroes => {
// this code is executed when the response from the server arrives
this.hero = hero
});
// code here is executed before code from the server arrives
// even though it is written below
}
If you call subscribe() on an Observable a Subscription is returned. You can't call subscribe() on a subscription.
Instead use just an operator (map()) and use subscribe() on the call site:
public getHero(id: string) {
return this.getHeroes()
.map(heroes => this.heroes.filter(hero => heroes.id === id)[0]);
}
ngOnInit(){
let id = this._routeParams.get('id');
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
In contrary to subscribe(), map() also operates on an Observable but also returns an Observable.
[0] means to just take the first item of the filtered heroes.
update
ngOnInit(){
let id = this._routeParams.get('id');
this._searchService.getById(id)
.subscribe(searchCase => {
// this code is executed when the response from the server arrives
this.searchCase = searchCase;
console.log("id: ", this.searchCase);
});
// code here is executed before code from the server arrives
// event though it is written below
}
This code is a function
searchCase => {
// this code is executed when the response from the server arrives
this.searchCase = searchCase);
console.log("id: ", this.searchCase);
}
that is passed to subscribe() and the Observable calls this function when it has new data for the subscriber. Therefore this code is not executed immediately but only when the observable emits new data.
Code that comes after subscribe() is executed immediately and therefore before above function and therefore this.searchCase does not yet have a value.
This is a way you can do it:
//HeroService
public getHero(id: string) {
return this.getHeroes()
.map(heroes => this.heroes.filter(hero => heroes.id === id)[0]);
}
//HeroDetailComponent
ngOnInit(){
let id = this._routeParams.get('id');
this.heroService.getHero(id)
.subscribe(hero => {
// your code here
});
}
The [0] is an array accessor. You're selecting the first element on array index 0 with it. You need this, because Array.filter() returns a new array with the filtered values, but you only want one hero.
Unfortunately as I was storing many results on the service, I'm now stuck with many {{myservice.somevalue}} (and myservice.someother) etc. sprinkled over my other components.
I believe it would be nicer to return from the service to a component.
This is the code that I have on the service:
getIssue(issueName: string) {
return this.http.post(this.URL + 'issue/all', JSON.stringify({'issueName': issueName}))
.subscribe(data => this.somevalue = data.json(),
err => console.log(err));
}
So then on several other components I call functions like this.myservice.getIssue(issueName).
In the HTML I'd prefer to use {{somevalue}}.
What can I do to return a http observable from the http service?
class SomeService {
someValue = new BehaviorSubject();
getIssue(issueName: string) {
return this.http.post(this.URL + 'issue/all', JSON.stringify({'issueName': issueName}))
.subscribe(data => this.somevalue.next(data.json()),
err => console.log(err));
}
}
In your component
class MyComponent {
constructor(someService:SomeService) {
someService.someValue.subscribe(val => {
this.someValue = val;
});
}
}
For an alternative approach see also https://stackoverflow.com/a/36291681/217408