Use Value of Result Mono from DB Request in Another Request - spring

I want to get a user by his username, assign this user as the author of a note and then save that note to the database. The problem is that I get a Mono with a user in it and I can't assign that to the author field of type user.
What I'm trying to do:
noteRepository
.save(noteMapper
.fromDTO(noteDTO)
.apply { owner = userReository
.findByUsername(userPrincipalService.getCurrentUserPrincipal()
.map { it.username })
})

userRepository
// Find the User
.findByUsername(userPrincipalService.getCurrentUserPrincipal()
.map { it.username })
// Map the User to a Note, while setting the note's owner
.map { user ->
noteMapper.fromDTO(noteDTO).apply {
owner = user
}
}
// Save the Note
.flatMap { note -> noteRepository.save(note) }

Related

I don't know why edge generate doesn't work in entgo

i'm trying build app by golang and entgo and falling problem about entgo generate,
https://entgo.io/
below is user table code
type user table
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("nickname").NotEmpty().Unique(),
field.String("user_type").Default("guest"),
field.Time("created_at").Default(time.Now()),
field.Time("updated_at").Default(time.Now()).UpdateDefault(time.Now()),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("token", Token.Type),
}
}
and below is about token
// Token holds the schema definition for the Token entity.
type Token struct {
ent.Schema
}
// Fields of the Token.
func (Token) Fields() []ent.Field {
return []ent.Field{
field.String("token"),
field.Int("user_id"),
field.Time("created_at").Default(time.Now()),
field.Time("updated_at").Default(time.Now()).UpdateDefault(time.Now()),
}
}
// Edges of the Token.
func (Token) Edges() []ent.Edge {
return []ent.Edge{
edge.From("user", User.Type).Ref("users").Field("user_id"),
}
}
and im try generate entgo like this
go generate ./ent
but it is not working and i give error like below,
entc/gen: resolve "Token" relations: edge "users" is missing for inverse edge: Token.user(User)
exit status 1
i dont know what
I don't know what's wrong, please help
You've reversed the order of edge.To / edge.From.
See the ent docs:
A schema that defines an edge using the edge.To builder owns the relation, unlike using the edge.From builder that gives only a back-reference for the relation (with a different name).
From your example it looks like you're trying to achieve a O2M relation where a User can have many Tokens associated.
Change the Edges methods on both structs to be:
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.From("token", Token.Type).
Ref("users"),
}
}
and
// Edges of the Token.
func (Token) Edges() []ent.Edge {
return []ent.Edge{
edge.To("users", User.Type).
Unique().
Field("user_id").
Required(),
}
}

Spring WebFlux check user exists

I want to check that the user has not been created yet before creating a new one, if there is then create an error... I found a similar question, but I can't remake it =(
Spring WebFlux: Emit exception upon null value in Spring Data MongoDB reactive repositories?
public Mono<CustomerDto> createCustomer(Mono<CustomerDto> dtoMono) {
//How Create Mono error???
Mono<Customer> fallback = Mono.error(new DealBoardException("Customer with email: " + dtoMono ???));
return dtoMono.map(customerConverter::convertDto) //convert from DTO to Document
.map(document -> {
customerRepository.findByEmailOrPhone(document.getEmail(), document.getPhone())
})
.switchIfEmpty() //How check such customer doesn't exists?
.map(document -> { //Filling in additional information from other services
var customerRequest = customerConverter.convertDocumentToStripe(document);
var customerStripe = customerExternalService.createCustomer(customerRequest);
document.setCustomerId(customerStripe.getId());
return document;
})
.flatMap(customerRepository::save) //Save to MongoDB
.map(customerConverter::convertDocument); //convert from Document to Dto
}
public Mono<User> create(String username, String password) {
User user = new User();
user.setUsername(username);
user.setPassword(encoder.encode(password));
return userRepo.existsUserByUsername(username)
.flatMap(exists -> (exists) ? Mono.error(UserAlreadyExists::new) : userRepo.save(user));
}
Add the following index declaration on the top of your Customer class:
#CompoundIndex(def = "{'email': 1, 'phone': 1}", unique = true)
That will prevent duplicate entries to be inserted in the database.
You can catch your org.springframework.dao.DuplicateKeyException with the following construct:
customerService.save(newCustomer).onErrorMap(...);
Ref: MongoDB Compound Indexes official documentation

