Skip to content

Commit

Permalink
Revert "Make Project and TaskType names unique by Organization (#5334)"
Browse files Browse the repository at this point in the history
This reverts commit d06b56f.
  • Loading branch information
fm3 authored Apr 8, 2021
1 parent 5b5c120 commit 7a0e07b
Show file tree
Hide file tree
Showing 43 changed files with 310 additions and 455 deletions.
2 changes: 0 additions & 2 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
[Commits](https://github.com/scalableminds/webknossos/compare/21.04.0...HEAD)

### Added
- The names of Task Types and Projects no longer need to be globally unique, instead only within their respective organization. [#5334](https://github.com/scalableminds/webknossos/pull/5334)
- Upgraded UI library antd to version 4, creating a slightly more modern look and behavior of many UI elements. [#5350](https://github.com/scalableminds/webknossos/pull/5350)

### Changed
- webKnossos is now part of the [image.sc support community](https://forum.image.sc/tag/webknossos). [#5332](https://github.com/scalableminds/webknossos/pull/5332)
- Meshes that are imported by the user in the meshes tab are now rendered the same way as generated isosurface meshes. [#5326](https://github.com/scalableminds/webknossos/pull/5326)
- In the new REST API version 4, projects are no longer referenced by name, but instead by id. [#5334](https://github.com/scalableminds/webknossos/pull/5334)

### Fixed
- Fixed a bug where some values in the project list were displayed incorrectly after pausing/unpausing the project. [#5339](https://github.com/scalableminds/webknossos/pull/5339)
Expand Down
1 change: 0 additions & 1 deletion MIGRATIONS.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md).

### Postgres Evolutions:
- [068-pricing-plan.sql](conf/evolutions/068-pricing-plan.sql)
- [069-tasktype-project-unique-per-orga.sql](conf/evolutions/069-tasktype-project-unique-per-orga.sql)
4 changes: 2 additions & 2 deletions app/controllers/InitialDataController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ Samplecountry
"sampleTaskType",
"Check those cells out!"
)
for { _ <- taskTypeDAO.insertOne(taskType, defaultOrganization._id) } yield ()
for { _ <- taskTypeDAO.insertOne(taskType) } yield ()
} else Fox.successful(())
}.toFox

Expand All @@ -208,7 +208,7 @@ Samplecountry
paused = false,
Some(5400000),
isBlacklistedFromReport = false)
for { _ <- projectDAO.insertOne(project, defaultOrganization._id) } yield ()
for { _ <- projectDAO.insertOne(project) } yield ()
}
} else Fox.successful(())
}.toFox
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/JobsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ class JobDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext)
if (celeryJobIds.isEmpty) Fox.successful(List())
else {
for {
r <- run(
rList <- run(
sql"select #$columns from #$existingCollectionName where celeryJobId in #${writeStructTupleWithQuotes(celeryJobIds)}"
.as[JobsRow])
parsed <- parseAll(r)
parsed <- Fox.combined(rList.toList.map(parse))
} yield parsed
}

Expand Down
85 changes: 2 additions & 83 deletions app/controllers/LegacyApiController.scala
Original file line number Diff line number Diff line change
@@ -1,103 +1,22 @@
package controllers

import com.mohiva.play.silhouette.api.Silhouette
import com.scalableminds.util.tools.Fox
import javax.inject.Inject
import models.project.ProjectDAO
import models.task.{TaskDAO, TaskService}
import oxalis.security.WkEnv
import play.api.http.HttpEntity
import play.api.libs.json.{JsArray, JsObject, JsValue, Json}
import play.api.mvc.{Action, AnyContent, PlayBodyParsers, Result}
import utils.ObjectId
import utils.WkConf

import scala.concurrent.ExecutionContext

class LegacyApiController @Inject()(annotationController: AnnotationController,
taskController: TaskController,
userController: UserController,
projectController: ProjectController,
projectDAO: ProjectDAO,
taskDAO: TaskDAO,
taskService: TaskService,
conf: WkConf,
sil: Silhouette[WkEnv])(implicit ec: ExecutionContext, bodyParsers: PlayBodyParsers)
extends Controller {

/* to provide v3, find projects by name */

def projectRead(name: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.read(project._id.toString)(request)
} yield result
}

def projectDelete(name: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.delete(project._id.toString)(request)
} yield result
}

def projectUpdate(name: String): Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.update(project._id.toString)(request)
} yield result
}

def projectTasksForProject(name: String,
limit: Option[Int] = None,
pageNumber: Option[Int] = None,
includeTotalCount: Option[Boolean]): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.tasksForProject(project._id.toString, limit, pageNumber, includeTotalCount)(request)
} yield result
}

def projectIncrementEachTasksInstances(name: String, delta: Option[Long]): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.incrementEachTasksInstances(project._id.toString, delta)(request)
} yield result
}

