Skip to content

Commit

Permalink
refactor: wrapped id cc into Wrapper with companion objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Ioann Kurchin committed Feb 21, 2022
1 parent ac85bbc commit 447c854
Show file tree
Hide file tree
Showing 44 changed files with 225 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import akka.http.scaladsl.server.{ ExceptionHandler, Route }
import cromwell.pipeline.controller.ProjectConfigurationController._
import cromwell.pipeline.controller.utils.FromStringUnmarshallers._
import cromwell.pipeline.controller.utils.FromUnitMarshaller._
import cromwell.pipeline.controller.utils.PathMatchers.{ Path, ProjectId }
import cromwell.pipeline.controller.utils.PathMatchers.{ Path, ProjectId => ProjectIdPM }
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.datastorage.dto.auth.AccessTokenContent
import cromwell.pipeline.model.wrapper.ProjectId
import cromwell.pipeline.service.ProjectConfigurationService
import cromwell.pipeline.service.ProjectConfigurationService.Exceptions._
import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._
Expand Down Expand Up @@ -61,7 +62,7 @@ class ProjectConfigurationController(projectConfigurationService: ProjectConfigu

val route: AccessTokenContent => Route = implicit accessToken =>
handleExceptions(projectConfigurationServiceExceptionHandler) {
pathPrefix("projects" / ProjectId / "configurations") { projectId =>
pathPrefix("projects" / ProjectIdPM / "configurations") { projectId =>
buildConfiguration(projectId) ~
addConfiguration(projectId) ~
getConfiguration(projectId) ~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import akka.http.scaladsl.server.Route
import akka.stream.Materializer
import cromwell.pipeline.controller.utils.FieldUnmarshallers._
import cromwell.pipeline.controller.utils.FromStringUnmarshallers._
import cromwell.pipeline.controller.utils.PathMatchers.{ Path, ProjectId }
import cromwell.pipeline.controller.utils.PathMatchers.{ Path, ProjectId => ProjectIdPM }
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.datastorage.dto.auth.AccessTokenContent
import cromwell.pipeline.model.wrapper.ProjectId
import cromwell.pipeline.service.ProjectFileService
import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._

Expand Down Expand Up @@ -99,10 +100,14 @@ class ProjectFileController(wdlService: ProjectFileService)(

val route: AccessTokenContent => Route = implicit accessToken =>
validateFile ~
pathPrefix("projects" / ProjectId / "files") { projectId =>
getFile(projectId) ~
getFiles(projectId) ~
uploadFile(projectId) ~
deleteFile(projectId)
pathPrefix("projects") {
path(ProjectIdPM / "files") { projectId =>
{
getFile(projectId) ~
getFiles(projectId) ~
uploadFile(projectId) ~
deleteFile(projectId)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import akka.http.scaladsl.model.{ StatusCode, StatusCodes }
import akka.http.scaladsl.server.Directives.{ entity, _ }
import akka.http.scaladsl.server.{ ExceptionHandler, Route }
import cromwell.pipeline.controller.RunController.runServiceExceptionHandler
import cromwell.pipeline.controller.utils.PathMatchers.{ ProjectId, RunId }
import cromwell.pipeline.controller.utils.PathMatchers.{ RunId, ProjectId => ProjectIdPM }
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.datastorage.dto.auth.AccessTokenContent
import cromwell.pipeline.model.wrapper.ProjectId
import cromwell.pipeline.service.RunService
import cromwell.pipeline.service.RunService.Exceptions.RunServiceException
import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._
Expand Down Expand Up @@ -46,12 +47,16 @@ class RunController(runService: RunService) {

val route: AccessTokenContent => Route = implicit accessToken =>
handleExceptions(runServiceExceptionHandler) {
pathPrefix("projects" / ProjectId / "runs") { projectId =>
getRun(projectId) ~
getRunsByProject(projectId) ~
deleteRun(projectId) ~
updateRun(projectId) ~
addRun(projectId)
pathPrefix("projects") {
path(ProjectIdPM / "runs") { projectId =>
{
getRun(projectId) ~
getRunsByProject(projectId) ~
deleteRun(projectId) ~
updateRun(projectId) ~
addRun(projectId)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package cromwell.pipeline.controller.utils

import akka.http.scaladsl.unmarshalling.Unmarshaller
import cats.data.Validated
import cromwell.pipeline.datastorage.dto.{ PipelineVersion, ProjectId }
import cromwell.pipeline.model.wrapper.RunId
import cromwell.pipeline.datastorage.dto.PipelineVersion
import cromwell.pipeline.model.wrapper.{ ProjectId, RunId }

import java.nio.file.{ Path, Paths }

Expand All @@ -17,7 +17,10 @@ object FromStringUnmarshallers {
}
implicit val stringToProjectId: Unmarshaller[String, ProjectId] = Unmarshaller.strict[String, ProjectId] {
projectId =>
ProjectId(projectId)
ProjectId.from(projectId) match {
case Validated.Valid(content) => content
case Validated.Invalid(errors) => throw new RuntimeException(errors.head)
}
}
implicit val stringToPath: Unmarshaller[String, Path] = Unmarshaller.strict[String, Path] { path =>
Paths.get(path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package cromwell.pipeline.controller.utils

import akka.http.scaladsl.server.PathMatcher1
import akka.http.scaladsl.server.PathMatchers.Segment
import cromwell.pipeline.datastorage.dto
import cromwell.pipeline.model.wrapper

import java.nio.file.{ Path, Paths }

object PathMatchers {
val ProjectId: PathMatcher1[dto.ProjectId] = Segment.map(dto.ProjectId(_))
val ProjectId: PathMatcher1[wrapper.ProjectId] = Segment.flatMap(wrapper.ProjectId.from(_).toOption)
val Path: PathMatcher1[Path] = Segment.map(Paths.get(_))
val RunId: PathMatcher1[wrapper.RunId] = Segment.flatMap(wrapper.RunId.from(_).toOption)
val ProjectSearchFilterId: PathMatcher1[wrapper.ProjectSearchFilterId] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest
import cromwell.pipeline.datastorage.dao.utils.{ TestProjectUtils, TestUserUtils }
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.datastorage.dto.auth.AccessTokenContent
import cromwell.pipeline.model.wrapper.ProjectConfigurationId
import cromwell.pipeline.service.ProjectConfigurationService.Exceptions.{ InternalError, NotFound, ValidationError }
import cromwell.pipeline.service.{ ProjectConfigurationService, VersioningException }
import cromwell.pipeline.utils.URLEncoderUtils
Expand Down Expand Up @@ -46,21 +47,19 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit

"return success for update configuration" in {
when(configurationService.addConfiguration(configuration, accessToken.userId)).thenReturn(Future.unit)
Put(s"/projects/${projectId.value}/configurations", configurationAdditionRequest) ~> configurationController
.route(
accessToken
) ~> check {
Put(s"/projects/$projectId/configurations", configurationAdditionRequest) ~> configurationController.route(
accessToken
) ~> check {
status shouldBe StatusCodes.NoContent
}
}

"return InternalServerError when failure update configuration" in {
val error = InternalError("Something went wrong")
when(configurationService.addConfiguration(configuration, accessToken.userId)).thenReturn(Future.failed(error))
Put(s"/projects/${projectId.value}/configurations", configurationAdditionRequest) ~> configurationController
.route(
accessToken
) ~> check {
Put(s"/projects/$projectId/configurations", configurationAdditionRequest) ~> configurationController.route(
accessToken
) ~> check {
status shouldBe StatusCodes.InternalServerError
entityAs[String] shouldBe "Something went wrong"
}
Expand All @@ -69,10 +68,9 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return NotFound when failure find project to update configuration" in {
when(configurationService.addConfiguration(configuration, accessToken.userId))
.thenReturn(Future.failed(NotFound()))
Put(s"/projects/${projectId.value}/configurations", configurationAdditionRequest) ~> configurationController
.route(
accessToken
) ~> check {
Put(s"/projects/$projectId/configurations", configurationAdditionRequest) ~> configurationController.route(
accessToken
) ~> check {
status shouldBe StatusCodes.NotFound
}
}
Expand All @@ -82,7 +80,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return configuration by existing project id" in {
when(configurationService.getLastByProjectId(projectId, accessToken.userId))
.thenReturn(Future.successful(configuration))
Get(s"/projects/${projectId.value}/configurations") ~> configurationController.route(accessToken) ~> check {
Get(s"/projects/$projectId/configurations") ~> configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.OK
entityAs[ProjectConfiguration] shouldBe configuration
}
Expand All @@ -91,7 +89,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return Configuration not found message" in {
when(configurationService.getLastByProjectId(projectId, accessToken.userId))
.thenReturn(Future.failed(NotFound(s"There is no configuration with project_id: ${projectId.value}")))
Get(s"/projects/${projectId.value}/configurations") ~> configurationController.route(accessToken) ~> check {
Get(s"/projects/$projectId/configurations") ~> configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.NotFound
entityAs[String] shouldBe s"There is no configuration with project_id: ${projectId.value}"
}
Expand All @@ -103,15 +101,15 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit

"return success for deactivate configuration" in {
when(configurationService.deactivateLastByProjectId(projectId, accessToken.userId)).thenReturn(Future.unit)
Delete(s"/projects/${projectId.value}/configurations") ~> configurationController.route(accessToken) ~> check {
Delete(s"/projects/$projectId/configurations") ~> configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.NoContent
}
}

"return InternalServerError when failure deactivate configuration" in {
when(configurationService.deactivateLastByProjectId(projectId, accessToken.userId))
.thenReturn(Future.failed(error))
Delete(s"/projects/${projectId.value}/configurations") ~> configurationController.route(accessToken) ~> check {
Delete(s"/projects/$projectId/configurations") ~> configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.InternalServerError
entityAs[String] shouldBe "Something went wrong"
}
Expand All @@ -120,7 +118,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return NotFound when failure find project to deactivate configuration" in {
when(configurationService.deactivateLastByProjectId(projectId, accessToken.userId))
.thenReturn(Future.failed(NotFound()))
Delete(s"/projects/${projectId.value}/configurations") ~> configurationController.route(accessToken) ~> check {
Delete(s"/projects/$projectId/configurations") ~> configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.NotFound
}
}
Expand All @@ -131,7 +129,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return configuration for file" in {
when(configurationService.buildConfiguration(projectId, path, versionOption, accessToken.userId))
.thenReturn(Future.successful(configuration))
Get(s"/projects/${projectId.value}/configurations/files/$pathString?version=$versionString") ~>
Get(s"/projects/$projectId/configurations/files/$pathString?version=$versionString") ~>
configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.OK
entityAs[ProjectConfiguration] shouldBe configuration
Expand All @@ -141,7 +139,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return failed for Bad request" in {
when(configurationService.buildConfiguration(projectId, path, versionOption, accessToken.userId))
.thenReturn(Future.failed(VersioningException.HttpException("Bad request")))
Get(s"/projects/${projectId.value}/configurations/files/$pathString?version=$versionString") ~>
Get(s"/projects/$projectId/configurations/files/$pathString?version=$versionString") ~>
configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.InternalServerError
entityAs[String] shouldBe "Bad request"
Expand All @@ -151,7 +149,7 @@ class ProjectConfigurationControllerTest extends AsyncWordSpec with Matchers wit
"return failed for invalid file" in {
when(configurationService.buildConfiguration(projectId, path, versionOption, accessToken.userId))
.thenReturn(Future.failed(ValidationError(List("invalid some field").mkString(","))))
Get(s"/projects/${projectId.value}/configurations/files/$pathString?version=$versionString") ~>
Get(s"/projects/$projectId/configurations/files/$pathString?version=$versionString") ~>
configurationController.route(accessToken) ~> check {
status shouldBe StatusCodes.UnprocessableEntity
entityAs[String] shouldBe "invalid some field"
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ services:
restart: always
environment:
GITLAB_OMNIBUS_CONFIG: |
# gitlab_rails['initial_root_password'] = 'initial_root_password'
# gitlab_rails['initial_root_password'] = 'initial_root_password'
external_url 'http://localhost:9080'
nginx['listen_port'] = 9080 # make nginx to listen on the same port as confgured in external_url
# Add any other gitlab.rb configuration here, each on its own line
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ trait Wrapped[T] extends Any {
override def hashCode: Int = this.getClass.hashCode + unwrap.hashCode()
override def toString: String = unwrap.toString
}

object Wrapped {
trait Companion {
type Type
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cromwell.pipeline.model.wrapper

import cats.data.{ NonEmptyChain, Validated }
import cromwell.pipeline.model.validator.Wrapped
import slick.lifted.MappedTo

import java.util.UUID

final class ProjectConfigurationId private (override val unwrap: String)
extends AnyVal
with Wrapped[String]
with MappedTo[String] {
override def value: String = unwrap
}

object ProjectConfigurationId extends Wrapped.Companion {
override type Type = String
override type Wrapper = ProjectConfigurationId
override type Error = String

val pattern: String = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"

def randomId: ProjectConfigurationId = new ProjectConfigurationId(UUID.randomUUID().toString)

override protected def create(value: String): ProjectConfigurationId = new ProjectConfigurationId(value)

override protected def validate(value: String): ValidationResult[String] = Validated.cond(
value.matches(pattern),
value,
NonEmptyChain.one("Invalid ProjectConfigurationId")
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cromwell.pipeline.model.wrapper

import cats.data.{ NonEmptyChain, Validated }
import cromwell.pipeline.model.validator.Wrapped
import play.api.libs.json.Format
import slick.lifted.MappedTo

import java.util.UUID.randomUUID

final class ProjectId private (override val unwrap: String) extends AnyVal with Wrapped[String] with MappedTo[String] {
override def value: String = unwrap
}

object ProjectId extends Wrapped.Companion {
type Type = String
type Wrapper = ProjectId
type Error = String

implicit lazy val projectIdFormat: Format[ProjectId] = wrapperFormat

val pattern: String = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"

override protected def create(value: String): ProjectId = new ProjectId(value)
override protected def validate(value: String): ValidationResult[String] = Validated.cond(
value.matches(pattern),
value,
NonEmptyChain.one("Invalid ProjectId")
)

def random: ProjectId = new ProjectId(randomUUID().toString)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cromwell.pipeline.model.wrapper

import cats.data.{ NonEmptyChain, Validated }
import cromwell.pipeline.model.validator.Wrapped
import play.api.libs.json.Format
import slick.lifted.MappedTo

final case class RepositoryId(override val unwrap: Int) extends AnyVal with MappedTo[Int] with Wrapped[Int] {
override def value: Int = unwrap
}

object RepositoryId extends Wrapped.Companion {
type Type = Int
type Wrapper = RepositoryId
type Error = String

implicit lazy val repositoryIdFormat: Format[RepositoryId] = wrapperFormat

val pattern = "^[0-9]+$"

override protected def create(value: Int): RepositoryId = new RepositoryId(value)
override protected def validate(value: Int): ValidationResult[Int] = Validated.cond(
value >= 0,
value,
NonEmptyChain.one("Value should be not negative integer")
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ trait Profile {

implicit def uuidIso: Isomorphism[UserId, String] = iso[UserId, String](_.unwrap, UserId(_, Enable.Unsafe))
implicit def runidIso: Isomorphism[RunId, String] = iso[RunId, String](_.unwrap, RunId(_, Enable.Unsafe))
implicit def projectidIso: Isomorphism[ProjectId, String] =
iso[ProjectId, String](_.unwrap, ProjectId(_, Enable.Unsafe))
implicit def projectConfigurationIdIso: Isomorphism[ProjectConfigurationId, String] =
iso[ProjectConfigurationId, String](_.unwrap, ProjectConfigurationId(_, Enable.Unsafe))
implicit def repositoryId: Isomorphism[RepositoryId, Int] =
iso[RepositoryId, Int](_.unwrap, RepositoryId(_, Enable.Unsafe))
implicit def filteridIso: Isomorphism[ProjectSearchFilterId, String] =
iso[ProjectSearchFilterId, String](_.unwrap, ProjectSearchFilterId(_, Enable.Unsafe))
implicit def searchQueryIso: Isomorphism[ProjectSearchQuery, JsValue] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package cromwell.pipeline.datastorage.dao.entry

import cromwell.pipeline.datastorage.Profile
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.model.wrapper.UserId
import slick.lifted.MappedToBase.mappedToIsomorphism
import cromwell.pipeline.model.wrapper.{ ProjectId, RepositoryId, UserId }
import slick.lifted.{ ForeignKeyQuery, ProvenShape }

trait ProjectEntry { this: Profile with UserEntry with MyPostgresProfile with AliasesSupport =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package cromwell.pipeline.datastorage.dao.entry

import java.time.Instant

import cromwell.pipeline.datastorage.Profile
import cromwell.pipeline.datastorage.dto._
import cromwell.pipeline.model.wrapper.{ RunId, UserId }
import cromwell.pipeline.model.wrapper.{ ProjectId, RunId, UserId }
import slick.lifted.{ ForeignKeyQuery, ProvenShape }

import java.time.Instant

trait RunEntry { this: Profile with UserEntry with ProjectEntry with MyPostgresProfile with AliasesSupport =>
import Implicits._
import api._
Expand Down
Loading

0 comments on commit 447c854

Please sign in to comment.