Can I grade assignments "logged as student" with Google Classroom API?

I have an app using Google Classroom API. When connected as the teacher I can create course works and assignments. When connected as a student I can list my assignments and I can turn in a specific assignment.
I am using the REST API:
https://developers.google.com/classroom/reference/rest
When (logged as student) I turn in an assignment but I would like to include a draft grade.
I know if I were logged as the teacher I could set the grade, but what I want is the app calculating the draft grade based on some specific built-in logic, so that the teacher does not have to do it on their own for each student.
According to the documentation, both "draftGrade" and "assignedGrade" can only be updated by the teacher.
https://developers.google.com/classroom/reference/rest/v1/courses.courseWork.studentSubmissions#StudentSubmission
Any ideas about how to automate setting grades for submissions?
I think that is not possible: you cannot update the draftGrade with student privileges.
What you can do:
From the "student" session you save a draft grade in the application DB, associated to the Submission ID.
From the "teacher" session, and hence "teacher" permissions, you get the grade from the application DB and I call the Path query to set the draftGrade.
Some code (Swift, using GoogleAPIClientForREST) for step 2:
func executeQuery_GradeSubmission(studentSubmission: GTLRClassroom_StudentSubmission) -> GTLRServiceTicket? {
guard let courseID = self.myClassroom?.courseID,
let courseWorkID = self.selectedCourseWorkID else { return nil }
if let grade = self.gradesForSelectedWorkID?[studentSubmission.identifier!] {
studentSubmission.draftGrade = NSNumber(floatLiteral: Double(grade))
}
let query = GTLRClassroomQuery_CoursesCourseWorkStudentSubmissionsPatch.query(withObject: studentSubmission,
courseId: courseID,
courseWorkId: courseWorkID,
identifier: studentSubmission.identifier!)
query.updateMask = "draftGrade"
return self.myClassroom?.service.executeQuery(query,
delegate: self,
didFinish: #selector(displayGradeSubmissionResult(ticket:finishedWithObject:error:)))
}
#objc func displayGradeSubmissionResult(ticket: GTLRServiceTicket, finishedWithObject: GTLRObject, error: Any?){
let classroomSubmissionResponse = finishedWithObject as? GTLRClassroom_StudentSubmission
if let classroomError = error as? NSError {
print("displayGradeSubmissionResult. ERROR: \(classroomError.description)")
// TODO: inform something went wrong
} else {
if let submissionItems = self.classroomSubmissionsResponse?.studentSubmissions {
for submissionItem in submissionItems {
if submissionItem.identifier == classroomSubmissionResponse?.identifier {
submissionItem.draftGrade = classroomSubmissionResponse?.draftGrade
}
}
}
}
}

BehaviorSubject with transformation

