Coder Social home page Coder Social logo

nozzle's Introduction

Nozzle

Principled spray.io web services with minimal boilerplate.

Updated documentation coming soon.

Dependency

libraryDependencies += "io.buildo" %% "nozzle" % <version>

nozzle's People

Contributors

calippo avatar danielegallingani avatar drprofesq avatar gabro avatar lucacioria avatar utaal avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

utaal

nozzle's Issues

Add support of Java 8 `java.time` classes

Requirements

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

Specs

  • At least the following classes must be supported:
    • 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.
  • Support must be provided for both:
    • JSON serialization and deserialization as RootJsonFormats
    • Slick database columns conversion as MappedColumnTypes

Config case class should be an object

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")
  )
}

Nozzle Server should optionally accept an external ActorSystem

stories

  • As a service I would like to use only one ActorSystem to handle both http Server and http Client so that I can gracefully kill the Client ActorSystem if the Server fails (e.g. bind to port).

requirements

Nozzle Server should optionally accept an external ActorSystem.

Allow customizing error status codes

requirements

Currently there's no clean way of customizing the mapping between WebError and StatusCode

specs

Add a level of indirection over webresult.MarshallingSupport and provide a default webresult.DefaultMarshallingSupport to be mixed in by the user.

[CORS] add the ability to customize `allowedHeaders`

requirements

currently only allowedOrigin is configurable
I don't see why allowedHeaders (and methods?) shouldn't be

specs

{optional: describe technical specs to implement this feature, if not obvious}

misc

{optional: other useful info}

Add https

Add support for HTTPS to nozzle using spray TLS

[Controllers] eitherT interface enhancement

requirements

We 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].

specs

proposed solution

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:

  • validation and action are not generalized on purpose.
  • in our experience CtrlFlow[Unit] and FutureCtrlFlow[Unit] cover most of the common situations you need to write that boilerplate.

misc

Discussion so far

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:

  1. we like to know what method we're using, especially when a high level of magic is involved
  2. 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

Boot timeout should be configurable

requirements

Server boot timeout should be configurable instead of using the hardcoded 5 seconds one.

specs

  • Add a bootTimeout parameter of type Duration to Server class constructor.
  • Raise default value to 10 seconds

Consider adding a default handler to map spray rejections to JSend

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.

Abstract over WebError

requirements

The WebError family is rather arbitrary.
We should allow defining custom errors.

JSON Error serialization

description

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.