Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(v3): add role slice (DEV-1010) #2099

Merged
merged 24 commits into from Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 72 additions & 0 deletions build.sbt
Expand Up @@ -242,6 +242,76 @@ lazy val valueObjects = project
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)

// Role projects

lazy val roleInterface = project
.in(file("dsp-role/interface"))
.settings(
scalacOptions ++= Seq(
"-feature",
"-unchecked",
"-deprecation",
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "roleInterface",
libraryDependencies ++= Dependencies.roleInterfaceLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, roleHandler)

lazy val roleHandler = project
.in(file("dsp-role/handler"))
.settings(
scalacOptions ++= Seq(
"-feature",
"-unchecked",
"-deprecation",
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "roleHandler",
libraryDependencies ++= Dependencies.roleHandlerLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(
shared,
roleCore % "compile->compile;test->test",
roleRepo % "test->test"
)

lazy val roleRepo = project
.in(file("dsp-role/repo"))
.settings(
scalacOptions ++= Seq(
"-feature",
"-unchecked",
"-deprecation",
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "roleRepo",
libraryDependencies ++= Dependencies.roleRepoLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, roleCore % "compile->compile;test->test")

lazy val roleCore = project
.in(file("dsp-role/core"))
.settings(
scalacOptions ++= Seq(
"-feature",
"-unchecked",
"-deprecation",
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "roleCore",
libraryDependencies ++= Dependencies.roleCoreLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared)

// User projects

lazy val userInterface = project
Expand Down Expand Up @@ -312,6 +382,8 @@ lazy val userCore = project
)
.dependsOn(shared)

// Shared project

lazy val shared = project
.in(file("dsp-shared"))
.settings(
Expand Down
53 changes: 53 additions & 0 deletions dsp-role/core/src/main/scala/dsp/role/api/RoleRepo.scala
@@ -0,0 +1,53 @@
/*
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.role.api

import dsp.role.domain.Role
import dsp.valueobjects.Id._
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
import zio._
import zio.macros.accessible

/**
* The trait (interface) for the role repository.
* The role repository is responsible for storing and retrieving roles.
* Needs to be used by the role repository implementations.
*/
@accessible
trait RoleRepo {

/**
* Writes a role into the repository, while both creating or updating a role.
*
* @param r the role to write
* @return Unit
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
*/
def storeRole(r: Role): UIO[RoleId]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def storeRole(r: Role): UIO[RoleId]
def storeRole(role: Role): UIO[RoleId]

I think a little more expressive variable names are nicer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In strongly typed languages the type of param, if not omitted, speaks for itself. Therefore in most of the cases instead of repeating the type in the name it could be shortened to first letter of the type, or named: value, param, whatever, because at the end the IDE will inform one need to pass something of the defined (Role in this case) type.


/**
* Gets all roles from teh repository.
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
*
* @return a list of [[Role]]
*/
def getRoles(): UIO[List[Role]]

/**
* Retrieves the role frim the repository.
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param id the role's ID
* @return the [[Role]] if found
*/
def getRoleById(id: RoleId): IO[Option[Nothing], Role]

// should the role name be unique like username???

/**
* Deletes the [[Role]] from the repository by its [[RoleId]]
*
* @param id the role ID
* @return the [[RoleId]] deleted role, if found
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
*/
def deleteRole(id: RoleId): IO[Option[Nothing], RoleId]
}
123 changes: 123 additions & 0 deletions dsp-role/core/src/main/scala/dsp/role/domain/RoleDomain.scala
@@ -0,0 +1,123 @@
/*
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.role.domain

import dsp.errors.BadRequestException
import dsp.valueobjects.Id.RoleId
import dsp.valueobjects.Id.UserId
import dsp.valueobjects.Permission
import dsp.valueobjects.Role._
import zio.prelude.Validation

case class RoleUser(
id: UserId
)

/**
* Role's domain model.
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param id the role's ID
* @param name the role's name
* @param description the role's description
* @param users the role's users
* @param permission the role's permission
*/
sealed abstract case class Role private (
id: RoleId,
name: LangString,
description: LangString,
users: List[RoleUser], // List[User]
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
permission: Permission
) extends Ordered[Role] { self =>

/**
* Allows to compare the [[Role]] instances.
*
* @param that [[Role]] to compare
* @return [[Boolean]] value
*/
def compare(that: Role): Int = self.id.iri.toString().compareTo(that.id.iri.toString())
// Boolean = self.id.equals(that.id)
mpro7 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Updates the role's name.
*
* @param newValue new role's name to update
* @return updated [[Role]]
*/
def updateName(newValue: LangString): Validation[BadRequestException, Role] =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def updateName(newValue: LangString): Validation[BadRequestException, Role] =
def updateName(name: LangString): Validation[BadRequestException, Role] =

would be more expressive, maybe even newName?

Copy link
Collaborator Author

@mpro7 mpro7 Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In opposite to that r: Role here maybe the param name could be name/newName, because type doesn't say what it is, but:

  • def updateName(newValue: LangString) is very clear the method will update the Name and requires its new value in LangString type to be passed
  • we have the docString which clarify it even more
  • followed the convention we have in user slice

Role.make(
self.id,
newValue,
self.description,
self.users,
self.permission
)

/**
* Updates the role's description.
*
* @param newValue new role's description to update
* @return updated [[Role]]
*/
def updateDescription(newValue: LangString): Validation[BadRequestException, Role] =
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
Role.make(
self.id,
self.name,
newValue,
self.users,
self.permission
)

/**
* Updates the role's users.
*
* @param newValue new role's users to update
* @return updated [[Role]]
*/
def updateUsers(newValue: List[RoleUser]): Validation[BadRequestException, Role] =
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
Role.make(
self.id,
self.name,
self.description,
newValue,
self.permission
)

/**
* Updates the role's permission.
*
* @param newValue new role's permission to update
* @return updated [[Role]]
*/
def updatePermission(newValue: Permission): Validation[BadRequestException, Role] =
mpro7 marked this conversation as resolved.
Show resolved Hide resolved
Role.make(
self.id,
self.name,
self.description,
self.users,
newValue
)
}

object Role {
def make(
id: RoleId,
name: LangString,
description: LangString,
users: List[RoleUser],
permission: Permission
): Validation[BadRequestException, Role] =
Validation.succeed(
new Role(
id,
name,
description,
users,
permission
) {}
)
}
11 changes: 11 additions & 0 deletions dsp-role/core/src/test/scala/dsp/role/api/RoleRepoSpec.scala
@@ -0,0 +1,11 @@
/*
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.role.api

/**
* To be implemented...
*/
object placeholder {}
mpro7 marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file needed? Are you going to implement it? I don't think we need to test the repo api (it's just an interface). I think it makes more sense to test the implementations of it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this I'm not sure. You also have it in the user slice.

110 changes: 110 additions & 0 deletions dsp-role/core/src/test/scala/dsp/role/domain/RoleDomainSpec.scala
@@ -0,0 +1,110 @@
/*
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.role.domain

import dsp.valueobjects.Id
import dsp.valueobjects.Permission
import dsp.valueobjects.Role._
import zio.test._

/**
* This spec is used to test [[RoleDomain]].
*/
object RoleDomainSpec extends ZIOSpecDefault {
def spec = (compareRolesTest + createRoleTest + updateRoleTest)

private val compareRolesTest = suite("compareRoles")(
test("compare two roles") {
val role = RoleTestData.role1
val equalRole = RoleTestData.role1
val nonEqualRole = RoleTestData.role2

assertTrue(role == equalRole) &&
assertTrue(role != nonEqualRole)
}
)

private val createRoleTest = suite("createRole")(
test("create a role") {
(
for {
id <- RoleTestData.id1
name <- RoleTestData.name1
description <- RoleTestData.description1
users = RoleTestData.users1
permission <- RoleTestData.permission1
mpro7 marked this conversation as resolved.
Show resolved Hide resolved

role <- Role.make(
id,
name,
description,
users = users,
permission
)
} yield assertTrue(role.id == id) &&
assertTrue(role.name == name) &&
assertTrue(role.description == description) &&
assertTrue(role.users == users) &&
assertTrue(role.permission == permission)
).toZIO
}
)

private val updateRoleTest = suite("updateRole")(
test("update the name") {
(
for {
role <- RoleTestData.role1
newValue <- LangString.make("newRoleName", "en")
updatedRole <- role.updateName(newValue)
} yield assertTrue(updatedRole.name == newValue) &&
assertTrue(updatedRole.name != role.name) &&
assertTrue(updatedRole.description == role.description) &&
assertTrue(updatedRole.users == role.users) &&
assertTrue(updatedRole.permission == role.permission)
).toZIO
},
test("update the description") {
(
for {
role <- RoleTestData.role1
newValue <- LangString.make("New Role Description", "en")
updatedRole <- role.updateDescription(newValue)
} yield assertTrue(updatedRole.name == role.name) &&
assertTrue(updatedRole.description == newValue) &&
assertTrue(updatedRole.description != role.description) &&
assertTrue(updatedRole.users == role.users) &&
assertTrue(updatedRole.permission == role.permission)
).toZIO
},
test("update the users") {
(
for {
role <- RoleTestData.role1
newValue = List(RoleUser(Id.UserId.make().fold(e => throw e.head, v => v)))
updatedRole <- role.updateUsers(newValue)
} yield assertTrue(updatedRole.name == role.name) &&
assertTrue(updatedRole.description == role.description) &&
assertTrue(updatedRole.users == newValue) &&
assertTrue(updatedRole.users != role.users) &&
assertTrue(updatedRole.permission == role.permission)
).toZIO
},
test("update the permission") {
(
for {
role <- RoleTestData.role1
newValue <- Permission.make(Permission.Create)
updatedRole <- role.updatePermission(newValue)
} yield assertTrue(updatedRole.name == role.name) &&
assertTrue(updatedRole.description == role.description) &&
assertTrue(updatedRole.users == role.users) &&
assertTrue(updatedRole.permission == newValue) &&
assertTrue(updatedRole.permission != role.permission)
).toZIO
}
)
}