Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

concat helper method to chain together multiple service implementations, #59 #256

Merged
merged 1 commit into from
Jun 29, 2018

Conversation

patriknw
Copy link
Member

I have to test and add documentation, but wanted to see if you agree with the approach first.

Refs #59

Copy link
Contributor

@raboof raboof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the utility looks useful.

Looking at it again I wonder if it wouldn't make sense to have it in akka-http-core even.

As for naming, though we started with it perhaps 'concat' (as we've called it before) isn't great since unlike Route.concat it also ends with returning a 404 (which I think is useful). WDYT?

import akka.http.javadsl.model.HttpRequest
import akka.http.javadsl.model.HttpResponse
import akka.http.javadsl.model.StatusCodes
import akka.japi.{Function => JFunction}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find a reason not to use java.util.function.Function here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change that occurrence also together with the other one

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that in this PR

result.completeExceptionally(new UnsupportedOperationException("Unexpected path"));
return result;
} else {
return notFound;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed this to NOT_FOUND because I guess that's a more valid response if a single handler is used, and felt better to use that as the signal to continue with next handler in concat rather than using exception

val service: HttpRequest => Future[HttpResponse] = GreeterServiceHandler(new GreeterServiceImpl(mat))
.orElse { case _ => Future.successful(HttpResponse(StatusCodes.NotFound)) }
val service: HttpRequest => Future[HttpResponse] =
ServiceHandler(GreeterServiceHandler(new GreeterServiceImpl(mat)))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would a single implementation be "wrong" if used without the NotFound?

val service: HttpRequest => Future[HttpResponse] = GreeterServiceHandler(new GreeterServiceImpl(mat))

It might be easy to miss that?
Should we change GreeterServiceHandler.apply to be a total function appending the NotFound by default, and make the partial function another method? GreeterServiceHandler.partial?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then the ServiceHandler.apply that I added wouldn't be needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds quite elegant!

private val notFound = Future.successful(HttpResponse.create().withStatus(StatusCodes.NOT_FOUND))

/**
* Creates a `HttpRequest` to `HttpResponse` handler that can be used in for example `Http.get(system).bindAndHandleAsync`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe as [[]] links? (the response/request)

@patriknw
Copy link
Member Author

I think the utility looks useful.

I think that for javadsl it's essential, "impossible" to compose several otherwise

@patriknw
Copy link
Member Author

As for naming, though we started with it perhaps 'concat' (as we've called it before) isn't great since unlike Route.concat it also ends with returning a 404 (which I think is useful).

concatOrNotFound?

@@ -24,18 +24,17 @@ import akka.grpc.javadsl.GrpcExceptionHandler;
import static @{service.packageName}.@{service.name}.Serializers.*;

public class @{service.name}HandlerFactory {
private static CompletionStage<HttpResponse> notFound = CompletableFuture.completedFuture(
HttpResponse.create().withStatus(StatusCodes.NOT_FOUND));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Responding with an error immediately instead of going through the GrpcExceptionHandler seems nice, but we'd want to return a GrpcResponseHelpers.status(Status.UNIMPLEMENTED) at least for missing methods

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is for the path segments, the method match is still using UnsupportedOperationException

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good

@@ -24,18 +24,17 @@ import akka.grpc.javadsl.GrpcExceptionHandler;
import static @{service.packageName}.@{service.name}.Serializers.*;

public class @{service.name}HandlerFactory {
private static CompletionStage<HttpResponse> notFound = CompletableFuture.completedFuture(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx

new JFunction[HttpRequest, CompletionStage[HttpResponse]] {
override def apply(req: HttpRequest): CompletionStage[HttpResponse] =
cont(req, notFound, handlers.toList).toJava
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, since we generate functions and not javadsl.Route, looks good

@ktoso
Copy link
Contributor

ktoso commented Jun 27, 2018

concatOrNotFound?

Sounds good to me (from an consistency with akka-http naming style perspective)

@patriknw
Copy link
Member Author

Updated with the things we discussed so far. Will continue with test and docs

@patriknw
Copy link
Member Author

Added documentation and test.
Also changed the implementation for javadsl to use plain CompletionStage.

This should be ready for final review now.

@patriknw
Copy link
Member Author

and the reason for the japi.Function was that bindAndHandleAsync expected that, so I reverted that change

@@ -0,0 +1,49 @@
/**
* Copyright (C) 2018 Lightbend Inc. <http://www.lightbend.com>
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think sbt-headers expects a newline after this

val greeterHandler = GreeterServiceHandler(new GreeterServiceImpl)
val echoHandler = EchoServiceHandler.partial(new EchoServiceImpl)
val greeterHandler = GreeterServiceHandler.partial(new GreeterServiceImpl)
val serviceHandler = ServiceHandler.concatOrNotFound(echoHandler, greeterHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@chbatey chbatey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

GreeterServiceHandlerFactory.create(new GreeterServiceImpl(mat), mat);
Function<HttpRequest, CompletionStage<HttpResponse>> echoService =
EchoServiceHandlerFactory.create(new EchoServiceImpl(), mat);
Function<HttpRequest, CompletionStage<HttpResponse>> serviceHandlers =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

});
}

private static HttpsConnectionContext serverHttpContext() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a few of these :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, finding a nicer alternative for that is part of #89

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it should hurt your eyes so that we make it happen :)

*
* Use `@{service.name}Handler.apply` if the server is only handling one service.
*/
def partial(implementation: @service.name)(implicit mat: Materializer): PartialFunction[HttpRequest, Future[HttpResponse]] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@raboof raboof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently fails the headerCheck on travis, after that LGTM!

@patriknw patriknw force-pushed the wip-59-concat-patriknw branch from 9a8a900 to 1f14611 Compare June 29, 2018 09:39
@patriknw
Copy link
Member Author

Fixed headerCheck, rebased and squashed

@patriknw patriknw merged commit 9a606d0 into master Jun 29, 2018
@patriknw patriknw deleted the wip-59-concat-patriknw branch June 29, 2018 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants