Reputation: 79
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
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.
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 Vector
s. 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