Chained ActionFunction stucks

Hi,

I’ve got two issues:

1st issue

class UserRequest[A](val user: Option[UserView], request: Request[A]) extends WrappedRequest[A](request)
class OrganisationRequest[A](val organisationId: OrganisationId, val userId: UserId, val role: Role, request: UserRequest[A]) extends WrappedRequest[A](request)

class UserAction @Inject()(val parser: BodyParsers.Default, env: Environment, organisationDAO: OrganisationDAO)(implicit val executionContext: ExecutionContext, configuration: Configuration)
  extends ActionBuilder[UserRequest, AnyContent] with ActionTransformer[Request, UserRequest] {
  override def transform[A](request: Request[A])= Future.successful {
    new UserRequest[A](user = request.jwtSession.getAs[UserView]("user"), request)
  }
}


case class SecuredControllerComponents @Inject()(
                                                  userAction: UserAction,
                                                  actionBuilder: DefaultActionBuilder,
                                                  parsers: PlayBodyParsers,
                                                  messagesApi: MessagesApi,
                                                  langs: Langs,
                                                  fileMimeTypes: FileMimeTypes,
                                                  executionContext: scala.concurrent.ExecutionContext
                                                ) extends ControllerComponents

class SecuredController @Inject()(scc: SecuredControllerComponents, organisationDAO: OrganisationDAO)(implicit val executionContext: ExecutionContext, configuration: Configuration)
  extends AbstractController(scc) {
  def organisationActionOn(organisationId: OrganisationId)(implicit executionContext: ExecutionContext) = new ActionRefiner[UserRequest, OrganisationRequest] {
    def executionContext: ExecutionContext = executionContext

    def refine[A](request: UserRequest[A]) = Future.successful {
      request.user.map { user =>
        organisationDAO
          .getOrganisationRoleByOrganisationIdForUserId(user.id, organisationId)
          .map(role => Right(new OrganisationRequest[A](organisationId, user.id, role, request)))
          .getOrElse(Left(InternalServerError.refreshJwtSession(request)))
      }.getOrElse(Left(Unauthorized))
    }
  }

  def adminFilter(implicit executionContext: ExecutionContext) = new ActionFilter[OrganisationRequest] {
    override protected def executionContext: ExecutionContext = executionContext

    override protected def filter[A](request: OrganisationRequest[A]) = Future.successful {
      if (request.role == ORGANISATION_ADMIN || request.role == ORGANISATION_MEMBER) None
      else Some(Forbidden.refreshJwtSession(request))
    }
  }

  def organisationAdminAction(organisationId: OrganisationId) = scc.userAction. andThen(organisationActionOn(organisationId)).andThen(adminFilter)
}

This code is just a https://www.playframework.com/documentation/2.7.x/ScalaActionsComposition impl. I got a Request, I check if there is JwtSession on it, then I transform it to UserRequest. Then I refine to OrganisationRequest to add user + organisation informations. Finally I filter if the user is authorized to access to the organisation given.

Currently it stucks to the refine impl. For a Left(Unauthorized) it outputs nothing and stucks. The query (using curl) just timeouts. No exceptions throwed.

2nd issue

The second is about to override the invokeBlock method to refresh the jwtSession. I don’t know where to place it.

Globally

I’m pretty sure I did a wrong usage of https://www.playframework.com/documentation/2.7.x/ScalaActionsComposition, but I think I exactly followed the documentation.

Thanks for you help.

Previously I was directly using ActionBuilderImpl with override invokeBlock, but the doc shows this clean way so I tried to impl it with failure.

Bump this message.

Hey @KannarFr, better if you can create a reproducer. I would also recommend to share some thread dump to better understand where it stucks.

Hi @marcospereira, thank for the answer :).

Well it’s just some rename on https://www.playframework.com/documentation/2.7.x/ScalaActionsComposition#Different-request-types example.

These is an example:

So here are the two request model:

class UserRequest[A](val user: Option[UserView], request: Request[A]) extends WrappedRequest[A](request)
class AuthenticatedUserRequest[A](val user: UserView, request: Request[A]) extends WrappedRequest[A](request)

Now let’s define UserAction as transform Request to UserRequest:

class UserAction @Inject()(val parser: BodyParsers.Default, env: Environment, organisationDAO: OrganisationDAO)(implicit val executionContext: ExecutionContext, configuration: Configuration)
  extends ActionBuilder[UserRequest, AnyContent] with ActionTransformer[Request, UserRequest] {
  override def transform[A](request: Request[A])= Future.successful {
    println("transforms Request to UserRequest (with optional userview)")
    new UserRequest[A](user = request.jwtSession.getAs[UserView]("user"), request)
  }
}

Then define a refiner to verify the user really exists:

  def authenticatedUserAction()(implicit executionContext: ExecutionContext) = new ActionRefiner[UserRequest, AuthenticatedUserRequest] {
    def executionContext: ExecutionContext = executionContext

    override protected def refine[A](request: UserRequest[A]) = Future.successful {
      println("refines UserRequest (with optional userview) to AuthenticatedUserRequest (with userview)")
      val yo = request.user.map { user =>
        Right(new AuthenticatedUserRequest(user, request))
      }.getOrElse(Left(Unauthorized))
      println(yo)
      yo
    }
  }

Then make a chain of actions as Request to UserRequest to AuthenticatedUserRequest:

def userAction = userAction.andThen(authenticatedUserAction) { request =>
   println("hello")
}

Here is the output:

transforms Request to UserRequest (with optional userview)
refines UserRequest (with optional userview) to AuthenticatedUserRequest (with userview)
Right(GET /v1/users/user_f971a8a9-750a-45f9-a31c-bf3eb9cf7f79/organisations)

As you can see after refine call, nothing happens and the request just timeout.

And how in all this action composition put a block(request)? Maybe that is the missing point an override of invokeBlock somewhere. And if it’s not enough, I will create a quick repo on github to reproduce it tomorrow.

No body to help?

So I was able to reproduce this and the problem seems like in

   def executionContext: ExecutionContext = executionContext

   override protected def refine[A](request: UserRequest[A]) = Future.successful {
     println("refines UserRequest (with optional userview) to AuthenticatedUserRequest (with userview)")
     val yo = request.user.map { user =>
       Right(new AuthenticatedUserRequest(user, request))
     }.getOrElse(Left(Unauthorized))
     println(yo)
     yo
   }
 }

Where the implicit val executionContext: ExecutionContext should be something like implicit val ec: ExecutionContext and then in the method set def executionContext: ExecutionContext = ec. I ran into the same problem and then did this and then my “action” pipeline was no longer getting stuck