def projectPause(name: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.pause(project._id.toString)(request)
} yield result
}

def projectResume(name: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
project <- projectDAO.findOneByNameAndOrganization(name, request.identity._organization)
result <- projectController.resume(project._id.toString)(request)
} yield result
}

def taskListTasks: Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
for {
userIdOpt <- Fox.runOptional((request.body \ "user").asOpt[String])(ObjectId.parse)
projectNameOpt = (request.body \ "project").asOpt[String]
projectOpt <- Fox.runOptional(projectNameOpt)(projectName =>
projectDAO.findOneByNameAndOrganization(projectName, request.identity._organization))
taskIdsOpt <- Fox.runOptional((request.body \ "ids").asOpt[List[String]])(ids =>
Fox.serialCombined(ids)(ObjectId.parse))
taskTypeIdOpt <- Fox.runOptional((request.body \ "taskType").asOpt[String])(ObjectId.parse)
randomizeOpt = (request.body \ "random").asOpt[Boolean]
tasks <- taskDAO.findAllByProjectAndTaskTypeAndIdsAndUser(projectOpt.map(_._id),
taskTypeIdOpt,
taskIdsOpt,
userIdOpt,
randomizeOpt)
jsResult <- Fox.serialCombined(tasks)(taskService.publicWrites(_))
} yield Ok(Json.toJson(jsResult))
}

/* to provide v2, insert automatic timestamp in finish and info request */

def annotationFinishV2(typ: String, id: String): Action[AnyContent] =
Expand Down
123 changes: 62 additions & 61 deletions app/controllers/ProjectController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import com.mohiva.play.silhouette.api.actions.SecuredRequest
import com.scalableminds.util.accesscontext.GlobalAccessContext
import com.scalableminds.util.tools.DefaultConverters.BoolToOption
import com.scalableminds.util.tools.{Fox, FoxImplicits}
import javax.inject.Inject
import models.annotation.{AnnotationDAO, AnnotationService, AnnotationType}
import models.project._
import models.task._
import models.user.UserService
import net.liftweb.common.Empty
import oxalis.security.WkEnv
import play.api.i18n.Messages
import play.api.libs.json.{JsValue, Json}
import play.api.mvc.{Action, AnyContent}
import utils.ObjectId
import javax.inject.Inject
import play.api.mvc.{Action, AnyContent}

import scala.concurrent.ExecutionContext
import scala.concurrent.{ExecutionContext, Future}

class ProjectController @Inject()(projectService: ProjectService,
projectDAO: ProjectDAO,
Expand Down Expand Up @@ -49,67 +50,70 @@ class ProjectController @Inject()(projectService: ProjectService,
} yield Ok(Json.toJson(js))
}

def read(id: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
def read(projectName: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
js <- projectService.publicWrites(project)
} yield Ok(js)
} yield {
Ok(js)
}
}

def delete(id: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
def delete(projectName: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
_ <- bool2Fox(project.isDeletableBy(request.identity)) ?~> "project.remove.notAllowed" ~> FORBIDDEN
_ <- projectService.deleteOne(project._id) ?~> "project.remove.failure"
} yield JsonOk(Messages("project.remove.success"))
} yield {
JsonOk(Messages("project.remove.success"))
}
}

def create: Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
withJsonBodyUsing(Project.projectPublicReads) { project =>
for {
_ <- projectDAO
.findOneByNameAndOrganization(project.name, request.identity._organization)(GlobalAccessContext)
.reverse ?~> "project.name.alreadyTaken"
_ <- Fox
.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
_ <- projectDAO.insertOne(project, request.identity._organization) ?~> "project.creation.failed"
js <- projectService.publicWrites(project)
} yield Ok(js)
projectDAO.findOneByName(project.name)(GlobalAccessContext).futureBox.flatMap {
case Empty =>
for {
_ <- Fox.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
_ <- projectDAO.insertOne(project) ?~> "project.creation.failed"
js <- projectService.publicWrites(project)
} yield Ok(js)
case _ =>
Future.successful(JsonBadRequest(Messages("project.name.alreadyTaken")))
}
}
}

def update(id: String): Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
def update(projectName: String): Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
withJsonBodyUsing(Project.projectPublicReads) { updateRequest =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated)(GlobalAccessContext) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName)(GlobalAccessContext) ?~> Messages("project.notFound",
projectName) ~> NOT_FOUND
_ <- Fox
.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
_ <- projectDAO
.updateOne(updateRequest.copy(_id = project._id, paused = project.paused)) ?~> "project.update.failed"
updated <- projectDAO.findOne(projectIdValidated)
_ <- projectDAO.updateOne(updateRequest.copy(_id = project._id, paused = project.paused)) ?~> Messages(
"project.update.failed",
projectName)
updated <- projectDAO.findOneByName(projectName)
js <- projectService.publicWrites(updated)
} yield Ok(js)
}
}

