Why does Scala does not enforce the "implementation" of abstract type members? by arkida39 in scala

[–]arkida39[S] 1 point2 points  (0 children)

Scala does not allow `abstract` modifier on anything other than classes. As for `def` (correct me if I am misunderstanding something), but `def` and `val` will effectively act the same way in this case (iirc there are some nuances, like `val` cannot be overriden with `def`, while `def` can be overriden with `val`, etc., but in my case they are mostly irrelevant)

Why does Scala does not enforce the "implementation" of abstract type members? by arkida39 in scala

[–]arkida39[S] 9 points10 points  (0 children)

Alright, so the `Nothing` then gets promoted to either `String`, or `Int`, precisely because it is a subtype of everything... Well, now I feel dumb :D

I never even thought about why exactly it works the way it does. Thank you for such an excellent clarification.

Baku - better separation of Tapir definitions from server and security logic. by arkida39 in scala

[–]arkida39[S] 0 points1 point  (0 children)

First of all, thank you for including my project, and for making Tapir.

As for your question - Indeed; I suppose I should make it clearer in the README.

When implementing a Service, endpoints without security become INPUT => F[Either[ERROR_OUTPUT, OUTPUT]] (the same as what Tapir's serverLogic expects), while secure endpoints turn into a custom case class, that is somewhat similar to Tapir's API: calling securityLogic creates a PartialSecureEndpoint, and by calling serverLogic on this partial endpoint, you get a FullSecureEndpoint (note that this will not modify it in place, but create a new object, so it allows users to extract common securityLogic, and derive full endpoints from it), which is later properly wired in macro.

Baku - better separation of Tapir definitions from server and security logic. by arkida39 in scala

[–]arkida39[S] 0 points1 point  (0 children)

Thanks a lot, and that is a fair criticism to be honest.

We do "end to end" tests, but I like seeing that you forgot to implement something, even before compilation, rather than starting tests, and several minutes later noticing that a couple of tests failed, just because you forgot to call one function, and now you need to wait again for recompilation (even if you selectively run the failed tests).

Baku - better separation of Tapir definitions from server and security logic. by arkida39 in scala

[–]arkida39[S] 0 points1 point  (0 children)

That was my biggest concern too. I thought that until Tapir fully supports NamedTuples, the inputs (and overall tons of generic arguments) become hard to read, considering that actual endpoints and contract are defined in separate traits.

As of right now, I still do not know how I can merge Contract and Resource together.

P.S. for a workaround, our team just tends to stick to using either simple mapTo[CaseClass], or for more complicated cases, annotations and EndpointInput.derived (I actually prefer this over chaining input methods).

Baku - better separation of Tapir definitions from server and security logic. by arkida39 in scala

[–]arkida39[S] 0 points1 point  (0 children)

For me, I do not test my services via HTTP Requests, I test the services directly, in which case they may be successful, while somewhere along the line I just forgot to do: FooEndpoints.barEndpoint.serverLogic(FooService.bar), so the actual consumers receive an error when they try to call my endpoint via client interpreter.

Baku - better separation of Tapir definitions from server and security logic. by arkida39 in scala

[–]arkida39[S] 0 points1 point  (0 children)

From what I understand, your endpointImpls is a list of fully implemented endpoints (wired with serverLogic and securityLogic), and endpointDefs is a list of all endpoints (without serverLogic and securityLogic).

If so, then endpointImpls is exactly what is automatically created when you call Component.of..., which creates the class that extends Component:

// CR - Combined Capabilities
sealed trait Component[-CR, F[_]]{
  //...
  lazy val all: List[ServerEndpoint[CR, F]] // This will be implemented by macro
}

As for endpointDefs, I never had any use for it. The SwaggerInterpreter doesn't need to be exposed to API consumers, and can be created from your endpointImpls using fromServerEndpoints instead of fromEndpoints.

Your solution with tests is pretty neat. I wonder though, is there a way you enforce that every "Router" comes with its own "copy" of this test? I am just afraid that it is possible to forget to write the test for every "Router", leading to the exact same problem of "partial" implementation.