Skip to content

Commit

Permalink
refactor(v3): finish user slice (DEV-671) (#2078)
Browse files Browse the repository at this point in the history
* start with C4 documentation

* refactor UserId

* add imports for UserId

* separate username and email checks

* add update methods

* refactor id

* use password hash instead of password

* add negative tests

* refactor UserHandlerSpec

* add shared test data

* use shared test data in tests

* finish user slice

* add all user projects to root

* remove unnecessary log output

* add architecture documentation

* add sequence diagram with mermaid

* cleanup code

* bump ZioLoggingVersion

* refactor tests

* implement feedback from review

* fix failing test

* Update Id.scala

* rename test data

* add password strength as value object

* move taps to end of method

* simplify test

* refactor user to return a validation

* remove throwables from shared test data

* refactor tests

* remove duplicated line

* correct typo

* remove sha-1 support

* remove unsafeMake as we don't need them

* add placeholder to empty files to remove warnings

* return error instead of throw

* update docs

* improve handling of value objects

* add unsafeMake again

* Update UsersRouteADM.scala

* refactor UserDomainSpec

* Update Makefile

* add slf4j dependency

Co-authored-by: Ivan Subotic <400790+subotic@users.noreply.github.com>
  • Loading branch information
irinaschubert and subotic committed Jul 4, 2022
1 parent 9e038ec commit 48592ad
Show file tree
Hide file tree
Showing 32 changed files with 2,003 additions and 649 deletions.
5 changes: 0 additions & 5 deletions Makefile
Expand Up @@ -203,11 +203,6 @@ test-repository-upgrade: build init-db-test-minimal ## runs DB upgrade integrati

.PHONY: test
test: build ## runs all tests
sbt -v "schemaApi/test"
sbt -v "schemaCore/test"
sbt -v "schemaRepo/test"
sbt -v "schemaRepoEventStoreService/test"
sbt -v "schemaRepoSearchService/test"
sbt -v "shared/test"
sbt -v "sipi/test"
sbt -v "userCore/test"
Expand Down
90 changes: 16 additions & 74 deletions build.sbt
Expand Up @@ -28,7 +28,7 @@ lazy val buildSettings = Seq(
lazy val rootBaseDir = ThisBuild / baseDirectory

lazy val root: Project = Project(id = "root", file("."))
.aggregate(webapi, apiMain)
.aggregate(webapi, sipi, shared, valueObjects, userCore, userHandler, userRepo, userInterface)
.enablePlugins(GitVersioning, GitBranchPrompt)
.settings(
// values set for all sub-projects
Expand Down Expand Up @@ -232,16 +232,6 @@ lazy val webapiJavaTestOptions = Seq(
// DSP's new codebase
//////////////////////////////////////

lazy val apiMain = project
.in(file("dsp-api-main"))
.settings(
name := "dsp-api-main",
libraryDependencies ++= Dependencies.dspApiMainLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaCore, schemaRepo, schemaApi)

// Value Objects project

lazy val valueObjects = project
Expand All @@ -252,57 +242,6 @@ lazy val valueObjects = project
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)

// Schema projects

lazy val schemaApi = project
.in(file("dsp-schema/api"))
.settings(
name := "schemaApi",
libraryDependencies ++= Dependencies.schemaApiLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaCore)

lazy val schemaCore = project
.in(file("dsp-schema/core"))
.settings(
name := "schemaCore",
libraryDependencies ++= Dependencies.schemaCoreLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)

lazy val schemaRepo = project
.in(file("dsp-schema/repo"))
.settings(
name := "schemaRepo",
libraryDependencies ++= Dependencies.schemaRepoLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaCore)

lazy val schemaRepoEventStoreService = project
.in(file("dsp-schema/repo-eventstore-service"))
.settings(
name := "schemaRepoEventstoreService",
libraryDependencies ++= Dependencies.schemaRepoEventStoreServiceLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaRepo)

lazy val schemaRepoSearchService = project
.in(file("dsp-schema/repo-search-service"))
.settings(
name := "dsp-schema-repo-search-service",
libraryDependencies ++= Dependencies.schemaRepoSearchServiceLibraryDependencies,
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaRepo)

// User projects

lazy val userInterface = project
Expand Down Expand Up @@ -335,10 +274,14 @@ lazy val userHandler = project
libraryDependencies ++= Dependencies.userHandlerLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, userCore, userRepo % "test->test") //userHandler tests need mock implementation of UserRepo
.dependsOn(
shared,
userCore % "compile->compile;test->test",
userRepo % "test->test" //userHandler tests need mock implementation of UserRepo
)

lazy val userCore = project
.in(file("dsp-user/core"))
lazy val userRepo = project
.in(file("dsp-user/repo"))
.settings(
scalacOptions ++= Seq(
"-feature",
Expand All @@ -347,14 +290,14 @@ lazy val userCore = project
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "userCore",
libraryDependencies ++= Dependencies.userCoreLibraryDependencies,
name := "userRepo",
libraryDependencies ++= Dependencies.userRepoLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared)
.dependsOn(shared, userCore % "compile->compile;test->test")

lazy val userRepo = project
.in(file("dsp-user/repo"))
lazy val userCore = project
.in(file("dsp-user/core"))
.settings(
scalacOptions ++= Seq(
"-feature",
Expand All @@ -363,12 +306,11 @@ lazy val userRepo = project
"-Yresolve-term-conflict:package",
"-Ymacro-annotations"
),
name := "userRepo",
libraryDependencies ++= Dependencies.userRepoLibraryDependencies,
name := "userCore",
libraryDependencies ++= Dependencies.userCoreLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(shared, userCore)
//.dependsOn(userHandler % "compile->compile;test->test", userDomain)
.dependsOn(shared)

lazy val shared = project
.in(file("dsp-shared"))
Expand Down
27 changes: 27 additions & 0 deletions docs/architecture/docs/http-request-flow-with-events.md
@@ -0,0 +1,27 @@
## Example for an HTTP Request Flow with Events

### Create a User
```mermaid
sequenceDiagram
autonumber
user ->> userRoute: "sends HTTP request"
userRoute ->> userRoute: "validates input (payload) and creates value objects"
userRoute ->> userHandler: "sends value objects"
userHandler ->> userRepo: "reserves username"
userRepo ->> eventStoreService: "reserves username"
eventStoreService ->> eventStoreService: "checks if username exists"
eventStoreService ->> eventStoreService: "reserves username"
userHandler ->> userDomain: "calls User.make() with value objects"
userDomain ->> userDomain: "creates userDomainEntity + userCreatedEvent(who, what)"
userDomain ->> userHandler: "returns (userDomainEntity + userCreatedEvent)"
userHandler ->> userRepo: "storeUser(userDomainEntity + userCreatedEvent)"
userRepo ->> eventStoreService: "storeUser(userDomainEntity + userCreatedEvent)"
eventStoreService ->> eventStoreService: "store event(s), userCreatedEvent(who, what, when(!))"
eventStoreService ->> eventListener: "publishEvent(userCreatedEvent)"
eventListener ->> triplestoreService: "writeToTsService(E)"
triplestoreService ->> triplestoreService: "SPARQL update - write user to triplestore"
eventListener ->> arkService: "writeToArkService(E)"
arkService ->> arkService: "create ARK(URL)"
eventListener ->> elasticSearchService: "writeToEsService(E)"
elasticSearchService ->> elasticSearchService: "write"
```
@@ -1,4 +1,4 @@
HTTP Request Flow
## HTTP Request Flow V2 vs. V3

V1 / V2 / admin:
```mermaid
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture/users.dsl
@@ -0,0 +1,8 @@
# person <name> [description] [tags]
user = person "A user / client" "A user of DSP, regardless if known/unknow or its role"
#userUnknown = person "Unknown user" "An unknown user of DSP"
#userUnknown = person "Unknown user" "An unknown user of DSP"
#userKnown = person "Known user" "A logged-in user of DSP"
#userSysAdmin = person "System Admin" "A system admin of DSP"
#userProjectAdmin = person "Project Admin" "A project administrator"
#userProjectMember = person "Project Member" "A project member"
169 changes: 163 additions & 6 deletions docs/architecture/workspace.dsl
@@ -1,21 +1,178 @@
workspace {
workspace "Architecture Diagrams for DSP" "This is a collection of diagrams for the DSP architecture" {
# https://github.com/structurizr/dsl/blob/master/docs/language-reference.md
# for an example, see https://structurizr.com/dsl?example=big-bank-plc



# static model
model {
user = person "User"
softwareSystem = softwareSystem "Software System"
!include users.dsl

enterprise "DSP - DaSCH Service Platform" {
# softwareSystem <name> [description] [tags]
dspJsLib = softwaresystem "JS-LIB" "Layer between DSP-API and DSP-APP"
dspApp = softwaresystem "DSP-APP" "admin.dasch.swiss"
dspTools = softwaresystem "DSP-TOOLS" "CLI for DSP-API"
fuseki = softwaresystem "Fuseki Triplestore" "RDF database" "Database"
sipi = softwaresystem "SIPI" "IIIF image server"
arkResolver = softwaresystem "ARK Resolver" "Forwards ARK URLs to DSP-APP URLs"
dspApi = softwareSystem "DSP-API" "api.dasch.swiss" {
# container <name> [description] [technology] [tags]

eventStoreService = container "Event Store Service"
eventListener = container "Event Listener"
triplestoreService = container "Triplestore Service"
arkService = container "ARK Service"
elasticSearchService = container "Elastic Search Service"

sharedProject = container "Shared Project" "The project that handles shared entities" {
valueObjectsPackage = component "ValueObjects Package" "The package that provides value objects"
errorPackage = component "Error Package" "The package that provides errors"
}

webapiProject = container "Webapi" "The project that wraps webapi V2"

projectSlice = container "Project Slice" "The slice that handles projects"
roleSlice = container "Role Slice" "The slice that handles roles"
schemaSlice = container "Schema Slice" "The slice that handles schemas"
resourceSlice = container "Resource Slice" "The slice that handles resources"
listSlice = container "List Slice" "The slice that handles lists"
routes = container "Routes" "The slice that provides all routes"

userSlice = container "User Slice" "The slice that handles users" {
userCore = component "User Core"
userDomain = component "User Domain"
userHandler = component "User Handler"

userRepo = component "User Repo (API)"
userRepoLive = component "User Repo Live (Implementation)"
userRepoMock = component "User Repo Mock (Implementation for Tests)"

userRoute = component "User Route"
}

}
}

# relationships between users and software systems
user -> dspApp "Uses [Browser]"
user -> arkResolver "Uses [Browser]"
user -> dspTools "Uses [CLI]"

# relationships to/from software systems
dspApp -> dspJsLib
dspJsLib -> dspApi
dspTools -> dspApi
dspApi -> fuseki
dspApi -> sipi
dspTools -> sipi

# relationships to/from containers
webapiProject -> sharedProject "depends on"
projectSlice -> sharedProject "depends on"
roleSlice -> sharedProject "depends on"
schemaSlice -> sharedProject "depends on"
resourceSlice -> sharedProject "depends on"
listSlice -> sharedProject "depends on"
routes -> sharedProject "depends on"
userSlice -> sharedProject "depends on"

# relationships to/from components
userRepo -> userCore "depends on"
userRepoLive -> userCore "depends on"
userRepoMock -> userCore "depends on"
userRoute -> userCore "depends on"

userRepoLive -> userRepo "implements"
userRepoMock -> userRepo "implements"

userCore -> userDomain "contains"
userCore -> userHandler "contains"

user -> softwareSystem "Uses"
}

views {
systemContext softwareSystem "Diagram1" {
systemlandscape "SystemLandscape" "System Landscape of DSP-API" {
include *
autoLayout
}

# systemContext <software system identifier> [key] [description]
systemContext dspApi "SystemContextDspApi" "DSP-API System Context" {
include *
autoLayout
}

# container <software system identifier> [key] [description]
container dspApi "ContainerDspApi" "Containers of DSP-API" {
include *
autoLayout
}

# component <container identifier> [key] [description]
component userSlice "ComponentsOfUserSlice" "Components of the User Slice" {
include *

}

component SharedProject "ComponentsOfSharedProject" "Components of the Shared Project" {
include *
autoLayout
}

# dynamic <*|software system identifier|container identifier> [key] [description]
dynamic userSlice "HttpRequestWithEventsCreateUser" "Example workflow for a HTTP request with events (create user)" {
user -> userRoute "sends HTTP request to"
userRoute -> userRoute "validates input and creates value objects"
userRoute -> userHandler "createUser(vo)"
userHandler -> userRepo "reserve username"
userRepo -> eventStoreService "reserve username"
eventStoreService -> eventStoreService "check if username exists"
eventStoreService -> eventStoreService "reserve username"
userHandler -> userDomain ".make(vo)"
userDomain -> userDomain "create user domain entity + userCreatedEvent(who, what)"
userDomain -> userHandler "return (userDomainEntity + userCreatedEvent)"
userHandler -> userRepo "storeUser(userDomainEntity + userCreatedEvent)"
userRepo -> eventStoreService "storeUser(userDomainEntity + userCreatedEvent)"
eventStoreService -> eventStoreService "store event(s), userCreatedEvent(who, what, when(!))"
eventStoreService -> eventListener "publishEvent(userCreatedEvent)"
eventListener -> triplestoreService "writeToTsService(E)"
triplestoreService -> triplestoreService "SPARQL Update"
eventListener -> arkService "writeToArkService(E)"
arkService -> arkService "create ARK(URL)"
eventListener -> elasticSearchService "writeToEsService(E)"

}

dynamic userSlice "HttpRequestWithEventsUpdateUser" "Example workflow for a HTTP request with events (update username)" {
user -> userRoute "sends HTTP request to"
userRoute -> userRoute "validates input and creates value objects"
userRoute -> userHandler "updateUsername(vo)"
userHandler -> userRepo "getUser(userId)"
userRepo -> eventStoreService "getUser(userId)"
eventStoreService -> eventStoreService "get all events for this user"
eventStoreService -> userDomain "createUserFromEvents(E,E,E,...)"
userDomain -> userHandler "return User"
userHandler -> userDomain "updateUsername(vo)"
userDomain -> userDomain "updateUser(userDomainEntity + userUpdatedEvent(who, what))"
userDomain -> userHandler "return userDomainEntity + userUpdatedEvent(who, what)"
userHandler -> userRepo "storeUser(userDomainEntity + userUpdatedEvent(who, what, when(!))"

userRepo -> eventStoreService "storeUser(userDomainEntity + userCreatedEvent)"
eventStoreService -> eventStoreService "store event(s), userCreatedEvent(who, what, when(!))"
eventStoreService -> eventListener "publishEvent(userCreatedEvent)"
eventListener -> triplestoreService "writeToTsService(E)"
triplestoreService -> triplestoreService "SPARQL Update"
eventListener -> arkService "writeToArkService(E)"
arkService -> arkService "create ARK(URL)"
eventListener -> elasticSearchService "writeToEsService(E)"

autoLayout
}

theme default
}

!adrs decisions
!docs flows
!docs docs
}

0 comments on commit 48592ad

Please sign in to comment.