Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
refactor(v3): add project slice (DEV-1009) (#2076)
  • Loading branch information
BalduinLandolt committed Sep 8, 2022
1 parent 82703d7 commit bd2d31e
Show file tree
Hide file tree
Showing 30 changed files with 1,561 additions and 147 deletions.
26 changes: 25 additions & 1 deletion Makefile
Expand Up @@ -202,15 +202,39 @@ test-repository-upgrade: build init-db-test-minimal ## runs DB upgrade integrati
@$(MAKE) -f $(THIS_FILE) stack-up

.PHONY: test
test: build ## runs all tests
test: build test-shared test-user-slice test-role-slice test-project-slice ## runs all tests
sbt -v coverage "webapi/test"
sbt -v coverage "schemaCore/test"
sbt coverageAggregate

.PHONY: test-shared
test-shared: ## tests the shared projects (build is not called from this target)
sbt -v coverage "shared/test"

.PHONY: test-user-slice
test-user-slice: ## tests all projects relating to the user slice (build is not called from this target)
sbt -v coverage "userCore/test"
sbt -v coverage "userHandler/test"
sbt -v coverage "userInterface/test"
sbt -v coverage "userRepo/test"
sbt coverageAggregate

.PHONY: test-role-slice
test-role-slice: ## tests all projects relating to the role slice (build is not called from this target)
sbt -v coverage "roleCore/test"
sbt -v coverage "roleHandler/test"
sbt -v coverage "roleInterface/test"
sbt -v coverage "roleRepo/test"
sbt coverageAggregate

.PHONY: test-project-slice
test-project-slice: ## tests all projects relating to the project slice (build is not called from this target)
sbt -v coverage "projectCore/test"
sbt -v coverage "projectHandler/test"
sbt -v coverage "projectInterface/test"
sbt -v coverage "projectRepo/test"
sbt coverageAggregate


#################################
## Database Management
Expand Down
56 changes: 55 additions & 1 deletion build.sbt
Expand Up @@ -37,14 +37,22 @@ lazy val root: Project = Project(id = "root", file("."))
webapi,
sipi,
shared,
// user
userCore,
userHandler,
userRepo,
userInterface,
// role
roleCore,
roleRepo,
roleHandler,
roleRepo,
roleInterface,
// project
projectCore,
projectHandler,
projectRepo,
projectInterface,
// schema
schemaCore
)
.enablePlugins(GitVersioning, GitBranchPrompt)
Expand Down Expand Up @@ -352,6 +360,52 @@ lazy val userCore = project
)
.dependsOn(shared)

// project projects

lazy val projectInterface = project
.in(file("dsp-project/interface"))
.settings(
scalacOptions ++= customScalacOptions,
name := "projectInterface",
libraryDependencies ++= Dependencies.projectInterfaceLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, projectHandler)

