I am trying to use PATCH request.
below is the code which i am using. and it is like ladder of if statements
#PatchMapping("/updateInvoiceByEmail/{email}")
public Mono<ResponseEntity<Invoice>> updateInvoiceByEmail(
#PathVariable String email,
#RequestBody Invoice invoice) {
return invoiceRepository
.findByEmail(vendorEmail)
.flatMap(existing -> {
if (invoice.getInvoiceStatus() != null) {
existing.setInvoiceStatus(invoice.getInvoiceStatus());
}
if (invoice.getCanRaise() != null) {
existing.setCanRaise(invoice.getCanRaise());
}
if (invoice.getAttachmentId() != null) {
existing.setAttachmentId(invoice.getAttachmentId());
}
if (invoice.getInvoiceId() != null) {
existing.setInvoiceId(invoice.getInvoiceId());
}
... and so on.
return invoiceRepository.save(existing);
})
.map(updatedInvoice -> new ResponseEntity<>(updatedInvoice, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
I am using
Spring WebFlux and mongodb
How can i make shorter and cleaner.
Thanks
You can use reflection if you want to reduce number of lines although reflection is always last thing that you should do. What I would do is to move this if - get - set logic in separate component(in separate class or method). Aside from that, I would just like to mention that simplicity is not the only problem here. Assume that null is valid value sent by client, you will need some mechanism how to detect if value is not sent or you explicitly wanted to set null value. Some example of moving this code to separate component would look something like this:
class InvoiceAssembler {
public static assemble(Invoice existing, Invoice newInvoice) {
if(newInvoice.getInvoiceId() != null) {
existing.setInvoiceId(newInvoice.getInvoiceId());
}
...
}
}
Only way I see you could improve is if you use a UpdateDTO for the fields that are most likely to be updated and therefor reduce the amount of fields you have to check.
Good description of how you use UpdateDTO here
Otherwise you are left with reflection but I don't know if that would make your code more readable or more confusing instead.
Related
I'm trying to create a classifier VS extension to colorize "TODO" comments. For that, in my IClassifier.GetClassificationSpans method I want to get the current classification spans, and only process those classified as "comment".
In my classifier provider I added an IClassifierAggregatorService field, and used its GetClassifier method to pass the result to my classifier, which can use it to get the classifications. However, this generates a StackOverflowException, since my GetClassifier method is called again and again in a loop:
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
namespace TestTasksClassifier
{
[Export(typeof(IClassifierProvider))]
[ContentType("code")]
internal class EditorClassifier1Provider : IClassifierProvider
{
[Import] private IClassificationTypeRegistryService classificationRegistry;
[Import] private IClassifierAggregatorService aggregatorService;
public IClassifier GetClassifier(ITextBuffer buffer)
{
return buffer.Properties.GetOrCreateSingletonProperty<EditorClassifier1>(creator: () =>
new EditorClassifier1(classificationRegistry, aggregatorService.GetClassifier(buffer)));
}
}
}
To solve the StackOverflowException, in GetClassifier I've ended using ITextBuffer.Properties to store my classifier instance, and if it already exists I just return a null classifier to stop the loop. It works in my limited tests, but I have no idea whether this is a valid and safe approach:
public IClassifier GetClassifier(ITextBuffer buffer)
{
if (buffer.Properties.TryGetProperty<EditorClassifier1>(typeof(EditorClassifier1), out var _))
{
return null;
}
else
{
EditorClassifier1 editorClassifier1 = new EditorClassifier1(classificationRegistry);
buffer.Properties.AddProperty(typeof(EditorClassifier1), editorClassifier1);
// Now I can call "aggregatorService.GetClassifier", since "editorClassifier1" is already added to "buffer.Properties",
// and this method will return null when called again.
editorClassifier1.Classifier = aggregatorService.GetClassifier(buffer);
return editorClassifier1;
}
}
What would be the best approach to retrieve the current classifications from my custom classifier?
I'm using gorm as a Golang ORM. I need to detect if any model field has changed to trigger a update on a thirdy-party API service.
I have tried to test every field with an If statement, but it gets ugly when the model has many fields.
var person Person
db.Where("id = ?", id).First(&person)
if person.Name != body.Person.Name || person.Age != body.Person.Age {
// Trigger API update
}
db.Save(&person)
Is there a easy way to achieve this?
I don't know if this is the simplest way, and it is probably not idiomatic, but you can accomplish this with reflection. The following function uses the reflect Package to compare two Person structs to see if their values for each field are the same, skipping the Model struct (whose internals vary independently of the data element the Person represents).
func (this Person) Equals(that Person) bool {
vThis := reflect.ValueOf(this)
vThat := reflect.ValueOf(that)
for i := 0; i < vThis.NumField(); i++ {
if vThis.Field(i) != vThis.FieldByName("Model") {
if vThis.Field(i).Interface() != vThat.Field(i).Interface() {
return false
}
}
}
return true
}
You could use this in your code snippet then as follows:
if !person.Equals(body.Person) {
// Trigger API update
}
Note that I'm quite new to go, so I may be leading you astray here in terms of "proper" code. But this does work.
GORM does provide such a feature via the 'Changed' method which could be used in Before Update Hooks. It returns if the field was changed or not.
https://gorm.io/docs/update.html#Check-Field-has-changed
I am using rxjava2 for the first time on an Android project, and am doing SQL queries on a background thread.
However I am having trouble figuring out the best way to do a simple SQL query, and being able to handle the case where the record may or may not exist. Here is the code I am using:
public Observable<Record> createRecordObservable(int id) {
Callable<Record> callback = new Callable<Record>() {
#Override
public Record call() throws Exception {
// do the actual sql stuff, e.g.
// select * from Record where id = ?
return record;
}
};
return Observable.fromCallable(callback).subscribeOn(Schedulers.computation());
}
This works well when there is a record present. But in the case of a non-existent record matching the id, it treats it like an error. Apparently this is because rxjava2 doesn't allow the Callable to return a null.
Obviously I don't really want this. An error should be only if the database failed or something, whereas a empty result is perfectly valid. I read somewhere that one possible solution is wrapping Record in a Java 8 Optional, but my project is not Java 8, and anyway that solution seems a bit ugly.
This is surely such a common, everyday task that I'm sure there must be a simple and easy solution, but I couldn't find one so far. What is the recommended pattern to use here?
Your use case seems appropriate for the RxJava2 new Observable type Maybe, which emit 1 or 0 items.
Maybe.fromCallable will treat returned null as no items emitted.
You can see this discussion regarding nulls with RxJava2, I guess that there is no many choices but using Optional alike in other cases where you need nulls/empty values.
Thanks to #yosriz, I have it working with Maybe. Since I can't put code in comments, I'll post a complete answer here:
Instead of Observable, use Maybe like this:
public Maybe<Record> lookupRecord(int id) {
Callable<Record> callback = new Callable<Record>() {
#Override
public Record call() throws Exception {
// do the actual sql stuff, e.g.
// select * from Record where id = ?
return record;
}
};
return Maybe.fromCallable(callback).subscribeOn(Schedulers.computation());
}
The good thing is the returned record is allowed to be null. To detect which situation occurred in the subscriber, the code is like this:
lookupRecord(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Record>() {
#Override
public void accept(Record r) {
// record was loaded OK
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) {
// there was an error
}
}, new Action() {
#Override
public void run() {
// there was an empty result
}
});
I have a list view that can be sorted, searched and filtered. From that list view the user can edit items in multiple steps. Finally after editing and reviewing the changes the user goes back to the list. Now I want the list to use the same sorting, search term and filters that the user set before and show the correct results.
How can multiple paramters (sorting, search, filter) be stored and reused when showing the list action?
Possible unsatisfactory ways that I thought of:
pass through all the needed parameters. Does work hardly if there are multiple actions involved between the two list action calls
save the parameters in the session object. This seems to require a lot of code to handle multiple parameters (check if parameter was passed to action, store new value, if parameter was not passed, get old parameter from session, handle empty string parameters):
Long longParameter
if(params.containsKey('longParameter')) {
longParameter = params.getLong('longParameter')
session.setAttribute('longParameter', longParameter)
} else {
longParameter = session.getAttribute('longParameter') as Long
params['longParameter'] = longParameter
}
If you want to make it more generic you could use an Interceptor instead.
This could perhaps be generalized like this:
class SessionParamInterceptor {
SessionParamInterceptor() {
matchAll() // You could match only controllers that are relevant.
}
static final List<String> sessionParams = ['myParam','otherParam','coolParam']
boolean before() {
sessionParams.each {
// If the request contains param, then set it in session
if (params.containsKey(it)) {
session[it] = params[it]
} else {
// Else, get the value from session (it will be null, if not present)
params[it] = session[it]
}
}
true
}
}
The static sessionParams holds the parameters you want to store/retrieve from session.
If the params contains an element from the list, it is stored in session under the same name. If not, it is taken from session (given that it exists).
In your controller, you can now just access params.getLong('theParam') like you always would. You could also use Grails parameter conversion:
def myAction(Long theParam) {
}
Lots of LOC saved.
I use the session as well. Here is a sample that you may adapt to your needs:
def list() {
if (request.method == 'GET' && !request.queryString) {
if (session[controllerName]) {
// Recall params from memory
params.putAll(session[controllerName])
}
} else {
// Save params to memory and redirect to get clean URL
session[controllerName] = extractParams(params)
redirect(action: actionName)
return
}
// Do your actions here...
}
def extractParams(params) {
def ret = [:]
params.each { entry ->
if (entry.key.startsWith("filter_") || entry.key == "max" || entry.key == "offset" || entry.key == "sort" || entry.key == "order") {
ret[entry.key] = entry.value
}
}
return ret
}
Using session is your best bet. Just save the preference when preferred. I mean, when user sorts, or filter, just save that information in the session, for that particular <controller>.<action>, before returning the page. Next time, check the session, if it has anything related to that <controller>.<action>, apply those; otherwise render the default page.
You might like to use some Interceptor for this, as suggested by sbglasius, here.
I hope you're getting my point.
I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.