Principled spray.io web services with minimal boilerplate.
Updated documentation coming soon.
libraryDependencies += "io.buildo" %% "nozzle" % <version>
License: MIT License
Server boot timeout should be configurable instead of using the hardcoded 5 seconds
one.
bootTimeout
parameter of type Duration
to Server
class constructor.10 seconds
currently only allowedOrigin
is configurable
I don't see why allowedHeaders
(and methods?) shouldn't be
{optional: describe technical specs to implement this feature, if not obvious}
{optional: other useful info}
We must be able to serialize with or without JSend
Derive a generic implementation from https://github.com/buildo/nozzle/blob/1.0-master/src/main/scala/webresult/JSendMarshallingSupport.scala
statusCode
should not be included in GenericError
.
HTTP status code should not be decided by the controller.
Currently there's no clean way of customizing the mapping between WebError
and StatusCode
Add a level of indirection over webresult.MarshallingSupport
and provide a default webresult.DefaultMarshallingSupport
to be mixed in by the user.
Add support for HTTPS to nozzle using spray TLS
Configs are currently written as:
private[this] case class LocalConfig(beAwesome: Boolean)
private[this] val localConfig = config.get { conf =>
LocalConfig(conf.getBoolean("beAwesome"))
}
why is LocalConfig a case class? I think it should be a singleton. I don't see situations when we're going to reuse that class (reusing that case class is probably a mistake).
I think we should use object
instead of case class
for config as best practice.
private[this] object localConfig {
val beAwsome: Boolean = config.get { conf => conf.getBoolean("beAwsome") }
}
the only problem I see is that it gets kind of cumbersome for more variables
private[this] object localConfig {
val (beAwsome: Boolean, beCumbersome: Boolean) = config.get { conf =>
(conf.getBoolean("beAwsome") ,
conf.getBoolean("beCumbersome")
}
}
vs
private[this] case class LocalConfig(beAwesome: Boolean, beCumbersome: Boolean)
private[this] val localConfig = config.get { conf =>
LocalConfig(
conf.getBoolean("beAwesome"),
conf.getBoolean("beCumbersome")
)
}
Once a certain directive has been matched- invalid parameters rejections should result in a 4** and prevent route fallback.
services should optionally be able to configure bootTimeout
Currently in our internal projects we provide custom rejection handlers that produce a JSendFailure
, then we leave the rest to the default rejection handler, which produces meaningful responses, with appropriate HTTP error codes, but that is in plaintext.
It should be trivial to add a default JSend wrapper for the default rejection handler response, using mapHttpResponse
. Something along the lines of
def toJSend(res: HttpResponse): HttpResponse =
res.withEntity(HttpBody(ContentType.`application/json`,
JSendFailure(message = res.entity.asString.toJson).toJson))
implicit val jsonRejectionHandler = RejectionHandler {
case rejections => mapHttpResponse(toJSend)(RejectionHandler.Default(rejections))
}
Not sure the above compiles, but you get the idea.
@utaal, thoughts?
I know @federico-pellegatta is trying to implement this in one of our projects.
We must add support to Java 8 java.time
.
Have a look to https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html
LocalDate
: A date without a time-zone in the ISO-8601
calendar system, such as 2007-12-03
.LocalDateTime
: A date-time without a time-zone in the ISO-8601
calendar system, such as 2007-12-03T10:15:30
.LocalTime
: A time without a time-zone in the ISO-8601
calendar system, such as 10:15:30
.Duration
: A time-based amount of time, such as 34.5 seconds
.RootJsonFormat
sMappedColumnType
sWe want to avoid people write things like
for {
a <- eitherT(something.map(_.point[CtrlFlow]))
b <- eitherT(something.point[Future])
}
At the same time, we don't want to hide monad transformers too much.
We'd like to be clear why in user <- eitherT(Future(\/-(user)))
the type of user
is User
and not \/[_, User].
Me and @federico-pellegatta decided to introduce in a project these helpers to avoid people to get mad finding the right EitherT method
object CtrlHelper {
def validationEitherT(validation: CtrlFlow[Unit])(implicit executionContext: ExecutionContext): FutureCtrlFlow[Unit] = EitherT.fromDisjunction(validation)
def actionEitherT(action: Future[Unit])(implicit executionContext: ExecutionContext): FutureCtrlFlow[Unit] = EitherT.right(action)
def eitherT[T](obj: Future[CtrlFlow[T]])(implicit executionContext: ExecutionContext): FutureCtrlFlow[T] = EitherT.eitherT(obj)
}
observations about the proposed solution:
CtrlFlow[Unit]
and FutureCtrlFlow[Unit]
cover most of the common situations you need to write that boilerplate.If you're going for an alias to keep scalaz out of the radars, may I suggest something that doesn't end with eitherT?
gabro
EitherT name is fine, is not bound to scalaz and we are not trying to hide what we're doing.
My problem is that EitherT.fromDisjunction and EitherT.right are too hidden in scalaz code. In other project we are not using EitherT.fromDisjunction and EitherT.right even though they would be useful.
We just couldn't easily find them.That's why we added an alias with a meaningful signature in the project.
claudio
My point is
validationEitherT[A] returns a FutureCtrlFlow[A], i.e. the name of the method leaks the underlying abstraction.
What about toFutureCtrlFlow (possibly overloaded)
gabro
That was actually our first experiment.
I was kind of ok with that, but we came across a couple of problems:
- we like to know what method we're using, especially when a high level of magic is involved
- overload method clashes on Future[_] -> FutureCtrlFlow
We didn't implement generic methods (with the exception of eitherT[T]). validationEitherT and actionEitherT are there only for FutureCtrlFlow[Unit]. We are calling validation something that returns /[SomeError, Unit] and action something that returns Future[Unit].
It was meant to be easier for the developer to understand, if it was not clear to you we are probably doing something wrong.
claudio
The WebError family is rather arbitrary.
We should allow defining custom errors.
Nozzle Server should optionally accept an external ActorSystem.
We should provide in nozzle a default (overridable) error serialization in JSON.
Right now some of the errors such as bad requests are serialized with an HTML payload without any particular reason: let's JSONize them all!
Authentication issues should probably always be 401s.
Moved from https://github.com/buildo/infra/issues/44
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.