Scala idiom for partial models?

I am writing an HTTP REST API, and I want strongly typed model classes in Scala, for example. if I have a Car model, I want to create the following RESTful /car API:

1) For POST (create a new car):

 case class Car(manufacturer: String, name: String, year: Int) 

2) For PUT (edit an existing car) and GET s, I want to also mark the id tag:

 case class Car(id: Long, manufacturer: String, name: String, year: Int) 

3) For PATCH es (partial editing of an existing car) I need this partial object:

 case class Car(id: Long, manufacturer: Option[String], name: Option[String], year: Option[Int]) 

But saving the three models is essentially the same thing is redundant and error prone (for example, if I edit one model, I need to remember other models).

Is there a prominent way to support all 3 models? I am fine with answers that also use macros.

I managed to combine the first two, as shown below.

 trait Id { val id: Long } type PersistedCar = Car with Id 
+6
source share
4 answers

Actually, I was able to solve this problem using the small library I wrote: https://github.com/pathikrit/metarest

Using the library above, this is simply done:

 import com.github.pathikrit.MetaRest._ @MetaRest case class Car( @get @put id: Long, @get @post @put @patch manufacturer: String, @get @post @put @patch name: String, @get @post @put @patch year: Int) ) 
0
source

I would go with something like this

  trait Update[T] { def patch(obj: T): T } case class Car(manufacturer: String, name: String, year: Int) case class CarUpdate(manufacturer: Option[String], name: Option[String], year: Option[Int]) extends Update[Car] { override def patch(car: Car): Car = Car( manufacturer.getOrElse(car.manufacturer), name.getOrElse(car.name), year.getOrElse(car.year) ) } sealed trait Request case class Post[T](obj: T) extends Request case class Put[T](id: Long, obj: T) extends Request case class Patch[T, U <: Update[T]](patch: U) extends Request 

With Post and Put, everything is simple. Patch is a little trickier. I am sure that the CarUpdate class can be replaced automatically with macros.

If you update your car model, you probably will not forget about the patch, because it will not work during compilation. However, these two models look too copy-paste-like.

+5
source

You can present your models as Shapeless records , then id is just another field on the front panel, and mapping to / from options can be done in general terms using the usual Shapeless font-level programming methods. It should also be possible to generalize / deserialize such things to JSON (I did this in the past, but the corresponding code belongs to the previous employer). But you will definitely push boundaries and do complex programming at the level level; I do not think that currently existing libraries with this approach exist.

+3
source

So far, I agree with Pavelโ€™s comment (yes, you would have many duplicate fields, but this is because you decouple the external representation of the fields into the internal representation of the fields, which is good if you want to change your internal representation without changing the API), a possible way to achieve what you want (that, if I understood correctly, to have one idea):

 case class CarAllRepresentationsInOne( id: Option[Long] = None, manufacturer: Option[String] = None, name: Option[String] = None, year: Option[Int] = None) 

Since you have default values โ€‹โ€‹for all specified as None, you can instantiate this CClass from all routes, with the only drawbacks being the need to use named parameters during instance creation and checking None in all field applications.

But I would strongly recommend having different types for your internal presentation and for every possible external request resource: this may seem like duplicating the code at the beginning, but the way to model cars inside your world should be separated by the resources used by the outside world, to separate them and allow you to change internal representation without changing the api contract with external when new needs arise.

-1
source

Source: https://habr.com/ru/post/976958/


All Articles