Only update values if they are not null in Koltin - rest

So i try to only update values if they are not null in the response body of my request. This is how it looks at the moment, and if i dont send all the values with it, they just get nulled in the database. Im using Kotlin with JpaRepositories.
#PutMapping(value = ["/patient"], produces = ["application/json"])
fun updateClient(#RequestBody client: Client): ResponseEntity<Client>{
val old = repository.findById(client.id).orElseThrow{ NotFoundException("no patient found for id "+ client.id) }
val new = old.copy(lastName= client.lastName, firstName = client.firstName,
birthDate = client.birthDate, insuranceNumber = client.insuranceNumber)
return ResponseEntity(repository.save(new), HttpStatus.OK)
}
This is how the model class looks like
#Entity
data class Client(
#Id
val id: String,
val lastName: String?,
val firstName: String?,
val birthDate: Date?,
val insuranceNumber: Int?
)
Is there a easier way then writing copy once for every value and checking if its not null before?

The only thing that comes to mind that might make the process easier without modifying the current model or having to create other helper model/functions would be to use the elvis operator.
val new = old.copy(
lastName = client.lastName ?: old.lastName,
firstName = client.firstName ?: old.firstName,
birthDate = client.birthDate ?: old.birthDate,
insuranceNumber = client.insuranceNumber ?: old.insuranceNumber
)
Other ways of doing this would be to create our own copy function that would ignore input nulls, or a custom constructor that does the same. But that would require more work, and it depends on the model if that makes sense or not, for the example model that would not make sense in my opinion, it would be overkill

Related

Scala/Akka How do you reference the message being received?

I have a Java program that I must implement in Scala, but I am extremely new to Scala. After reading a number of SO question & answers as well as reading through a number of Google-retrieved resources on case classes, I am still having trouble grasping how to acquire a reference to the message I received? Example code is below:
case class SpecialMessage(key: Int) {
val id: Int = Main.idNum.getAndIncrement().intValue()
def getId(): Int = {
return id
}
}
Then in another class's receive I am trying to reference that number with:
def receive() = {
case SpecialMessage(key) {
val empID = ?? getId() // Get the id stored in the Special Message
// Do stuff with empID
}
}
I cannot figure out what to put on the right sight of empID = in order to get that id. Is this really simple, or something that isn't normally done?
These are 2 ways to do what you want, pick the one that suits best
case msg: SpecialMessage => {
val empID = msg.getId() // Get the id stored in the Special Message
// Do stuff with empID
}
case msg # SpecialMessage(key) => {
val empID = msg.getId() // Get the id stored in the Special Message
// Do stuff with empID
}
Pim's answer is good.
But maybe you can modify the structure of SpecialMessage like
case class SpecialMessage(key: Int,val id: Int = Main.idNum.getAndIncrement().intValue())
so you can get id directly from pattern matching.
def receive() = {
case SpecialMessage(key, empID) {
// Do stuff with empID
}
}

Play Scala Anorm dynamic SQL for UPDATE query