lazy val projectHandler = project
.in(file("dsp-project/handler"))
.settings(
scalacOptions ++= customScalacOptions,
name := "projectHandler",
libraryDependencies ++= Dependencies.projectHandlerLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(
shared,
projectCore,
projectRepo % "test->test"
) // projectHandler tests need mock implementation of ProjectRepo

lazy val projectCore = project
.in(file("dsp-project/core"))
.settings(
scalacOptions ++= customScalacOptions,
name := "projectCore",
libraryDependencies ++= Dependencies.projectCoreLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared)

lazy val projectRepo = project
.in(file("dsp-project/repo"))
.settings(
scalacOptions ++= customScalacOptions,
name := "projectRepo",
libraryDependencies ++= Dependencies.projectRepoLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, projectCore)

// schema projects

lazy val schemaCore = project
Expand Down
70 changes: 70 additions & 0 deletions dsp-project/core/src/main/scala/dsp/project/api/ProjectRepo.scala
@@ -0,0 +1,70 @@
/*
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.project.api

import zio._
import zio.macros.accessible

import dsp.project.domain._
import dsp.valueobjects.Project._
import dsp.valueobjects._

/**
* The trait (interface) for the project repository. The project repository is responsible for storing and retrieving projects.
* Needs to be used by the project repository implementations.
*/
@accessible // with this annotation we don't have to write the companion object ourselves
trait ProjectRepo {

/**
* Writes a project to the repository (used for both create and update).
* If this fails (e.g. the triplestore is not available), it's a non-recovable error. That's why we need UIO.
* When used, we should do it like: ...store(...).orDie
*
* @param project the project to write
* @return The project ID
*/
def storeProject(project: Project): UIO[ProjectId]

/**
* Gets all projects from the repository.
*
* @return a list of [[Project]]
*/
def getProjects(): UIO[List[Project]]

/**
* Retrieves the project from the repository by ID.
*
* @param id the project's ID
* @return an optional [[Project]]
*/
def getProjectById(id: ProjectId): IO[Option[Nothing], Project]

/**
* Retrieves the project from the repository by ShortCode.
*
* @param shortCode ShortCode of the project.
* @return an optional [[Project]].
*/
def getProjectByShortCode(shortCode: ShortCode): IO[Option[Nothing], Project]

/**
* Checks if a project ShortCode is available or if it already exists in the repo.
*
* @param shortCode ShortCode of the project.
* @return Success of Unit if the ShortCode is available, Error of None if not.
*/
def checkIfShortCodeIsAvailable(shortCode: ShortCode): IO[Option[Nothing], Unit]

/**
* Deletes a [[Project]] from the repository by its [[ProjectId]].
*
* @param id the project ID
* @return Project ID or None if not found
*/
def deleteProject(id: ProjectId): IO[Option[Nothing], ProjectId]
}
@@ -0,0 +1,67 @@
/*
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.project.domain

import zio.prelude.Validation

import dsp.errors.ValidationException
import dsp.valueobjects.Project._
import dsp.valueobjects._

/**
* Represents the project domain object.
*
* @param id the ID of the project
* @param name the name of the project
* @param description the description of the project
*/
sealed abstract case class Project private (
id: ProjectId,
name: Name,
description: ProjectDescription
// TODO-BL: [domain-model] missing status, shortname, selfjoin
) extends Ordered[Project] { self =>

/**
* Allows to sort collections of [[Project]]s. Sorting is done by the IRI.
*/
def compare(that: Project): Int = self.id.iri.toString().compareTo(that.id.iri.toString())

/**
* Updates the name of the project.
*
* @param name the new name
* @return the updated Project or a ValidationException
*/
def updateProjectName(name: Name): Validation[ValidationException, Project] =
Project.make(
id = self.id,
name = name,
description = self.description
)

/**
* Updates the description of the project.
*
* @param description the new description
* @return the updated Project or a ValidationException
*/
def updateProjectDescription(description: ProjectDescription): Validation[ValidationException, Project] =
Project.make(
id = self.id,
name = self.name,
description = description
)
}
object Project {
def make(
id: ProjectId,
name: Name,
description: ProjectDescription
): Validation[ValidationException, Project] =
Validation.succeed(new Project(id = id, name = name, description = description) {})

}
@@ -0,0 +1,96 @@
/*
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.project.domain

import zio.test._

import dsp.valueobjects.Iri
import dsp.valueobjects.Project._
import dsp.valueobjects.ProjectId
import dsp.valueobjects.V2

/**
* This spec is used to test [[dsp.project.domain.ProjectDomain]].
*/
object ProjectDomainSpec extends ZIOSpecDefault {

private val shortCode = ShortCode.make("0001").fold(e => throw e.head, v => v)
private val id = ProjectId.make(shortCode).fold(e => throw e.head, v => v)
private val name = Name.make("proj").fold(e => throw e.head, v => v)
private val description = ProjectDescription
.make(Seq(V2.StringLiteralV2("A Project", Some("en"))))
.fold(e => throw e.head, v => v)

override def spec =
suite("ProjectDomainSpec")(
projectCreateTests,
projectCompareTests,
projectUpdateTests
)

val projectCreateTests = suite("create project")(
test("create a project from valid input") {
(for {
project <- Project.make(id, name, description)
} yield (
assertTrue(project.id == id) &&
assertTrue(project.name == name) &&
assertTrue(project.description == description)
)).toZIO
}
)

val projectCompareTests = suite("compare projects")(
test("compare projects by IRI") {
val iri1String = s"http://rdfh.ch/projects/d78c6cc8-0a18-4131-af72-4bb6cb688bed"
val iri2String = s"http://rdfh.ch/projects/f4184d7a-caf7-4ab9-991e-d5da9eb7ec17"
(for {
iri1 <- Iri.ProjectIri.make(iri1String)
iri2 <- Iri.ProjectIri.make(iri2String)
id1 <- ProjectId.fromIri(iri1, shortCode)
id2 <- ProjectId.fromIri(iri2, shortCode)
project1 <- Project.make(id1, name, description)
project2 <- Project.make(id2, name, description)
listInitial = List(project1, project2)
listSorted = listInitial.sorted
listSortedInverse = listInitial.sortWith(_ > _)
} yield (
assertTrue(listInitial == listSorted) &&
assertTrue(listInitial != listSortedInverse) &&
assertTrue(listInitial == listSortedInverse.reverse)
)).toZIO
}
)

val projectUpdateTests = suite("update project")(
test("update a project name when provided a valid new name") {
(for {
newName <- Name.make("new project name")
project <- Project.make(id, name, description)
updatedProject <- project.updateProjectName(newName)
} yield (
assertTrue(project.id == updatedProject.id) &&
assertTrue(project.name != updatedProject.name) &&
assertTrue(project.description == updatedProject.description) &&
assertTrue(project.name == name) &&
assertTrue(updatedProject.name == newName)
)).toZIO
},
test("update a project description when provided a valid new description") {
(for {
newDescription <- ProjectDescription.make(Seq(V2.StringLiteralV2("new project name", Some("en"))))
project <- Project.make(id, name, description)
updatedProject <- project.updateProjectDescription(newDescription)
} yield (
assertTrue(project.id == updatedProject.id) &&
assertTrue(project.name == updatedProject.name) &&
assertTrue(project.description != updatedProject.description) &&
assertTrue(project.description == description) &&
assertTrue(updatedProject.description == newDescription)
)).toZIO
}
)
}

0 comments on commit bd2d31e

Please sign in to comment.