Is there a way to return a transformed BehaviorSubject?
class ViewModel {
let username: BehaviorSubject<String>
init() {
// I want username to emit trimmed values...
username = BehaviorSubject<String>(value: "")
// ... but map returns an Observable<>, not BehaviorSubject
.map { $0.trimmingCharacters(in: CharacterSet.whitespaces)
}
}
The short answer is no, there is no way to return a transformed BehaviorSubject.
You have to first define the output that you want to affect, then figure out what inputs affect it. The map will go between those. So for example:
myTextField.rx.text.orEmpty
.map { $0.trimmingCharacters(in: CharacterSet.whitespaces)
.bind(to: username)
.disposed(by: disposeBag)
If you want username to be the output, or
username.asObservable() // I'm not sure if the asObservable() is actually necessary at the moment. Check that.
.map { $0.trimmingCharacters(in: CharacterSet.whitespaces)
.bind(to: myLabel.rx.text)
.disposed(by: disposeBag)
if you want username to be the input.
To create a username just use let username = BehaviorSubject<String>()

How to check if Mono is empty?

I'm developing a app with Spring Boot 2.0 and Kotlin using the WebFlux framework.
I want to check if a user id exits before save a transaction. I'm stucked in a simple thing like validate if a Mono is empty.
fun createTransaction(serverRequest: ServerRequest) : Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java))
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
}
It is possible to do what I want?
The techniques that allow checking whether Flux/Mono is empty
Using operators .switchIfEmpty/.defaultIfEmpty/Mono.repeatWhenEmpty
Using mentioned operators you will be able to react to the case when Stream has been completed without emitting any elements.
First of all, remember that operators such .map, .flatMap, .filter and many others will not be invoked at all if there no onNext has been invoked.
That means that in your case next code
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
will not be invoked at all, if transaction will be empty.
In case if there is a requirement for handling cases when your flow is empty, you should consider operators like next in the following manner:
transaction
.flatMap(it -> {
val user = userRepository.findById(it.userId)
})
.swithIfEmpty(Flux.defer(() -> Flux.just(badRequest())));
Actual solution
Also, I have noted that you created two sub-flows from the main transaction. Actually, following code will not be executed at all:
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
and will be only executed the last one, which is returned from the method. That happens because you ain't subscribed using operator .subscribe(...).
The second point, you can't subscribe to the same request body more the one time (kind of limitation for WebClient's reponse). Thus you are required to share your request body in the next way, so completed example will be:
fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java)).cache()
transaction
.flatMap { userRepository.findById(it.userId) }
.flatMap { transaction.flatMap { transactionRepository.save(it) } }
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
.switchIfEmpty(transaction.flatMap { ServerResponse.badRequest().syncBody("missed User for transaction " + it.id) })
}
Or more simple case without sharing transaction flow but using Tuple:
fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val emptyUser = !User()
val transaction = serverRequest.body<Mono<Transaction>>(BodyExtractors.toMono(Transaction::class.java))
transaction
.flatMap { t ->
userRepository.findById(t.userId)
.map { Tuples.of(t, it) }
.defaultIfEmpty(Tuples.of(t, emptyUser))
}
.flatMap {
if (it.t2 != emptyUser) {
transactionRepository.save(it.t1)
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
} else {
ServerResponse.badRequest().syncBody("missed User for transaction " + it.t1.id)
}
}
}
You can check it using the Mono's provided method hasElement() which is analogous to Optional's isPresent(). The method definition is :
Mono<Boolean> hasElement()
for more details checkout : project reactor documentation
In case you have to perform some action based on this value you can further use switchIfEmpty() to provide with alternate publisher.
Let me start by saying I am a newbie on reactive (java) and on this forum.
I think you cannot really check in this code if a mono is empty because a mono represents code that will be executed later on, so in this code body you won't know yet if its is empty. Does that make sense?
I just wrote something similar in Java which seems to work (but not 100% this is the best approach either):
public Mono<ServerResponse> queryStore(ServerRequest request) {
Optional<String> postalCode = request.queryParam("postalCode");
Mono<ServerResponse> badQuery = ServerResponse.badRequest().build();
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
if (!postalCode.isPresent()) { return badQuery; }
Flux<Store> stores = this.repository
.getNearByStores(postalCode.get(), 5);
return ServerResponse.ok().contentType(APPLICATION_JSON)
.body(stores, Store.class)
.switchIfEmpty(notFound);
}
We can use switchIfEmpty method for this
Below example, I'm checking if the user exists with email if not then add it
userRepository.findByEmail(user.getEmail())
.switchIfEmpty(s -> {
user.setStatus("InActive");
String encodedPassword = DigestUtils.sha256Hex(user.getPassword());
user.setPassword(encodedPassword);
userRepository.save(user).subscribe();
s.onComplete();
}).then(Mono.just(user));
Use Mono with Optional:
return findExistingUserMono
.map(Optional::of)
.defaultIfEmpty(Optional.empty())
.flatMap(optionalUser -> {
if(optionalUser.isPresent()) {
return Mono.error('xxxx');
}
return this.userService.create(optionalUser.get());
});
This way it will always emit Optional value so that the stream will never break.

Resources