My Google-fu is letting me down, so I'm hoping you can help
I'm building some webservices is the play framework using scala and anorm for database access
One of my calls is to update an existing row in a database - i.e run a query like
UPDATE [Clerks]
SET [firstName] = {firstName}
,[lastName] = {lastName}
,[login] = {login}
,[password] = {password}
WHERE [id] = {id}
My method receives a clerk object BUT all the parameters are optional (except the id of course) as they may only wish to update a single column of the row like so
UPDATE [Clerks]
SET [firstName] = {firstName}
WHERE [id] = {id}
So I want the method to check which clerk params are defined and build the 'SET' part of the update statement accordingly
It seems like there should be a better way than to go through each param of the clerk object, check if it is defined and build the query string - but I've been unable to find anything on the topic so far.
Does anyone have any suggestions how this is best handled
As the commenters mentioned it appears to not be possible - you must build the query string yourself.
I found that examples around this lacking and it took more time to resolve this than it should have (I'm new to scala and the play framework, so this has been a common theme)
in the end this is what I implemented:
override def updateClerk(clerk: Clerk) = {
var setString: String = "[modified] = {modified}"
var params: collection.mutable.Seq[NamedParameter] =
collection.mutable.Seq(
NamedParameter("modified", toParameterValue(System.currentTimeMillis / 1000)),
NamedParameter("id", toParameterValue(clerk.id.get)))
if (clerk.firstName.isDefined) {
setString += ", [firstName] = {firstName}"
params = params :+ NamedParameter("firstName", toParameterValue(clerk.firstName.getOrElse("")))
}
if (clerk.lastName.isDefined) {
setString += ", [lastName] = {lastName}"
params = params :+ NamedParameter("lastName", toParameterValue(clerk.lastName.getOrElse("")))
}
if (clerk.login.isDefined) {
setString += ", [login] = {login}"
params = params :+ NamedParameter("login", toParameterValue(clerk.login.getOrElse("")))
}
if (clerk.password.isDefined) {
setString += ", [password] = {password}"
params = params :+ NamedParameter("password", toParameterValue(clerk.password.getOrElse("")))
}
if (clerk.supervisor.isDefined) {
setString += ", [isSupervisor] = {supervisor}"
params = params :+ NamedParameter("supervisor", toParameterValue(clerk.supervisor.getOrElse(false)))
}
val result = DB.withConnection { implicit c =>
SQL("UPDATE [Clerks] SET " + setString + " WHERE [id] = {id}").on(params:_*).executeUpdate()
}
}
it likely isn't the best way to do this, however I found it quite readable and the parameters are properly handled in the prepared statement.
Hopefully this can benefit someone running into a similar issue
If anyone wants to offer up improvements, they'd be gratefully received
Since roughly 2.6.0 this is possible directly with anorm using their macros, http://playframework.github.io/anorm/#generated-parameter-conversions
Here is my example:
case class UpdateLeagueFormInput(transferLimit: Option[Int], transferWildcard: Option[Boolean], transferOpen: Option[Boolean])
val input = UpdateLeagueFormInput(None, None, Some(true))
val toParams: ToParameterList[UpdateLeagueFormInput] = Macro.toParameters[UpdateLeagueFormInput]
val params = toParams(input)
val dynamicUpdates = params.map(p => {
val snakeName = camelToSnake(p.name)
s"$snakeName = CASE WHEN {${p.name}} IS NULL THEN l.$snakeName ELSE {${p.name}} END"
})
val generatedStmt = s"""UPDATE league l set ${dynamicUpdates.mkString(", ")} where league_id = ${league.leagueId}"""
SQL(generatedStmt).on(params: _*).executeUpdate()
producing:
UPDATE league l set transfer_limit = CASE WHEN null IS NULL THEN l.transfer_limit ELSE null END, transfer_wildcard = CASE WHEN null IS NULL THEN l.transfer_wildcard ELSE null END, transfer_open = CASE WHEN true IS NULL THEN l.transfer_open ELSE true END where league_id = 26;
Notes:
The camelToSnake function is just my own (There is an obvious ColumnNaming.SnakeCase available for parser rows, but I couldn't find something similar for parameter parsing)
My example string interpolates {league.leagueId}, when it could treat this as a parameter as well
Would be nice to avoid the redundant sets for null fields, however I don't think it's possible, and in my opinion clean code/messy auto-generated sql > messy code/clean auto-generated sql

many to many relation gorm table

I have a little problem filtering data...I have a relation many to many.
Sponsor and Old
I have one intermediary table with both ids...
When I log in, my session save a Sponsor id...but when I go to see Old view, appears every Olds... How to access the intermediary table from the OldController or the SponsorController?
In my Oldcontroller I have:
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
def sponsor = Sponsor.findById(session.getAttribute("id"))
def myOld = Old.findAllBySponsor(sponsor, params)
def allMyOld = Old.findAllBySponsor(sponsor)
respond myOld, model:[oldInstanceCount: allMyOld.size()]
//respond Old.list(params), model:[oldInstanceCount: Old.count()]
}
But returns unexpected NullpointerException when processing request: [GET] /oldCare/old/index
No value specified for parameter 2.. Stacktrace follows:
Message: No value specified for parameter 2.
How I access the intermediary table for filtering the olds and appears just the Old associated a one Sponsor?
Assuming you have the following :
class Old {
String title
static hasMany = [sponsours: Sponsour]
}
class Sponsour {
String name
static hasMany = [olds:Old]
}
To find :
def sponsor = Sponsor.load(session.getAttribute("id"))
def allMyOlds = Old.findBySponsour(sponsor,params)

Testing With A Fake DbContext and Autofixture and Moq

SO follow this example
example and how make a fake DBContex For test my test using just this work fine
[Test]
public void CiudadIndex()
{
var ciudades = new FakeDbSet<Ciudad>
{
new Ciudad {CiudadId = 1, EmpresaId =1, Descripcion ="Santa Cruz", FechaProceso = DateTime.Now, MarcaBaja = null, UsuarioId = 1},
new Ciudad {CiudadId = 2, EmpresaId =1, Descripcion ="La Paz", FechaProceso = DateTime.Now, MarcaBaja = null, UsuarioId = 1},
new Ciudad {CiudadId = 3, EmpresaId =1, Descripcion ="Cochabamba", FechaProceso = DateTime.Now, MarcaBaja = null, UsuarioId = 1}
};
//// Create mock unit of work
var mockData = new Mock<IContext>();
mockData.Setup(m => m.Ciudades).Returns(ciudades);
// Setup controller
var homeController = new CiudadController(mockData.Object);
// Invoke
var viewResult = homeController.Index();
var ciudades_de_la_vista = (IEnumerable<Ciudad>)viewResult.Model;
// Assert..
}
Iam tryign now to use Autofixture-Moq
to create "ciudades" but I cant. I try this
var fixture = new Fixture();
var ciudades = fixture.Build<FakeDbSet<Ciudad>>().CreateMany<FakeDbSet<Ciudad>>();
var mockData = new Mock<IContext>();
mockData.Setup(m => m.Ciudades).Returns(ciudades);
I get this error
Cant convert System.Collections.Generic.IEnumerable(FakeDbSet(Ciudad)) to System.Data.Entity.IDbSet(Ciudad)
cant put "<>" so I replace with "()" in the error message
Implementation of IContext and FakeDbSet
public interface IContext
{
IDbSet<Ciudad> Ciudades { get; }
}
public class FakeDbSet<T> : IDbSet<T> where T : class
how can make this to work?
A minor point... In stuff like:
var ciudades_fixture = fixture.Build<Ciudad>().CreateMany<Ciudad>();
The second type arg is unnecessary and should be:
var ciudades_fixture = fixture.Build<Ciudad>().CreateMany();
I really understand why you need a FakeDbSet and the article is a bit TL;DR... In general, I try to avoid faking and mucking with ORM bits and instead dealing with interfaces returning POCOs to the max degree possible.
That aside... The reason the normal syntax for initialising the list works is that there is an Add (and IEnumerable) in DBFixture. AutoFixture doesn't have a story for that pattern directly (after all it is compiler syntactic sugar and not particularly amenable to reflection or in line with any other conventions) but you can use AddManyTo as long as there is an ICollection in play. Luckily, within the impl of FakeDbSet as in the article, the following gives us an in:-
public ObservableCollection<T> Local
{
get { return _data; }
}
As ObservableCollection<T> derives from ICollection<T>, you should be able to:
var ciudades = new FakeDbSet<Cuidad>();
fixture.AddManyTo(ciudades.Local);
var mockData = new Mock<IContext>();
mockData.Setup(m => m.Ciudades).Returns(ciudades);
It's possible to wire up a customization to make this prettier, but at least you have a way to manage it. The other option is to have something implement ICollection (or add a prop with a setter taking IEnumerable<T> and have AF generate the parent object, causing said collection to be filled in.
Long superseded side note: In your initial question, you effectively have:
fixture.Build<FakeDbSet<Ciudad>>().CreateMany()
The problem becomes clearer then - you are asking AF to generate Many FakeDbSet<Ciudad>s, which is not what you want.
I haven't used AutoFixture in a while, but shouldn't it be:
var ciudades = new FakeDbSet<Ciudad>();
fixture.AddManyTo(ciudades);
for the moment I end doing this, I will keep reading about how use automoq, cause I'm new in this
var fixture = new Fixture();
var ciudades_fixture = fixture.Build<Ciudad>().CreateMany<Ciudad>();
var ciudades = new FakeDbSet<Ciudad>();
foreach (var item in ciudades_fixture)
{
ciudades.Add(item);
}
var mockData = new Mock<IContext>();
fixture.Create<Mock<IContext>>();
mockData.Setup(r => r.Ciudades).Returns(ciudades);

What should be asserted/tested when inserting an entity in the database/repository

This is my integration test code:
// Arrange
var user = new User() { FirstName = "test", UserId = 4, LastName = "test", RegisteredAt = new DateTime(2013, 02, 02) };
var repository = new GenericRepository<User>(_context);
// Act
repository.Add(user);
_context.SaveChanges();
// Assert
Assert.IsNotNull(user.UserId);
I have seen people doing a IsNotNull test with the UserId which makes no sense as the UserId should be never a nullable integer.
What and how would YOU test to make sure the entity you created and added into the database is the same?
Would you do:
var dbUser = _context.Users.First(u => u.FirstName == "test")
and then
Assert.AreEqual(dbUser.FirstName,user.FirstName)
and this for all properties you created?
UPDATE
I have googled a bit more and the people are really doing different and weird things to test wether an entity was inserted into the database:
Assert.That(1, Is.EqualTo(context.Roads.Count()));
Assert.assertNotNull(repo.findUserById(user.getId()));
ICustomer retainedCustomer = repository.
Customers.
Where(q => q.CustomerName == expectedCustomerName).
FirstOrDefault();
Assert.IsNotNull(retainedCustomer);
The first does not even test the inserted entity called road it is checking the count of the road table...
The second and the third assertion seem wrong in my opinion because they use the repository methods with login in it which could fail in a later test to get the inserted customer/user.
What do you think?