Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: wrapped id cc into Wrapper with companion objects
Browse files Browse the repository at this point in the history
Ioann Kurchin committed Feb 21, 2022
1 parent ac85bbc commit 447c854
Showing 44 changed files with 225 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -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._
@@ -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) ~
Original file line number Diff line number Diff line change
@@ -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._

@@ -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
@@ -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._
@@ -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)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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 }

@@ -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)
Original file line number Diff line number Diff line change
@@ -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] =
Original file line number Diff line number Diff line change
@@ -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
@@ -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"
}
@@ -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
}
}
@@ -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
}
@@ -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}"
}
@@ -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"
}
@@ -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
}
}
@@ -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
@@ -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"
@@ -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"
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
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
@@ -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] =
Original file line number Diff line number Diff line change
@@ -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 =>
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._
Loading

0 comments on commit 447c854

Please sign in to comment.