Skip to content

Commit

Permalink
Return user data in workouts
Browse files Browse the repository at this point in the history
  • Loading branch information
khy committed Feb 5, 2017
1 parent 646a264 commit 5f4193a
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 36 deletions.
17 changes: 10 additions & 7 deletions modules/apis/core/app/controllers/account/AccountController.scala
Expand Up @@ -5,6 +5,7 @@ import scala.concurrent.Future
import play.api.mvc._
import play.api.libs.json.Json
import play.api.libs.concurrent.Execution.Implicits._
import io.useless.play.http.QueryStringUtil._
import io.useless.play.json.account.AccountJson._

import controllers.core.auth.Auth
Expand All @@ -31,21 +32,23 @@ object AccountController extends Controller {
}

def find = Auth.async { request =>
val optGuids = request.richQueryString.get[UUID]("guid")
val optEmail = request.queryString.get("email").flatMap(_.headOption)
val optHandle = request.queryString.get("handle").flatMap(_.headOption)

def accountResponse(optAccount: Option[Account]) = {
val accounts = optAccount.map(_.toPublic).toSeq
Ok(Json.toJson(accounts))
def accountResponse(accounts: Seq[Account]) = {
Ok(Json.toJson(accounts.map(_.toPublic)))
}

if (optEmail.isDefined) {
Account.forEmail(optEmail.get).map(accountResponse(_))
if (optGuids.isDefined) {
Account.forGuids(optGuids.get).map(accountResponse)
} else if (optEmail.isDefined) {
Account.forEmail(optEmail.get).map { optAccount => accountResponse(optAccount.toSeq) }
} else if (optHandle.isDefined) {
Account.forHandle(optHandle.get).map(accountResponse(_))
Account.forHandle(optHandle.get).map { optAccount => accountResponse(optAccount.toSeq) }
} else {
Future.successful {
UnprocessableEntity("email or handle must be specified.")
UnprocessableEntity("guid, email or handle must be specified.")
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions modules/apis/core/app/models/account/Account.scala
Expand Up @@ -65,6 +65,15 @@ object Account {
findOne(BSONDocument("_id" -> guid))
}

def forGuids(guids: Seq[UUID]): Future[Seq[Account]] = {
val documents = collection.
find(BSONDocument("_id" -> BSONDocument("$in" -> guids))).
cursor[AccountDocument].
collect[List]()

documents.map { documents => documents.map { new Account(_) } }
}

def forAccessToken(guid: UUID): Future[Option[Account]] = {
findOne(BSONDocument("access_tokens.guid" -> guid))
}
Expand Down
1 change: 1 addition & 0 deletions modules/apis/workouts/app/models/JsonImplicits.scala
Expand Up @@ -3,6 +3,7 @@ package models.workouts
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.data.validation.ValidationError
import io.useless.play.json.account.UserJson._

object JsonImplicits {

Expand Down
5 changes: 3 additions & 2 deletions modules/apis/workouts/app/models/Normalized.scala
Expand Up @@ -2,6 +2,7 @@ package models.workouts

import java.util.UUID
import java.time.ZonedDateTime
import io.useless.account.User

package core {

Expand Down Expand Up @@ -54,9 +55,9 @@ case class Workout(
score: Option[String],
tasks: Option[Seq[core.SubTask]],
createdAt: ZonedDateTime,
createdByAccount: UUID,
createdBy: User,
deletedAt: Option[ZonedDateTime],
deletedByAccount: Option[UUID]
deletedBy: Option[User]
)

case class SubTask(
Expand Down
6 changes: 4 additions & 2 deletions modules/apis/workouts/app/services/ServiceComponents.scala
@@ -1,13 +1,15 @@
package services.workouts

import io.useless.client.account.AccountClientComponents

import db.workouts.DbConfigComponents

trait ServiceComponents {

self: DbConfigComponents =>
self: DbConfigComponents with AccountClientComponents =>

lazy val movementsService = new MovementsService(dbConfig)

lazy val workoutsService = new WorkoutsService(dbConfig, movementsService)
lazy val workoutsService = new WorkoutsService(dbConfig, movementsService, accountClient)

}
26 changes: 20 additions & 6 deletions modules/apis/workouts/app/services/WorkoutsService.scala
Expand Up @@ -5,7 +5,9 @@ import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.json.Json
import slick.backend.DatabaseConfig
import io.useless.Message
import io.useless.account.User
import io.useless.accesstoken.AccessToken
import io.useless.client.account.AccountClient
import io.useless.validation._
import io.useless.pagination._
import io.useless.exception.service._
Expand All @@ -16,14 +18,26 @@ import models.workouts.JsonImplicits._

class WorkoutsService(
dbConfig: DatabaseConfig[Driver],
movementsService: MovementsService
movementsService: MovementsService,
accountClient: AccountClient
) {

import dbConfig.db
import dbConfig.driver.api._

def db2api(records: Seq[WorkoutRecord]): Future[Seq[Workout]] = {
Future.successful(
def db2api(records: Seq[WorkoutRecord])(implicit ec: ExecutionContext): Future[Seq[Workout]] = {
val userGuids = records.flatMap { record =>
Seq(record.createdByAccount) ++ record.deletedByAccount.toSeq
}

val futUsers = accountClient.findAccounts(guids = userGuids).map { accounts =>
accounts.flatMap {
case user: User => Some(user)
case _ => None
}
}

futUsers.map { users =>
records.map { record =>
Json.fromJson[core.Workout](record.json).fold(
error => throw new InvalidState(s"Invalid workout JSON from DB [$error]"),
Expand All @@ -36,13 +50,13 @@ class WorkoutsService(
score = workout.score,
tasks = workout.tasks,
createdAt = record.createdAt,
createdByAccount = record.createdByAccount,
createdBy = users.find(_.guid == record.createdByAccount).getOrElse(User.Anon),
deletedAt = record.deletedAt,
deletedByAccount = record.deletedByAccount
deletedBy = users.find(_.guid == record.deletedByAccount)
)
)
}
)
}
}

def findWorkouts(
Expand Down
6 changes: 6 additions & 0 deletions modules/lib/src/main/scala/io/useless/account/User.scala
Expand Up @@ -58,4 +58,10 @@ object User {
new AuthorizedUser(guid, email, handle, name)
}

val Anon: User = new PublicUser(
guid = UUID.fromString("00000000-0000-0000-0000-000000000000"),
handle = "anon",
name = Some("Anon E. Muss")
)

}
@@ -1,7 +1,7 @@
package io.useless.client.account

import java.util.UUID
import scala.concurrent.Future
import scala.concurrent.{ExecutionContext, Future}
import play.api.Application
import play.api.libs.ws.WSClient

Expand All @@ -22,10 +22,12 @@ object AccountClient extends Mockable[AccountClient] {

trait AccountClient {

def getAccount(guid: UUID): Future[Option[Account]]
def getAccount(guid: UUID)(implicit ec: ExecutionContext): Future[Option[Account]]

def getAccountForEmail(email: String): Future[Option[Account]]
def findAccounts(guids: Seq[UUID])(implicit ec: ExecutionContext): Future[Seq[Account]]

def getAccountForHandle(handle: String): Future[Option[Account]]
def getAccountForEmail(email: String)(implicit ec: ExecutionContext): Future[Option[Account]]

def getAccountForHandle(handle: String)(implicit ec: ExecutionContext): Future[Option[Account]]

}
@@ -1,20 +1,25 @@
package io.useless.client.account

import java.util.UUID
import scala.concurrent.Future
import scala.concurrent.{ExecutionContext, Future}

import io.useless.account.{ Account, AuthorizedUser, User }

class MockAccountClient(
accounts: Seq[Account]
) extends AccountClient {

def getAccount(guid: UUID) = {
val optAccount = accounts.find { _.guid == guid }
Future.successful(optAccount)
def getAccount(guid: UUID)(implicit ec: ExecutionContext) = {
findAccounts(Seq(guid)).map(_.headOption)
}

def findAccounts(guids: Seq[UUID])(implicit ec: ExecutionContext) = {
Future.successful(accounts.filter { account =>
guids.contains(account.guid)
})
}

def getAccountForEmail(email: String) = {
def getAccountForEmail(email: String)(implicit ec: ExecutionContext) = {
val optAccount = accounts.find { account =>
account match {
case user: AuthorizedUser => user.email == email
Expand All @@ -25,7 +30,7 @@ class MockAccountClient(
Future.successful(optAccount)
}

def getAccountForHandle(handle: String) = {
def getAccountForHandle(handle: String)(implicit ec: ExecutionContext) = {
val optAccount = accounts.find { account =>
account match {
case user: User => user.handle == handle
Expand Down
@@ -1,7 +1,7 @@
package io.useless.client.account

import java.util.UUID
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext
import play.api.Application
import play.api.libs.json.Json
import play.api.libs.ws.WSClient
Expand All @@ -21,16 +21,21 @@ class PlayAccountClient(
ResourceClient(client, baseUrl, authGuid.toString)
}

def getAccount(guid: UUID) = {
val path = "/accounts/%s".format(guid.toString)
resourceClient.get(path)
def getAccount(guid: UUID)(implicit ec: ExecutionContext) = {
resourceClient.get(s"/accounts/${guid}")
}

def getAccountForEmail(email: String) = {
def findAccounts(guids: Seq[UUID])(implicit ec: ExecutionContext) = {
resourceClient.find("/accounts", guids.map { guid =>
"guid" -> guid.toString
}:_*).map(_.items)
}

def getAccountForEmail(email: String)(implicit ec: ExecutionContext) = {
resourceClient.find("/accounts", "email" -> email).map(_.items.headOption)
}

def getAccountForHandle(handle: String) = {
def getAccountForHandle(handle: String)(implicit ec: ExecutionContext) = {
resourceClient.find("/accounts", "handle" -> handle).map(_.items.headOption)
}

Expand Down
@@ -1,8 +1,8 @@
package io.useless.client.account

import org.scalatest.FunSpec
import org.scalatest.Matchers
import java.util.UUID
import scala.concurrent.ExecutionContext.Implicits.global
import org.scalatest.{FunSpec, Matchers}

import io.useless.account.User
import io.useless.test.Await
Expand Down
@@ -1,8 +1,9 @@
package io.useless.client.account

import java.util.UUID
import scala.concurrent.ExecutionContext.Implicits.global
import org.scalatest.FunSpec
import org.scalatest.Matchers
import java.util.UUID
import play.api.libs.ws.{WS, WSResponse}
import play.api.libs.json._
import org.scalatestplus.play.OneAppPerSuite
Expand Down

0 comments on commit 5f4193a

Please sign in to comment.