jyoti
jyoti

Reputation: 79

For Comprehension Return type

I am trying to return Future[Vector[String]] from for comprehension but I am getting Future[Nothing] as my return type. How to convert Future[Nothing] return type to Future[Vector[String]]? Here is the code snippet:

def findProjectId(projectName: String, userId: String): Future[Nothing] = {
    for {
      projectIds <- dBService.fetchProjectIdByProjectName(projectName) // projectIds is Vector[String]
      pid <- projectIds
      projectId <- dBService.filterProjectId(pid, userId) // ProjectId is Vector[String]
      if projectId.nonEmpty
    } yield {
      projectId map {
        projectid =>
          dBService.fetchRoleId map { // fetchRoleId returns Future[Vector[String]]
            case Vector(rid) => dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
              listOfcollabs =>
                if (listOfcollabs.nonEmpty) {
                  listOfcollabs ++ Vector(userId)
                }
                else {
                  Vector(userId)
                }
            }
          }
      }
    }
  }

Signatures of dbService methods are:

  def checkCollaboratorOrAdmin(userId: String, projectId: String, roleId: String): Future[Vector[String]] = {
    dbComponent.db.run(checkAdminOrCollab(roleId))
  }

  def fetchRoleId: Future[Vector[String]] = {
    dbComponent.db.run(fetchRole)
  }

  def fetchCollaborator(roleId: String, projectId: String): Future[Vector[String]] = {
    dbComponent.db.run(fetchCollaborators(roleId, projectId))
  }

  def fetchProjectIdByProjectName(projectName: String) = {
    dbComponent.db.run(fetchProjectId(projectName))
  }

  def filterProjectId(projectId: String, userId: String) = {
    dbComponent.db.run(filterProjectIdByUserId(projectId, userId))
  }

These methods in turn call:

  def fetchRoleId(userId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select distinct(role_id) from project_user_role where user_id=$userId and project_id=$projectId""".as[String]
  }

  def checkAdminOrCollab(roleId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select role_id from roles where role_id=$roleId and role_name="collaborator" """.as[String]
  }

  def fetchRole(): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select role_id from roles where role_name="collaborator"""".as[String]
  }

  def fetchCollaborators(roleId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select user_id from project_user_role where roleId=$roleId and project_id=$projectId""".as[String]
  }

  def fetchProjectId(projectName: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select project_id from projects where project_name=$projectName""".as[String]
  }

  def filterProjectIdByUserId(projectId: String, userId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select project_id from project_user_role where project_id=$projectId and user_id=$userId""".as[String]
  }

Upvotes: 0

Views: 626

Answers (1)

Tim
Tim

Reputation: 27356

I'm guessing that the Future[Nothing] comes from IntelliJ hints, rather than the compiler itself. The compiler gives a couple of errors, the first coming from this line:

pid <- projectIds

This is the error I get:

Test.scala:47:13: type mismatch;

[error] found : scala.collection.immutable.Vector[Int]

[error] required: scala.concurrent.Future[?]

The problem is that the for expression is trying to build a value of type Future[_] using using map, flatMap and filter calls. (The collection type of a for is the collection type of the first expression in the for). flatMap on Future takes a Future and turns Future[Future[_]] into Future[_]. However you are giving it a Vector which is not supported.

I'm also unsure of the broader logic because you have two nested Vectors (projectIds and listOfCollabs) but no mechanism for flattening this into a single vector.

You probably want to look at using Future.traverse or Future.sequence to turn lists of Future into Future[List].

It would also make sense to break this down into some named functions to make the code more comprehensible and give a better chance of isolating the problem.

Update

This code will call the appropriate functions and return the results. The return type is Future[Vector[Vector[Vector[String]]]] because each dBService call returns Future[Vector[String]] so you get nested Vectors. It is not clear from the question how to flatten this into the result you want, but it should be straightforward. (The result of the last call is flattened by the case statement which is why there 4 dBService calls but only 3 nested Vectors )

def findProjectId(projectName: String, userId: String): Future[Vector[Vector[Vector[String]]]] = {
  dBService.fetchProjectIdByProjectName(projectName).flatMap { projectIds =>
    Future.traverse(projectIds) { pid =>
      dBService.filterProjectId(pid, userId).flatMap { projectId =>
        Future.traverse(projectId) { projectid =>
          dBService.fetchRoleId.flatMap { // fetchRoleId returns Future[Vector[String]]
            case Vector(rid) =>
              dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
                _ ++ Vector(userId)
              }
          }
        }
      }
    }
  }
}

It may be that you can just take the first element of some of these Vectors which would simplify the code.

Future.traverse(collection)(f) is equivalent to Future.sequence(collection.map(f)) but gives better layout and may be more efficient.

Upvotes: 5

Related Questions