Skip to content

Commit 48592ad

Browse files
irinaschubertsubotic
andauthored
refactor(v3): finish user slice (DEV-671) (#2078)
* 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>
1 parent 9e038ec commit 48592ad

File tree

32 files changed

+2003
-649
lines changed

32 files changed

+2003
-649
lines changed

Makefile

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,6 @@ test-repository-upgrade: build init-db-test-minimal ## runs DB upgrade integrati
203203

204204
.PHONY: test
205205
test: build ## runs all tests
206-
sbt -v "schemaApi/test"
207-
sbt -v "schemaCore/test"
208-
sbt -v "schemaRepo/test"
209-
sbt -v "schemaRepoEventStoreService/test"
210-
sbt -v "schemaRepoSearchService/test"
211206
sbt -v "shared/test"
212207
sbt -v "sipi/test"
213208
sbt -v "userCore/test"

build.sbt

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ lazy val buildSettings = Seq(
2828
lazy val rootBaseDir = ThisBuild / baseDirectory
2929

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

235-
lazy val apiMain = project
236-
.in(file("dsp-api-main"))
237-
.settings(
238-
name := "dsp-api-main",
239-
libraryDependencies ++= Dependencies.dspApiMainLibraryDependencies,
240-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
241-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
242-
)
243-
.dependsOn(schemaCore, schemaRepo, schemaApi)
244-
245235
// Value Objects project
246236

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

255-
// Schema projects
256-
257-
lazy val schemaApi = project
258-
.in(file("dsp-schema/api"))
259-
.settings(
260-
name := "schemaApi",
261-
libraryDependencies ++= Dependencies.schemaApiLibraryDependencies,
262-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
263-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
264-
)
265-
.dependsOn(schemaCore)
266-
267-
lazy val schemaCore = project
268-
.in(file("dsp-schema/core"))
269-
.settings(
270-
name := "schemaCore",
271-
libraryDependencies ++= Dependencies.schemaCoreLibraryDependencies,
272-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
273-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
274-
)
275-
276-
lazy val schemaRepo = project
277-
.in(file("dsp-schema/repo"))
278-
.settings(
279-
name := "schemaRepo",
280-
libraryDependencies ++= Dependencies.schemaRepoLibraryDependencies,
281-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
282-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
283-
)
284-
.dependsOn(schemaCore)
285-
286-
lazy val schemaRepoEventStoreService = project
287-
.in(file("dsp-schema/repo-eventstore-service"))
288-
.settings(
289-
name := "schemaRepoEventstoreService",
290-
libraryDependencies ++= Dependencies.schemaRepoEventStoreServiceLibraryDependencies,
291-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
292-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
293-
)
294-
.dependsOn(schemaRepo)
295-
296-
lazy val schemaRepoSearchService = project
297-
.in(file("dsp-schema/repo-search-service"))
298-
.settings(
299-
name := "dsp-schema-repo-search-service",
300-
libraryDependencies ++= Dependencies.schemaRepoSearchServiceLibraryDependencies,
301-
resolvers += "Sonatype" at "https://oss.sonatype.org/content/repositories/snapshots",
302-
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
303-
)
304-
.dependsOn(schemaRepo)
305-
306245
// User projects
307246

308247
lazy val userInterface = project
@@ -335,10 +274,14 @@ lazy val userHandler = project
335274
libraryDependencies ++= Dependencies.userHandlerLibraryDependencies,
336275
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
337276
)
338-
.dependsOn(shared, userCore, userRepo % "test->test") //userHandler tests need mock implementation of UserRepo
277+
.dependsOn(
278+
shared,
279+
userCore % "compile->compile;test->test",
280+
userRepo % "test->test" //userHandler tests need mock implementation of UserRepo
281+
)
339282

340-
lazy val userCore = project
341-
.in(file("dsp-user/core"))
283+
lazy val userRepo = project
284+
.in(file("dsp-user/repo"))
342285
.settings(
343286
scalacOptions ++= Seq(
344287
"-feature",
@@ -347,14 +290,14 @@ lazy val userCore = project
347290
"-Yresolve-term-conflict:package",
348291
"-Ymacro-annotations"
349292
),
350-
name := "userCore",
351-
libraryDependencies ++= Dependencies.userCoreLibraryDependencies,
293+
name := "userRepo",
294+
libraryDependencies ++= Dependencies.userRepoLibraryDependencies,
352295
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
353296
)
354-
.dependsOn(shared)
297+
.dependsOn(shared, userCore % "compile->compile;test->test")
355298

356-
lazy val userRepo = project
357-
.in(file("dsp-user/repo"))
299+
lazy val userCore = project
300+
.in(file("dsp-user/core"))
358301
.settings(
359302
scalacOptions ++= Seq(
360303
"-feature",
@@ -363,12 +306,11 @@ lazy val userRepo = project
363306
"-Yresolve-term-conflict:package",
364307
"-Ymacro-annotations"
365308
),
366-
name := "userRepo",
367-
libraryDependencies ++= Dependencies.userRepoLibraryDependencies,
309+
name := "userCore",
310+
libraryDependencies ++= Dependencies.userCoreLibraryDependencies,
368311
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
369312
)
370-
.dependsOn(shared, userCore)
371-
//.dependsOn(userHandler % "compile->compile;test->test", userDomain)
313+
.dependsOn(shared)
372314

373315
lazy val shared = project
374316
.in(file("dsp-shared"))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Example for an HTTP Request Flow with Events
2+
3+
### Create a User
4+
```mermaid
5+
sequenceDiagram
6+
autonumber
7+
user ->> userRoute: "sends HTTP request"
8+
userRoute ->> userRoute: "validates input (payload) and creates value objects"
9+
userRoute ->> userHandler: "sends value objects"
10+
userHandler ->> userRepo: "reserves username"
11+
userRepo ->> eventStoreService: "reserves username"
12+
eventStoreService ->> eventStoreService: "checks if username exists"
13+
eventStoreService ->> eventStoreService: "reserves username"
14+
userHandler ->> userDomain: "calls User.make() with value objects"
15+
userDomain ->> userDomain: "creates userDomainEntity + userCreatedEvent(who, what)"
16+
userDomain ->> userHandler: "returns (userDomainEntity + userCreatedEvent)"
17+
userHandler ->> userRepo: "storeUser(userDomainEntity + userCreatedEvent)"
18+
userRepo ->> eventStoreService: "storeUser(userDomainEntity + userCreatedEvent)"
19+
eventStoreService ->> eventStoreService: "store event(s), userCreatedEvent(who, what, when(!))"
20+
eventStoreService ->> eventListener: "publishEvent(userCreatedEvent)"
21+
eventListener ->> triplestoreService: "writeToTsService(E)"
22+
triplestoreService ->> triplestoreService: "SPARQL update - write user to triplestore"
23+
eventListener ->> arkService: "writeToArkService(E)"
24+
arkService ->> arkService: "create ARK(URL)"
25+
eventListener ->> elasticSearchService: "writeToEsService(E)"
26+
elasticSearchService ->> elasticSearchService: "write"
27+
```

docs/architecture/flows/http-request-flow.md renamed to docs/architecture/docs/http-request-flow.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
HTTP Request Flow
1+
## HTTP Request Flow V2 vs. V3
22

33
V1 / V2 / admin:
44
```mermaid

docs/architecture/users.dsl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# person <name> [description] [tags]
2+
user = person "A user / client" "A user of DSP, regardless if known/unknow or its role"
3+
#userUnknown = person "Unknown user" "An unknown user of DSP"
4+
#userUnknown = person "Unknown user" "An unknown user of DSP"
5+
#userKnown = person "Known user" "A logged-in user of DSP"
6+
#userSysAdmin = person "System Admin" "A system admin of DSP"
7+
#userProjectAdmin = person "Project Admin" "A project administrator"
8+
#userProjectMember = person "Project Member" "A project member"

docs/architecture/workspace.dsl

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,178 @@
1-
workspace {
1+
workspace "Architecture Diagrams for DSP" "This is a collection of diagrams for the DSP architecture" {
2+
# https://github.com/structurizr/dsl/blob/master/docs/language-reference.md
3+
# for an example, see https://structurizr.com/dsl?example=big-bank-plc
24

5+
6+
7+
# static model
38
model {
4-
user = person "User"
5-
softwareSystem = softwareSystem "Software System"
9+
!include users.dsl
10+
11+
enterprise "DSP - DaSCH Service Platform" {
12+
# softwareSystem <name> [description] [tags]
13+
dspJsLib = softwaresystem "JS-LIB" "Layer between DSP-API and DSP-APP"
14+
dspApp = softwaresystem "DSP-APP" "admin.dasch.swiss"
15+
dspTools = softwaresystem "DSP-TOOLS" "CLI for DSP-API"
16+
fuseki = softwaresystem "Fuseki Triplestore" "RDF database" "Database"
17+
sipi = softwaresystem "SIPI" "IIIF image server"
18+
arkResolver = softwaresystem "ARK Resolver" "Forwards ARK URLs to DSP-APP URLs"
19+
dspApi = softwareSystem "DSP-API" "api.dasch.swiss" {
20+
# container <name> [description] [technology] [tags]
21+
22+
eventStoreService = container "Event Store Service"
23+
eventListener = container "Event Listener"
24+
triplestoreService = container "Triplestore Service"
25+
arkService = container "ARK Service"
26+
elasticSearchService = container "Elastic Search Service"
27+
28+
sharedProject = container "Shared Project" "The project that handles shared entities" {
29+
valueObjectsPackage = component "ValueObjects Package" "The package that provides value objects"
30+
errorPackage = component "Error Package" "The package that provides errors"
31+
}
32+
33+
webapiProject = container "Webapi" "The project that wraps webapi V2"
34+
35+
projectSlice = container "Project Slice" "The slice that handles projects"
36+
roleSlice = container "Role Slice" "The slice that handles roles"
37+
schemaSlice = container "Schema Slice" "The slice that handles schemas"
38+
resourceSlice = container "Resource Slice" "The slice that handles resources"
39+
listSlice = container "List Slice" "The slice that handles lists"
40+
routes = container "Routes" "The slice that provides all routes"
41+
42+
userSlice = container "User Slice" "The slice that handles users" {
43+
userCore = component "User Core"
44+
userDomain = component "User Domain"
45+
userHandler = component "User Handler"
46+
47+
userRepo = component "User Repo (API)"
48+
userRepoLive = component "User Repo Live (Implementation)"
49+
userRepoMock = component "User Repo Mock (Implementation for Tests)"
50+
51+
userRoute = component "User Route"
52+
}
53+
54+
}
55+
}
56+
57+
# relationships between users and software systems
58+
user -> dspApp "Uses [Browser]"
59+
user -> arkResolver "Uses [Browser]"
60+
user -> dspTools "Uses [CLI]"
61+
62+
# relationships to/from software systems
63+
dspApp -> dspJsLib
64+
dspJsLib -> dspApi
65+
dspTools -> dspApi
66+
dspApi -> fuseki
67+
dspApi -> sipi
68+
dspTools -> sipi
69+
70+
# relationships to/from containers
71+
webapiProject -> sharedProject "depends on"
72+
projectSlice -> sharedProject "depends on"
73+
roleSlice -> sharedProject "depends on"
74+
schemaSlice -> sharedProject "depends on"
75+
resourceSlice -> sharedProject "depends on"
76+
listSlice -> sharedProject "depends on"
77+
routes -> sharedProject "depends on"
78+
userSlice -> sharedProject "depends on"
79+
80+
# relationships to/from components
81+
userRepo -> userCore "depends on"
82+
userRepoLive -> userCore "depends on"
83+
userRepoMock -> userCore "depends on"
84+
userRoute -> userCore "depends on"
85+
86+
userRepoLive -> userRepo "implements"
87+
userRepoMock -> userRepo "implements"
88+
89+
userCore -> userDomain "contains"
90+
userCore -> userHandler "contains"
691

7-
user -> softwareSystem "Uses"
892
}
993

1094
views {
11-
systemContext softwareSystem "Diagram1" {
95+
systemlandscape "SystemLandscape" "System Landscape of DSP-API" {
96+
include *
97+
autoLayout
98+
}
99+
100+
# systemContext <software system identifier> [key] [description]
101+
systemContext dspApi "SystemContextDspApi" "DSP-API System Context" {
102+
include *
103+
autoLayout
104+
}
105+
106+
# container <software system identifier> [key] [description]
107+
container dspApi "ContainerDspApi" "Containers of DSP-API" {
108+
include *
109+
autoLayout
110+
}
111+
112+
# component <container identifier> [key] [description]
113+
component userSlice "ComponentsOfUserSlice" "Components of the User Slice" {
114+
include *
115+
116+
}
117+
118+
component SharedProject "ComponentsOfSharedProject" "Components of the Shared Project" {
12119
include *
13120
autoLayout
14121
}
15122

123+
# dynamic <*|software system identifier|container identifier> [key] [description]
124+
dynamic userSlice "HttpRequestWithEventsCreateUser" "Example workflow for a HTTP request with events (create user)" {
125+
user -> userRoute "sends HTTP request to"
126+
userRoute -> userRoute "validates input and creates value objects"
127+
userRoute -> userHandler "createUser(vo)"
128+
userHandler -> userRepo "reserve username"
129+
userRepo -> eventStoreService "reserve username"
130+
eventStoreService -> eventStoreService "check if username exists"
131+
eventStoreService -> eventStoreService "reserve username"
132+
userHandler -> userDomain ".make(vo)"
133+
userDomain -> userDomain "create user domain entity + userCreatedEvent(who, what)"
134+
userDomain -> userHandler "return (userDomainEntity + userCreatedEvent)"
135+
userHandler -> userRepo "storeUser(userDomainEntity + userCreatedEvent)"
136+
userRepo -> eventStoreService "storeUser(userDomainEntity + userCreatedEvent)"
137+
eventStoreService -> eventStoreService "store event(s), userCreatedEvent(who, what, when(!))"
138+
eventStoreService -> eventListener "publishEvent(userCreatedEvent)"
139+
eventListener -> triplestoreService "writeToTsService(E)"
140+
triplestoreService -> triplestoreService "SPARQL Update"
141+
eventListener -> arkService "writeToArkService(E)"
142+
arkService -> arkService "create ARK(URL)"
143+
eventListener -> elasticSearchService "writeToEsService(E)"
144+
145+
}
146+
147+
dynamic userSlice "HttpRequestWithEventsUpdateUser" "Example workflow for a HTTP request with events (update username)" {
148+
user -> userRoute "sends HTTP request to"
149+
userRoute -> userRoute "validates input and creates value objects"
150+
userRoute -> userHandler "updateUsername(vo)"
151+
userHandler -> userRepo "getUser(userId)"
152+
userRepo -> eventStoreService "getUser(userId)"
153+
eventStoreService -> eventStoreService "get all events for this user"
154+
eventStoreService -> userDomain "createUserFromEvents(E,E,E,...)"
155+
userDomain -> userHandler "return User"
156+
userHandler -> userDomain "updateUsername(vo)"
157+
userDomain -> userDomain "updateUser(userDomainEntity + userUpdatedEvent(who, what))"
158+
userDomain -> userHandler "return userDomainEntity + userUpdatedEvent(who, what)"
159+
userHandler -> userRepo "storeUser(userDomainEntity + userUpdatedEvent(who, what, when(!))"
160+
161+
userRepo -> eventStoreService "storeUser(userDomainEntity + userCreatedEvent)"
162+
eventStoreService -> eventStoreService "store event(s), userCreatedEvent(who, what, when(!))"
163+
eventStoreService -> eventListener "publishEvent(userCreatedEvent)"
164+
eventListener -> triplestoreService "writeToTsService(E)"
165+
triplestoreService -> triplestoreService "SPARQL Update"
166+
eventListener -> arkService "writeToArkService(E)"
167+
arkService -> arkService "create ARK(URL)"
168+
eventListener -> elasticSearchService "writeToEsService(E)"
169+
170+
autoLayout
171+
}
172+
16173
theme default
17174
}
18175

19176
!adrs decisions
20-
!docs flows
177+
!docs docs
21178
}

0 commit comments

Comments
 (0)