def pause(id: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
updatePauseStatus(id, isPaused = true)
def pause(projectName: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
updatePauseStatus(projectName, isPaused = true)
}

def resume(id: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
updatePauseStatus(id, isPaused = false)
def resume(projectName: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
updatePauseStatus(projectName, isPaused = false)
}

private def updatePauseStatus(id: String, isPaused: Boolean)(implicit request: SecuredRequest[WkEnv, _]) =
private def updatePauseStatus(projectName: String, isPaused: Boolean)(implicit request: SecuredRequest[WkEnv, _]) =
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
_ <- Fox.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
_ <- projectDAO.updatePaused(project._id, isPaused) ?~> "project.update.failed"
updatedProject <- projectDAO.findOne(projectIdValidated)
_ <- projectDAO.updatePaused(project._id, isPaused) ?~> Messages("project.update.failed", projectName)
updatedProject <- projectDAO.findOne(project._id) ?~> Messages("project.notFound", projectName)
js <- projectService.publicWrites(updatedProject)
} yield Ok(js)

Expand All @@ -130,14 +134,13 @@ class ProjectController @Inject()(projectService: ProjectService,
}
}

def tasksForProject(id: String,
def tasksForProject(projectName: String,
limit: Option[Int] = None,
pageNumber: Option[Int] = None,
includeTotalCount: Option[Boolean]): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
_ <- Fox.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
tasks <- taskDAO.findAllByProject(project._id, limit.getOrElse(Int.MaxValue), pageNumber.getOrElse(0))
taskCount <- Fox.runOptional(includeTotalCount.flatMap(BoolToOption.convert))(_ =>
Expand All @@ -152,45 +155,43 @@ class ProjectController @Inject()(projectService: ProjectService,
}
}

def incrementEachTasksInstances(id: String, delta: Option[Long]): Action[AnyContent] =
def incrementEachTasksInstances(projectName: String, delta: Option[Long]): Action[AnyContent] =
sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(delta.getOrElse(1L) >= 0) ?~> "project.increaseTaskInstances.negative"
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
_ <- taskDAO.incrementTotalInstancesOfAllWithProject(project._id, delta.getOrElse(1L))
openInstancesAndTime <- taskDAO.countOpenInstancesAndTimeForProject(project._id)
js <- projectService.publicWritesWithStatus(project, openInstancesAndTime._1, openInstancesAndTime._2)
} yield Ok(js)
}

def usersWithActiveTasks(id: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
def usersWithActiveTasks(projectName: String): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
usersWithActiveTasks <- projectDAO.findUsersWithActiveTasks(project._id)
_ <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
usersWithActiveTasks <- projectDAO.findUsersWithActiveTasks(projectName)
} yield {
Ok(Json.toJson(usersWithActiveTasks.map(tuple =>
Json.obj("email" -> tuple._1, "firstName" -> tuple._2, "lastName" -> tuple._3, "activeTasks" -> tuple._4))))
}
}

def transferActiveTasks(id: String): Action[JsValue] = sil.SecuredAction.async(parse.json) { implicit request =>
for {
projectIdValidated <- ObjectId.parse(id)
project <- projectDAO.findOne(projectIdValidated) ?~> "project.notFound" ~> NOT_FOUND
_ <- Fox
.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
newUserId <- (request.body \ "userId").asOpt[String].toFox ?~> "user.id.notFound" ~> NOT_FOUND
newUserIdValidated <- ObjectId.parse(newUserId)
activeAnnotations <- annotationDAO.findAllActiveForProject(project._id)
_ <- Fox.serialCombined(activeAnnotations) { id =>
annotationService.transferAnnotationToUser(AnnotationType.Task.toString,
id.toString,
newUserIdValidated,
request.identity)
}
} yield Ok
def transferActiveTasks(projectName: String): Action[JsValue] = sil.SecuredAction.async(parse.json) {
implicit request =>
for {
project <- projectDAO.findOneByName(projectName) ?~> Messages("project.notFound", projectName) ~> NOT_FOUND
_ <- Fox
.assertTrue(userService.isTeamManagerOrAdminOf(request.identity, project._team)) ?~> "notAllowed" ~> FORBIDDEN
newUserId <- (request.body \ "userId").asOpt[String].toFox ?~> "user.id.notFound" ~> NOT_FOUND
newUserIdValidated <- ObjectId.parse(newUserId)
activeAnnotations <- annotationDAO.findAllActiveForProject(project._id)
updated <- Fox.serialCombined(activeAnnotations) { id =>
annotationService.transferAnnotationToUser(AnnotationType.Task.toString,
id.toString,
newUserIdValidated,
request.identity)
}
} yield Ok

}
}
Loading

0 comments on commit 7a0e07b

Please sign in to comment.