@ossy/users 1.13.2 → 1.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @ossy/users
2
+
3
+ User domain package for the Ossy platform — event-sourced user entities, workspace membership, and user-scoped API token actions.
4
+
5
+ ## What this package provides
6
+
7
+ | Primitive | File | Id / export |
8
+ |---|---|---|
9
+ | Schema | `user.schema.js` | `@ossy/users/schema/user` |
10
+ | Schema ids | `schema-ids.js` | `UserSchema` |
11
+ | Aggregate | `user.aggregate.js` | `User` (`kind: 'entity'`, `SchemaId`) |
12
+ | Events | `users.events.js` | `UsersEvents.Created`, `SignInVerified`, `NameUpdated`, … |
13
+ | Queries | `users.queries.js` | `UsersQueries` |
14
+ | Startup | `users.startup.js` | Bot user bootstrap |
15
+
16
+ Client exports (`@ossy/users`) surface action metadata for the SDK. Server exports (`@ossy/users/server`) include the aggregate, events, queries, and validators.
17
+
18
+ ## Actions
19
+
20
+ | Action | Access | Task |
21
+ |--------|--------|------|
22
+ | `@ossy/users/actions/update-details` | authenticated | `@ossy/users/tasks/update-details` |
23
+ | `@ossy/users/actions/get-current-user-history` | authenticated | `@ossy/users/tasks/get-current-user-history` |
24
+ | `@ossy/users/actions/join-workspace` | authenticated | `@ossy/users/tasks/join-workspace` |
25
+ | `@ossy/users/actions/leave-workspace` | authenticated | `@ossy/users/tasks/leave-workspace` |
26
+ | `@ossy/users/actions/get-api-tokens` | authenticated | `@ossy/users/tasks/get-api-tokens` |
27
+ | `@ossy/users/actions/create-api-token` | authenticated | `@ossy/users/tasks/create-api-token` |
28
+ | `@ossy/users/actions/invalidate-api-token` | authenticated | `@ossy/users/tasks/invalidate-api-token` |
29
+
30
+ API token tasks delegate to `@ossy/tokens` (`TokenEvents.Created` / revoke flows).
31
+
32
+ ## User schema (ADR 0006)
33
+
34
+ | Schema | Id | Fields |
35
+ |---|---|---|
36
+ | User | `@ossy/users/schema/user` | `email`, `firstName`, `lastName` |
37
+
38
+ ## Event model (ADR 0008)
39
+
40
+ User entity events use the universal envelope: `type` (schema id), `resourceId`, `event` (lifecycle name), `payload`, `createdBy`.
41
+
42
+ | Event | Effect |
43
+ |---|---|
44
+ | `Created` | New user (`type: 'User'` or `'Bot'`) |
45
+ | `SignInVerified` | Sets `verifiedAt` on first verification |
46
+ | `NameUpdated` | Updates `firstName` / `lastName` |
47
+ | `WorkspaceJoined` | Adds workspace id to `workspaces[]` |
48
+ | `WorkspaceLeft` | Removes workspace id from `workspaces[]` |
49
+
50
+ `UsersEvents.SignedUp` was renamed to `UsersEvents.Created` to match the ADR 0008 lifecycle name.
51
+
52
+ ## Server usage
53
+
54
+ ```js
55
+ import { Aggregate } from '@ossy/event-store'
56
+ import { User, UsersEvents, UserSchema } from '@ossy/users/server'
57
+
58
+ const created = UsersEvents.Created({ email, firstName, lastName })
59
+ const user = await Aggregate.Of(User, created).then(Aggregate.View())
60
+
61
+ UserSchema.user
62
+ // => '@ossy/users/schema/user'
63
+ ```
64
+
65
+ ## Testing
66
+
67
+ ```bash
68
+ npm test -w @ossy/users
69
+ npm run test:integration -w @ossy/users
70
+ ```
71
+
72
+ Unit coverage lives in package tests; `users.integration.spec.js` exercises actions against `@ossy/platform`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ossy/users",
3
3
  "description": "User domain - aggregate, events, and validators for the Ossy user model",
4
- "version": "1.13.2",
4
+ "version": "1.13.3",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "main": "./src/index.js",
@@ -11,7 +11,8 @@
11
11
  "./server": "./src/server.js"
12
12
  },
13
13
  "scripts": {
14
- "test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose"
14
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
15
+ "test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.integration.config.js --verbose"
15
16
  },
16
17
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
17
18
  "license": "MIT",
@@ -28,12 +29,13 @@
28
29
  "registry": "https://registry.npmjs.org"
29
30
  },
30
31
  "dependencies": {
31
- "@ossy/observability": "^1.8.2",
32
+ "@ossy/observability": "^1.8.3",
33
+ "@ossy/schema": "^1.0.1",
32
34
  "nanoid": "^5.1.11"
33
35
  },
34
36
  "devDependencies": {
35
37
  "@jest/globals": "^30.2.0",
36
- "@ossy/platform": "^1.39.2",
38
+ "@ossy/platform": "^1.39.3",
37
39
  "casual": "^1.6.2",
38
40
  "jest": "^30.2.0"
39
41
  },
@@ -41,5 +43,5 @@
41
43
  "/src",
42
44
  "README.md"
43
45
  ],
44
- "gitHead": "2b8745d57fee8b6c08787e2755b4df489291a5cd"
46
+ "gitHead": "a0d89185a17f8de8ce328c3a648c108ff1d61d8f"
45
47
  }
@@ -1 +1 @@
1
- export const metadata = { id: 'users/create-api-token', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/create-api-token', access: 'authenticated' }
@@ -1,12 +1,14 @@
1
+ import { nanoid } from 'nanoid'
1
2
  import jwt from 'jsonwebtoken'
2
3
  import { Aggregate } from '@ossy/event-store'
3
4
  import { Token, TokenEvents } from '@ossy/tokens/server'
4
5
  import { ConfigService } from '@ossy/platform'
5
6
  import { createLogger } from '@ossy/observability'
7
+ import { normalizeScopes } from '@ossy/schema'
6
8
 
7
- export const metadata = { id: 'users/create-api-token' }
9
+ export const metadata = { id: '@ossy/users/tasks/create-api-token' }
8
10
 
9
- const log = createLogger('users/create-api-token')
11
+ const log = createLogger('@ossy/users/tasks/create-api-token')
10
12
 
11
13
  const SECONDS_IN_100_YEARS = 60 * 60 * 24 * 365 * 100
12
14
 
@@ -16,19 +18,35 @@ export async function run({ payload, req }) {
16
18
  const description = payload?.description
17
19
 
18
20
  if (!name || typeof name !== 'string') {
19
- log.debug('[users/create-api-token] No name provided')
21
+ log.debug('[users/tasks/create-api-token] No name provided')
20
22
  throw Object.assign(new Error('No name provided'), { status: 400 })
21
23
  }
22
24
  if (!description || typeof description !== 'string') {
23
- log.debug('[users/create-api-token] No description provided')
25
+ log.debug('[users/tasks/create-api-token] No description provided')
24
26
  throw Object.assign(new Error('No description provided'), { status: 400 })
25
27
  }
26
28
 
27
29
  const expiresIn = SECONDS_IN_100_YEARS
28
30
  const expiresAt = Date.now() + expiresIn * 1000
29
- const token = jwt.sign({ sub: createdBy, type: 'Api' }, ConfigService.TokenSecret, { expiresIn })
30
-
31
- const event = TokenEvents.Created({ name, description, createdBy, type: 'Api', subject: createdBy, token, expiresAt })
31
+ const scopes = normalizeScopes(payload?.scopes)
32
+ const resourceId = nanoid()
33
+ const token = jwt.sign(
34
+ { sub: createdBy, type: 'Api', jti: resourceId, scopes },
35
+ ConfigService.TokenSecret,
36
+ { expiresIn },
37
+ )
38
+
39
+ const event = TokenEvents.Created({
40
+ resourceId,
41
+ name,
42
+ description,
43
+ createdBy,
44
+ type: 'Api',
45
+ subject: createdBy,
46
+ token,
47
+ expiresAt,
48
+ scopes,
49
+ })
32
50
 
33
51
  return Aggregate.Of(Token, event).then(Aggregate.View())
34
52
  }
@@ -1,16 +1,16 @@
1
1
  {
2
- "users/create-api-token.label": "Create API token",
3
- "users/create-api-token.description": "Create a personal API token for the authenticated user",
4
- "users/get-api-tokens.label": "Get API tokens",
5
- "users/get-api-tokens.description": "List API tokens for the authenticated user",
6
- "users/get-current-user-history.label": "Get user history",
7
- "users/get-current-user-history.description": "Return activity history for the current user",
8
- "users/invalidate-api-token.label": "Invalidate API token",
9
- "users/invalidate-api-token.description": "Revoke an API token by id",
10
- "users/join-workspace.label": "Join workspace",
11
- "users/join-workspace.description": "Add the current user to a workspace",
12
- "users/leave-workspace.label": "Leave workspace",
13
- "users/leave-workspace.description": "Remove the current user from a workspace",
14
- "users/update-details.label": "Update user details",
15
- "users/update-details.description": "Update profile fields for the authenticated user"
2
+ "@ossy/users/actions/create-api-token.label": "Create API token",
3
+ "@ossy/users/actions/create-api-token.description": "Create a personal API token for the authenticated user",
4
+ "@ossy/users/actions/get-api-tokens.label": "Get API tokens",
5
+ "@ossy/users/actions/get-api-tokens.description": "List API tokens for the authenticated user",
6
+ "@ossy/users/actions/get-current-user-history.label": "Get user history",
7
+ "@ossy/users/actions/get-current-user-history.description": "Return activity history for the current user",
8
+ "@ossy/users/actions/invalidate-api-token.label": "Invalidate API token",
9
+ "@ossy/users/actions/invalidate-api-token.description": "Revoke an API token by id",
10
+ "@ossy/users/actions/join-workspace.label": "Join workspace",
11
+ "@ossy/users/actions/join-workspace.description": "Add the current user to a workspace",
12
+ "@ossy/users/actions/leave-workspace.label": "Leave workspace",
13
+ "@ossy/users/actions/leave-workspace.description": "Remove the current user from a workspace",
14
+ "@ossy/users/actions/update-details.label": "Update user details",
15
+ "@ossy/users/actions/update-details.description": "Update profile fields for the authenticated user"
16
16
  }
@@ -1 +1 @@
1
- export const metadata = { id: 'users/get-api-tokens', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/get-api-tokens', access: 'authenticated' }
@@ -1,16 +1,17 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
+ import { TokenSchema } from '@ossy/tokens/server'
2
3
  import { createLogger } from '@ossy/observability'
3
4
 
4
- export const metadata = { id: 'users/get-api-tokens' }
5
+ export const metadata = { id: '@ossy/users/tasks/get-api-tokens' }
5
6
 
6
- const log = createLogger('users/get-api-tokens')
7
+ const log = createLogger('@ossy/users/tasks/get-api-tokens')
7
8
 
8
- export async function run({ payload, req }) {
9
+ export async function run ({ payload, req }) {
9
10
  const userId = payload?.userId ?? req?.userId
10
11
 
11
- log.info('[users/get-api-tokens] Fetching API tokens for user')
12
+ log.info('[users/tasks/get-api-tokens] Fetching API tokens for user')
12
13
  const tokens = await Aggregate.Collection.find({
13
- type: 'Token',
14
+ type: TokenSchema.token,
14
15
  'state.subject': userId,
15
16
  'state.type': 'Api',
16
17
  }, { state: true }).toArray().then(docs => docs.map(d => d.state))
@@ -1 +1 @@
1
- export const metadata = { id: 'users/get-current-user-history', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/get-current-user-history', access: 'authenticated' }
@@ -1,10 +1,10 @@
1
1
  import { EventStore } from '@ossy/event-store'
2
2
  import { User } from '@ossy/users/server'
3
3
 
4
- export const metadata = { id: 'users/get-current-user-history' }
4
+ export const metadata = { id: '@ossy/users/tasks/get-current-user-history' }
5
5
 
6
- export async function run({ payload, req }) {
6
+ export async function run ({ payload, req }) {
7
7
  const userId = payload?.userId ?? req?.userId
8
- const events = await EventStore.GetEventStream({ aggregateId: userId, fromVersion: 0 })
8
+ const events = await EventStore.GetResourceStream({ resourceId: userId, fromVersion: 0 })
9
9
  return User.History(events)
10
10
  }
@@ -1 +1 @@
1
- export const metadata = { id: 'users/invalidate-api-token', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/invalidate-api-token', access: 'authenticated' }
@@ -1,7 +1,7 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
2
  import { Token, TokenEvents } from '@ossy/tokens/server'
3
3
 
4
- export const metadata = { id: 'users/invalidate-api-token' }
4
+ export const metadata = { id: '@ossy/users/tasks/invalidate-api-token' }
5
5
 
6
6
  export async function run({ payload, req }) {
7
7
  const createdBy = payload?.userId ?? req?.userId
@@ -1 +1 @@
1
- export const metadata = { id: 'users/join-workspace', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/join-workspace', access: 'authenticated' }
@@ -1,7 +1,7 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
2
  import { User, UsersEvents } from '@ossy/users/server'
3
3
 
4
- export const metadata = { id: 'users/join-workspace' }
4
+ export const metadata = { id: '@ossy/users/tasks/join-workspace' }
5
5
 
6
6
  export async function run({ payload, req }) {
7
7
  const userId = payload?.targetUserId ?? payload?.userId ?? req?.userId
@@ -1 +1 @@
1
- export const metadata = { id: 'users/leave-workspace', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/leave-workspace', access: 'authenticated' }
@@ -1,7 +1,7 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
2
  import { User, UsersEvents } from '@ossy/users/server'
3
3
 
4
- export const metadata = { id: 'users/leave-workspace' }
4
+ export const metadata = { id: '@ossy/users/tasks/leave-workspace' }
5
5
 
6
6
  export async function run({ payload, req }) {
7
7
  const userId = payload?.userId ?? req?.userId
@@ -0,0 +1,3 @@
1
+ export const UserSchema = Object.freeze({
2
+ user: '@ossy/users/schema/user',
3
+ })
package/src/server.js CHANGED
@@ -2,3 +2,4 @@ export * from './user.aggregate.js'
2
2
  export * from './users.events.js'
3
3
  export * from './users.validators.js'
4
4
  export * from './users.queries.js'
5
+ export { UserSchema } from './schema-ids.js'
@@ -1,16 +1,16 @@
1
1
  {
2
- "users/create-api-token.label": "Skapa API-nyckel",
3
- "users/create-api-token.description": "Skapa en personlig API-nyckel för den inloggade användaren",
4
- "users/get-api-tokens.label": "Hämta API-nycklar",
5
- "users/get-api-tokens.description": "Lista API-nycklar för den inloggade användaren",
6
- "users/get-current-user-history.label": "Hämta användarhistorik",
7
- "users/get-current-user-history.description": "Returnera aktivitetshistorik för nuvarande användare",
8
- "users/invalidate-api-token.label": "Ogiltigförklara API-nyckel",
9
- "users/invalidate-api-token.description": "Återkalla en API-nyckel via id",
10
- "users/join-workspace.label": "Gå med i workspace",
11
- "users/join-workspace.description": "Lägg till nuvarande användare i ett workspace",
12
- "users/leave-workspace.label": "Lämna workspace",
13
- "users/leave-workspace.description": "Ta bort nuvarande användare från ett workspace",
14
- "users/update-details.label": "Uppdatera användaruppgifter",
15
- "users/update-details.description": "Uppdatera profilfält för den inloggade användaren"
2
+ "@ossy/users/actions/create-api-token.label": "Skapa API-nyckel",
3
+ "@ossy/users/actions/create-api-token.description": "Skapa en personlig API-nyckel för den inloggade användaren",
4
+ "@ossy/users/actions/get-api-tokens.label": "Hämta API-nycklar",
5
+ "@ossy/users/actions/get-api-tokens.description": "Lista API-nycklar för den inloggade användaren",
6
+ "@ossy/users/actions/get-current-user-history.label": "Hämta användarhistorik",
7
+ "@ossy/users/actions/get-current-user-history.description": "Returnera aktivitetshistorik för nuvarande användare",
8
+ "@ossy/users/actions/invalidate-api-token.label": "Ogiltigförklara API-nyckel",
9
+ "@ossy/users/actions/invalidate-api-token.description": "Återkalla en API-nyckel via id",
10
+ "@ossy/users/actions/join-workspace.label": "Gå med i workspace",
11
+ "@ossy/users/actions/join-workspace.description": "Lägg till nuvarande användare i ett workspace",
12
+ "@ossy/users/actions/leave-workspace.label": "Lämna workspace",
13
+ "@ossy/users/actions/leave-workspace.description": "Ta bort nuvarande användare från ett workspace",
14
+ "@ossy/users/actions/update-details.label": "Uppdatera användaruppgifter",
15
+ "@ossy/users/actions/update-details.description": "Uppdatera profilfält för den inloggade användaren"
16
16
  }
@@ -1 +1 @@
1
- export const metadata = { id: 'users/update-details', access: 'authenticated' }
1
+ export const metadata = { id: '@ossy/users/actions/update-details', access: 'authenticated' }
@@ -1,7 +1,7 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
2
  import { User, UsersEvents } from '@ossy/users/server'
3
3
 
4
- export const metadata = { id: 'users/update-details' }
4
+ export const metadata = { id: '@ossy/users/tasks/update-details' }
5
5
 
6
6
  export async function run({ payload, req }) {
7
7
  const createdBy = payload?.userId ?? req?.userId
@@ -1,85 +1,73 @@
1
+ import { UserSchema } from './schema-ids.js'
2
+
1
3
  export class User {
2
4
 
5
+ static kind = 'entity'
6
+ static SchemaId = UserSchema.user
3
7
  static AggregateType = 'User'
4
8
 
5
- static View(events, savedState = {}) {
9
+ static View (events, savedState = {}) {
6
10
  return events.reduce((user, event) => {
11
+ switch (event.event) {
7
12
 
8
- switch (event.type) {
9
-
10
- case 'SignedUp':
13
+ case 'Created':
11
14
  return {
12
15
  ...user,
13
- id: event.aggregateId,
16
+ id: event.resourceId,
14
17
  type: event.payload.type,
15
18
  created: event.created,
16
19
  email: event.payload.email,
17
- verificationToken: event.payload.verificationToken,
18
20
  firstName: event.payload.firstName,
19
21
  lastName: event.payload.lastName,
20
- workspaces: user.workspaces ?? []
22
+ workspaces: user.workspaces ?? [],
21
23
  }
22
24
 
23
25
  case 'SignInVerified':
24
26
  return {
25
27
  ...user,
26
- verifiedAt: user.verifiedAt ?? event.created
28
+ verifiedAt: user.verifiedAt ?? event.created,
27
29
  }
28
30
 
29
31
  case 'NameUpdated': {
30
32
  const firstName = event.payload.firstName || user.firstName
31
33
  const lastName = event.payload.lastName || user.lastName
32
- return {
33
- ...user,
34
- firstName,
35
- lastName
36
- }
34
+ return { ...user, firstName, lastName }
37
35
  }
38
36
 
39
- // TODO: #415
40
- // case 'EmailUpdated':
41
- // return {
42
- // ...user,
43
- // email: event.payload.email
44
- // }
45
-
46
37
  case 'WorkspaceJoined':
47
38
  return {
48
39
  ...user,
49
- workspaces: [...new Set([...(user.workspaces ?? []), event.payload.workspaceId])]
40
+ workspaces: [...new Set([...(user.workspaces ?? []), event.payload.workspaceId])],
50
41
  }
51
42
 
52
43
  case 'WorkspaceLeft':
53
44
  return {
54
45
  ...user,
55
- workspaces: (user.workspaces ?? []).filter(id => id !== event.payload.workspaceId)
46
+ workspaces: (user.workspaces ?? []).filter(id => id !== event.payload.workspaceId),
56
47
  }
57
48
 
58
49
  default:
59
50
  return user
60
-
61
51
  }
62
52
  }, savedState)
63
53
  }
64
54
 
65
- static UserForWorkspace(events, aggregate) {
55
+ static UserForWorkspace (events, aggregate) {
66
56
  const user = User.View(events, aggregate)
67
57
  return {
68
58
  id: user.id,
69
59
  firstName: user.firstName,
70
60
  lastName: user.lastName,
71
61
  email: user.email,
72
- type: user.type
62
+ type: user.type,
73
63
  }
74
64
  }
75
65
 
76
- static History(events) {
77
- // TODO: add aggregate
66
+ static History (events) {
78
67
  return events
79
68
  .sort((a, b) => b.created - a.created)
80
- .map(event => ({ type: event.type, created: event.created }))
69
+ .map(event => ({ type: event.event, created: event.created }))
81
70
  }
82
-
83
71
  }
84
72
 
85
73
  export { User as Aggregate }
@@ -0,0 +1,11 @@
1
+ export default {
2
+ id: '@ossy/users/schema/user',
3
+ name: 'User',
4
+ categoryName: 'Users',
5
+ icon: 'user',
6
+ fields: [
7
+ { name: 'email', type: 'text' },
8
+ { name: 'firstName', type: 'text' },
9
+ { name: 'lastName', type: 'text' },
10
+ ],
11
+ }
@@ -1,121 +1,57 @@
1
1
  import { nanoid } from 'nanoid'
2
+ import { UserSchema } from './schema-ids.js'
2
3
 
3
4
  /**
4
- * Database queries related to workspace events
5
- * @class
6
- */
5
+ * ADR 0008 user entity events.
6
+ */
7
7
  export class UsersEvents {
8
8
 
9
- static SignedUp({
9
+ static Created ({
10
10
  email,
11
11
  lastName,
12
12
  firstName,
13
13
  userId: _userId,
14
- type = 'User' // is 'User' or 'Bot'
14
+ type = 'User',
15
15
  }) {
16
- const userId = _userId || nanoid()
17
- return ({
18
- type: 'SignedUp',
19
- aggregateId: userId,
20
- createdBy: userId,
21
- payload: {
22
- email: email,
23
- lastName,
24
- firstName,
25
- type: type
26
- }
27
- })
28
- }
29
-
30
- // static SignInRequested({ createdBy }) {
31
- // return ({
32
- // type: 'SignInRequested',
33
- // createdBy: createdBy,
34
- // payload: { verificationToken: TokenService.new() }
35
- // })
36
- // }
16
+ const resourceId = _userId ?? nanoid()
17
+ return {
18
+ type: UserSchema.user,
19
+ resourceId,
20
+ event: 'Created',
21
+ createdBy: resourceId,
22
+ payload: { email, lastName, firstName, type },
23
+ }
24
+ }
37
25
 
38
- /** Same JWT as the `auth` cookie / WebAuth token aggregate — see `UsersQueries.getIdByAuthToken`. */
39
- static SignInVerified({ createdBy, token }) {
40
- return ({
41
- type: 'SignInVerified',
26
+ static SignInVerified ({ createdBy, token }) {
27
+ return {
28
+ event: 'SignInVerified',
42
29
  createdBy,
43
- payload: { token }
44
- })
30
+ payload: { token },
31
+ }
45
32
  }
46
33
 
47
- // static SignedOff({ token, createdBy }) {
48
- // return ({
49
- // type: 'SignedOff',
50
- // createdBy: createdBy,
51
- // payload: { token }
52
- // })
53
- // }
54
-
55
- // static ApiTokenCreated({ name, createdBy, description, token, expiresAt }) {
56
- // return ({
57
- // type: 'ApiTokenCreated',
58
- // createdBy: createdBy,
59
- // payload: {
60
- // name,
61
- // expiresAt: expiresAt,
62
- // description: description,
63
- // token: token
64
- // }
65
- // })
66
- // }
67
-
68
- // static ApiTokenInvalidated({ createdBy, tokenId }) {
69
- // return ({
70
- // type: 'ApiTokenInvalidated',
71
- // createdBy: createdBy,
72
- // payload: { tokenId }
73
- // })
74
- // }
75
-
76
- static NameUpdated({ lastName, firstName, createdBy }) {
77
- return ({
78
- type: 'NameUpdated',
79
- createdBy: createdBy,
80
- payload: {
81
- lastName,
82
- firstName
83
- }
84
- })
85
- }
86
-
87
- // TODO: #415
88
- // static EmailChangeRequested({ email, createdBy }) {
89
- // return ({
90
- // type: 'EmailUpdated',
91
- // createdBy: createdBy,
92
- // payload: { email }
93
- // })
94
- // }
95
-
96
- // TODO: #415
97
- // static EmailUpdated({ email, createdBy }) {
98
- // return ({
99
- // type: 'EmailUpdated',
100
- // createdBy: createdBy,
101
- // payload: { email }
102
- // })
103
- // }
104
-
105
- static WorkspaceJoined({ workspaceId, createdBy }) {
106
- return ({
107
- type: 'WorkspaceJoined',
108
- createdBy,
109
- payload: { workspaceId }
110
- })
34
+ static NameUpdated ({ lastName, firstName, createdBy }) {
35
+ return {
36
+ event: 'NameUpdated',
37
+ createdBy,
38
+ payload: { lastName, firstName },
111
39
  }
40
+ }
112
41
 
113
- static WorkspaceLeft({ workspaceId, createdBy }) {
114
- return ({
115
- type: 'WorkspaceLeft',
116
- createdBy,
117
- payload: { workspaceId }
118
- })
42
+ static WorkspaceJoined ({ workspaceId, createdBy }) {
43
+ return {
44
+ event: 'WorkspaceJoined',
45
+ createdBy,
46
+ payload: { workspaceId },
119
47
  }
48
+ }
120
49
 
50
+ static WorkspaceLeft ({ workspaceId, createdBy }) {
51
+ return {
52
+ event: 'WorkspaceLeft',
53
+ createdBy,
54
+ payload: { workspaceId },
55
+ }
56
+ }
121
57
  }
@@ -1,6 +1,9 @@
1
1
  import casual from 'casual'
2
2
  import { TestUtil, getApiTestBaseUrl, getSetCookieHeader } from '@ossy/platform/test'
3
3
 
4
+ const USER_SCHEMA = '@ossy/users/schema/user'
5
+ const TOKEN_SCHEMA = '@ossy/tokens/schema/token'
6
+
4
7
  const authHeaders = (token) => ({
5
8
  'Content-Type': 'application/json',
6
9
  Authorization: token,
@@ -29,31 +32,31 @@ function invalidSignUpPayload(invalidEmail) {
29
32
  return o
30
33
  }
31
34
 
32
- describe('[authentication/sign-up]', () => {
35
+ describe('[authentication/actions/sign-up]', () => {
33
36
 
34
37
  describe('given an email that is not already in use', () => {
35
38
 
36
39
  it('must return OK 200', () => TestUtil.AssertActionResponse({
37
- actionId: 'authentication/sign-up',
40
+ actionId: '@ossy/authentication/actions/sign-up',
38
41
  body: TestUtil.signUpBody({ email: casual.email }),
39
42
  expectedResponseStatus: 200,
40
43
  expectedResponseBody: { ok: true }
41
44
  }))
42
45
 
43
- it('must produce a SignedUp event', async () => {
46
+ it('must produce a Created event', async () => {
44
47
 
45
48
  const email = casual.email
46
49
 
47
50
  await TestUtil.AssertActionResponse({
48
- actionId: 'authentication/sign-up',
51
+ actionId: '@ossy/authentication/actions/sign-up',
49
52
  body: TestUtil.signUpBody({ email }),
50
53
  expectedResponseStatus: 200,
51
54
  expectedResponseBody: { ok: true }
52
55
  })
53
56
 
54
57
  await TestUtil.AssertEventExist({
55
- aggregateType: 'User',
56
- type: { $in: [ 'SignedUp' ] },
58
+ type: USER_SCHEMA,
59
+ event: 'Created',
57
60
  'payload.email': email
58
61
  })
59
62
 
@@ -67,7 +70,7 @@ describe('[authentication/sign-up]', () => {
67
70
  const email = casual.email
68
71
 
69
72
  const signUpTestRequest = {
70
- actionId: 'authentication/sign-up',
73
+ actionId: '@ossy/authentication/actions/sign-up',
71
74
  body: TestUtil.signUpBody({ email }),
72
75
  expectedResponseStatus: 200,
73
76
  expectedResponseBody: { ok: true }
@@ -81,7 +84,7 @@ describe('[authentication/sign-up]', () => {
81
84
  const email = `dup-${Date.now()}-${casual.email}`
82
85
 
83
86
  const signUpTestRequest = {
84
- actionId: 'authentication/sign-up',
87
+ actionId: '@ossy/authentication/actions/sign-up',
85
88
  body: TestUtil.signUpBody({ email }),
86
89
  expectedResponseStatus: 200,
87
90
  expectedResponseBody: { ok: true }
@@ -91,8 +94,8 @@ describe('[authentication/sign-up]', () => {
91
94
  await TestUtil.AssertActionResponse(signUpTestRequest)
92
95
 
93
96
  const signUpEvents = await TestUtil.GetEvents({
94
- aggregateType: 'User',
95
- type: { $in: [ 'SignedUp' ] },
97
+ type: USER_SCHEMA,
98
+ event: 'Created',
96
99
  'payload.email': email
97
100
  })
98
101
 
@@ -107,7 +110,7 @@ describe('[authentication/sign-up]', () => {
107
110
  it(
108
111
  `must return BAD REQUEST 400 for invalid email: ${invalidEmail}`,
109
112
  () => TestUtil.AssertActionResponse({
110
- actionId: 'authentication/sign-up',
113
+ actionId: '@ossy/authentication/actions/sign-up',
111
114
  payload: invalidSignUpPayload(invalidEmail),
112
115
  expectedResponseStatus: 400,
113
116
  expectedResponseBody: { error: 'Invalid email' }
@@ -118,7 +121,7 @@ describe('[authentication/sign-up]', () => {
118
121
 
119
122
  })
120
123
 
121
- describe('[authentication/request-sign-in]', () => {
124
+ describe('[authentication/actions/request-sign-in]', () => {
122
125
 
123
126
  describe('given an email that is in use', () => {
124
127
 
@@ -126,14 +129,14 @@ describe('[authentication/request-sign-in]', () => {
126
129
  const email = casual.email
127
130
 
128
131
  await TestUtil.AssertActionResponse({
129
- actionId: 'authentication/sign-up',
132
+ actionId: '@ossy/authentication/actions/sign-up',
130
133
  body: TestUtil.signUpBody({ email }),
131
134
  expectedResponseStatus: 200,
132
135
  expectedResponseBody: { ok: true }
133
136
  })
134
137
 
135
138
  await TestUtil.AssertActionResponse({
136
- actionId: 'authentication/request-sign-in',
139
+ actionId: '@ossy/authentication/actions/request-sign-in',
137
140
  payload: { email },
138
141
  expectedResponseStatus: 200,
139
142
  expectedResponseBody: { ok: true }
@@ -146,26 +149,26 @@ describe('[authentication/request-sign-in]', () => {
146
149
  const email = casual.email
147
150
 
148
151
  await TestUtil.AssertActionResponse({
149
- actionId: 'authentication/sign-up',
152
+ actionId: '@ossy/authentication/actions/sign-up',
150
153
  body: TestUtil.signUpBody({ email }),
151
154
  expectedResponseStatus: 200,
152
155
  expectedResponseBody: { ok: true }
153
156
  })
154
157
 
155
158
  await TestUtil.AssertActionResponse({
156
- actionId: 'authentication/request-sign-in',
159
+ actionId: '@ossy/authentication/actions/request-sign-in',
157
160
  payload: { email },
158
161
  expectedResponseStatus: 200,
159
162
  expectedResponseBody: { ok: true }
160
163
  })
161
164
 
162
- const signedUpEvent = await TestUtil.GetEvent({
163
- aggregateType: 'User',
164
- type: { $in: [ 'SignedUp' ] },
165
+ const createdEvent = await TestUtil.GetEvent({
166
+ type: USER_SCHEMA,
167
+ event: 'Created',
165
168
  'payload.email': email
166
169
  })
167
170
 
168
- const jwt = await TestUtil.getLatestVerificationJwtForSubject(signedUpEvent.aggregateId)
171
+ const jwt = await TestUtil.getLatestVerificationJwtForSubject(createdEvent.resourceId)
169
172
  expect(typeof jwt).toBe('string')
170
173
  })
171
174
 
@@ -175,7 +178,7 @@ describe('[authentication/request-sign-in]', () => {
175
178
 
176
179
  it('must, for security reasons, return an OK 200 response', async () => {
177
180
  await TestUtil.AssertActionResponse({
178
- actionId: 'authentication/request-sign-in',
181
+ actionId: '@ossy/authentication/actions/request-sign-in',
179
182
  payload: { email: casual.email },
180
183
  expectedResponseStatus: 200,
181
184
  expectedResponseBody: { ok: true }
@@ -187,7 +190,7 @@ describe('[authentication/request-sign-in]', () => {
187
190
  const verificationTokensBefore = await TestUtil.countVerificationTokenEvents()
188
191
 
189
192
  await TestUtil.AssertActionResponse({
190
- actionId: 'authentication/request-sign-in',
193
+ actionId: '@ossy/authentication/actions/request-sign-in',
191
194
  payload: { email: casual.email },
192
195
  expectedResponseStatus: 200,
193
196
  expectedResponseBody: { ok: true }
@@ -207,7 +210,7 @@ describe('[authentication/request-sign-in]', () => {
207
210
  it(
208
211
  `must return BAD REQUEST 400 for invalid email: ${invalidEmail}`,
209
212
  () => TestUtil.AssertActionResponse({
210
- actionId: 'authentication/request-sign-in',
213
+ actionId: '@ossy/authentication/actions/request-sign-in',
211
214
  payload: { email: invalidEmail },
212
215
  expectedResponseStatus: 400,
213
216
  expectedResponseBody: { error: 'No email provided' }
@@ -220,24 +223,24 @@ describe('[authentication/request-sign-in]', () => {
220
223
 
221
224
  describe('[/users/verify-sign-in][GET]', () => {
222
225
 
223
- describe('given a valid token created from a SignedUp event', () => {
226
+ describe('given a valid token created from a Created event', () => {
224
227
  it('must return OK 200 and an auth cookie and must produce a SignInVerified event', async () => {
225
228
  const email = casual.email
226
229
 
227
230
  await TestUtil.AssertActionResponse({
228
- actionId: 'authentication/sign-up',
231
+ actionId: '@ossy/authentication/actions/sign-up',
229
232
  body: TestUtil.signUpBody({ email }),
230
233
  expectedResponseStatus: 200,
231
234
  expectedResponseBody: { ok: true }
232
235
  })
233
236
 
234
- const signedUpEvent = await TestUtil.GetEvent({
235
- aggregateType: 'User',
236
- type: { $in: [ 'SignedUp' ] },
237
+ const createdEvent = await TestUtil.GetEvent({
238
+ type: USER_SCHEMA,
239
+ event: 'Created',
237
240
  'payload.email': email
238
241
  })
239
242
 
240
- const verificationJwt = await TestUtil.getLatestVerificationJwtForSubject(signedUpEvent.aggregateId)
243
+ const verificationJwt = await TestUtil.getLatestVerificationJwtForSubject(createdEvent.resourceId)
241
244
 
242
245
  const response = await fetch(
243
246
  `${getApiTestBaseUrl()}/users/verify-sign-in?token=${verificationJwt}`,
@@ -247,9 +250,9 @@ describe('[/users/verify-sign-in][GET]', () => {
247
250
  const body = await response.json()
248
251
 
249
252
  const signInVerifiedEvent = await TestUtil.GetEvent({
250
- aggregateType: 'User',
251
- aggregateId: signedUpEvent.aggregateId,
252
- type: { $in: [ 'SignInVerified' ] },
253
+ type: USER_SCHEMA,
254
+ resourceId: createdEvent.resourceId,
255
+ event: 'SignInVerified',
253
256
  })
254
257
 
255
258
  expect(body).toBe('')
@@ -264,26 +267,26 @@ describe('[/users/verify-sign-in][GET]', () => {
264
267
  const email = casual.email
265
268
 
266
269
  await TestUtil.AssertActionResponse({
267
- actionId: 'authentication/sign-up',
270
+ actionId: '@ossy/authentication/actions/sign-up',
268
271
  body: TestUtil.signUpBody({ email }),
269
272
  expectedResponseStatus: 200,
270
273
  expectedResponseBody: { ok: true }
271
274
  })
272
275
 
273
276
  await TestUtil.AssertActionResponse({
274
- actionId: 'authentication/request-sign-in',
277
+ actionId: '@ossy/authentication/actions/request-sign-in',
275
278
  payload: { email },
276
279
  expectedResponseStatus: 200,
277
280
  expectedResponseBody: { ok: true }
278
281
  })
279
282
 
280
- const signedUpEvent = await TestUtil.GetEvent({
281
- aggregateType: 'User',
282
- type: { $in: [ 'SignedUp' ] },
283
+ const createdEvent = await TestUtil.GetEvent({
284
+ type: USER_SCHEMA,
285
+ event: 'Created',
283
286
  'payload.email': email
284
287
  })
285
288
 
286
- const verificationJwt = await TestUtil.getLatestVerificationJwtForSubject(signedUpEvent.aggregateId)
289
+ const verificationJwt = await TestUtil.getLatestVerificationJwtForSubject(createdEvent.resourceId)
287
290
 
288
291
  const response = await fetch(
289
292
  `${getApiTestBaseUrl()}/users/verify-sign-in?token=${verificationJwt}`,
@@ -293,9 +296,9 @@ describe('[/users/verify-sign-in][GET]', () => {
293
296
  const body = await response.json()
294
297
 
295
298
  const signInVerifiedEvent = await TestUtil.GetEvent({
296
- aggregateType: 'User',
297
- aggregateId: signedUpEvent.aggregateId,
298
- type: { $in: [ 'SignInVerified' ] },
299
+ type: USER_SCHEMA,
300
+ resourceId: createdEvent.resourceId,
301
+ event: 'SignInVerified',
299
302
  })
300
303
 
301
304
  expect(body).toBe('')
@@ -334,11 +337,11 @@ describe('[/users/verify-sign-in][GET]', () => {
334
337
 
335
338
  })
336
339
 
337
- describe('[authentication/get-current-user]', () => {
340
+ describe('[authentication/actions/get-current-user]', () => {
338
341
 
339
342
  describe('given no auth token is provided', () => {
340
343
  it('must return null', async () => {
341
- const response = await TestUtil.InvokeAction({ actionId: 'authentication/get-current-user' })
344
+ const response = await TestUtil.InvokeAction({ actionId: '@ossy/authentication/actions/get-current-user' })
342
345
  expect(response.status).toEqual(200)
343
346
  await expect(response.json()).resolves.toBeNull()
344
347
  })
@@ -349,7 +352,7 @@ describe('[authentication/get-current-user]', () => {
349
352
  const user = await TestUtil.GetAuthenticatedTestUser()
350
353
 
351
354
  const response = await TestUtil.InvokeAction({
352
- actionId: 'authentication/get-current-user',
355
+ actionId: '@ossy/authentication/actions/get-current-user',
353
356
  headers: authHeaders(user.token),
354
357
  })
355
358
 
@@ -370,10 +373,10 @@ describe('[authentication/get-current-user]', () => {
370
373
  })
371
374
  })
372
375
 
373
- describe('[users/get-current-user-history]', () => {
376
+ describe('[users/actions/get-current-user-history]', () => {
374
377
 
375
378
  TestUtil.AssertActionAuthenticationNeeded({
376
- actionId: 'users/get-current-user-history',
379
+ actionId: '@ossy/users/actions/get-current-user-history',
377
380
  })
378
381
 
379
382
  describe('given an auth token', () => {
@@ -381,7 +384,7 @@ describe('[users/get-current-user-history]', () => {
381
384
  const user = await TestUtil.GetAuthenticatedTestUser()
382
385
 
383
386
  const response = await TestUtil.InvokeAction({
384
- actionId: 'users/get-current-user-history',
387
+ actionId: '@ossy/users/actions/get-current-user-history',
385
388
  headers: authHeaders(user.token),
386
389
  })
387
390
 
@@ -392,26 +395,26 @@ describe('[users/get-current-user-history]', () => {
392
395
 
393
396
  expect(body.map(x => x.type)).toEqual([
394
397
  'SignInVerified',
395
- 'SignedUp'
398
+ 'Created'
396
399
  ])
397
400
 
398
401
  })
399
402
  })
400
403
  })
401
404
 
402
- describe('[users/create-api-token]', () => {
405
+ describe('[users/actions/create-api-token]', () => {
403
406
 
404
407
  TestUtil.AssertActionAuthenticationNeeded({
405
- actionId: 'users/create-api-token',
408
+ actionId: '@ossy/users/actions/create-api-token',
406
409
  payload: { name: 't', description: 'd' }
407
410
  })
408
411
 
409
412
  describe('given a valid auth token', () => {
410
- it('must return OK 200 with an API token and produce an ApiTokenCreated event', async () => {
413
+ it('must return OK 200 with an API token and produce an Api token Created event', async () => {
411
414
  const user = await TestUtil.GetAuthenticatedTestUser()
412
415
 
413
416
  const response = await TestUtil.InvokeAction({
414
- actionId: 'users/create-api-token',
417
+ actionId: '@ossy/users/actions/create-api-token',
415
418
  headers: authHeaders(user.token),
416
419
  payload: { name: 'cli', description: casual.sentence }
417
420
  })
@@ -428,7 +431,7 @@ describe('[users/create-api-token]', () => {
428
431
  description: event.payload.description,
429
432
  token: event.payload.token,
430
433
  created: event.created,
431
- id: event.aggregateId,
434
+ id: event.resourceId,
432
435
  type: 'Api',
433
436
  name: 'cli',
434
437
  })
@@ -448,7 +451,7 @@ describe('[users/create-api-token]', () => {
448
451
  else payload = invalidDescription
449
452
 
450
453
  const response = await TestUtil.InvokeAction({
451
- actionId: 'users/create-api-token',
454
+ actionId: '@ossy/users/actions/create-api-token',
452
455
  headers: authHeaders(user.token),
453
456
  payload,
454
457
  })
@@ -456,9 +459,11 @@ describe('[users/create-api-token]', () => {
456
459
  const responseBody = await response.json()
457
460
 
458
461
  const event = TestUtil.GetEvent({
459
- aggregateId: user.id,
460
- aggregateType: 'User',
461
- type: 'ApiTokenCreated'
462
+ type: TOKEN_SCHEMA,
463
+ event: 'Created',
464
+ 'payload.type': 'Api',
465
+ 'payload.subject': user.id,
466
+ createdBy: user.id,
462
467
  })
463
468
 
464
469
  expect(response.status).toEqual(400)
@@ -471,10 +476,10 @@ describe('[users/create-api-token]', () => {
471
476
 
472
477
  })
473
478
 
474
- describe('[users/get-api-tokens]', () => {
479
+ describe('[users/actions/get-api-tokens]', () => {
475
480
 
476
481
  TestUtil.AssertActionAuthenticationNeeded({
477
- actionId: 'users/get-api-tokens',
482
+ actionId: '@ossy/users/actions/get-api-tokens',
478
483
  })
479
484
 
480
485
  describe('given authentication token is provided', () => {
@@ -483,13 +488,13 @@ describe('[users/get-api-tokens]', () => {
483
488
  const user = await TestUtil.GetAuthenticatedTestUser()
484
489
 
485
490
  const token = await TestUtil.InvokeAction({
486
- actionId: 'users/create-api-token',
491
+ actionId: '@ossy/users/actions/create-api-token',
487
492
  headers: authHeaders(user.token),
488
493
  payload: { name: 'list-test', description: casual.sentence }
489
494
  }).then(response => response.json())
490
495
 
491
496
  const response = await TestUtil.InvokeAction({
492
- actionId: 'users/get-api-tokens',
497
+ actionId: '@ossy/users/actions/get-api-tokens',
493
498
  headers: authHeaders(user.token),
494
499
  })
495
500
 
@@ -516,7 +521,7 @@ describe('[users/get-api-tokens]', () => {
516
521
 
517
522
  const createApiToken = async (user) => {
518
523
  const token = await TestUtil.InvokeAction({
519
- actionId: 'users/create-api-token',
524
+ actionId: '@ossy/users/actions/create-api-token',
520
525
  headers: authHeaders(user.token),
521
526
  payload: { name: 'delete-test', description: casual.sentence }
522
527
  }).then(response => response.json())
@@ -524,21 +529,21 @@ const createApiToken = async (user) => {
524
529
  return token
525
530
  }
526
531
 
527
- describe('[users/invalidate-api-token]', () => {
532
+ describe('[users/actions/invalidate-api-token]', () => {
528
533
 
529
534
  TestUtil.AssertActionAuthenticationNeeded({
530
- actionId: 'users/invalidate-api-token',
535
+ actionId: '@ossy/users/actions/invalidate-api-token',
531
536
  payload: { tokenId: '123' },
532
537
  })
533
538
 
534
539
  describe('authentication with user token', () => {
535
540
 
536
- it('must return OK 200 and produce an ApiTokenInvalidated event', async () => {
541
+ it('must return OK 200 and produce a Revoked event', async () => {
537
542
  const user = await TestUtil.GetAuthenticatedTestUser()
538
543
  const token = await createApiToken(user)
539
544
 
540
545
  const response = await TestUtil.InvokeAction({
541
- actionId: 'users/invalidate-api-token',
546
+ actionId: '@ossy/users/actions/invalidate-api-token',
542
547
  headers: authHeaders(user.token),
543
548
  payload: { tokenId: token.id },
544
549
  })
@@ -546,9 +551,9 @@ describe('[users/invalidate-api-token]', () => {
546
551
  const responseBody = await response.json()
547
552
 
548
553
  const event = await TestUtil.GetEvent({
549
- aggregateId: token.id,
550
- aggregateType: 'Token',
551
- type: 'Revoked',
554
+ resourceId: token.id,
555
+ type: TOKEN_SCHEMA,
556
+ event: 'Revoked',
552
557
  })
553
558
 
554
559
  expect(response.status).toEqual(200)
@@ -562,13 +567,13 @@ describe('[users/invalidate-api-token]', () => {
562
567
  const token = await createApiToken(user)
563
568
 
564
569
  await TestUtil.InvokeAction({
565
- actionId: 'users/invalidate-api-token',
570
+ actionId: '@ossy/users/actions/invalidate-api-token',
566
571
  headers: authHeaders(user.token),
567
572
  payload: { tokenId: token.id },
568
573
  })
569
574
 
570
575
  const response = await TestUtil.InvokeAction({
571
- actionId: 'users/get-api-tokens',
576
+ actionId: '@ossy/users/actions/get-api-tokens',
572
577
  headers: authHeaders(user.token),
573
578
  })
574
579
 
@@ -582,12 +587,12 @@ describe('[users/invalidate-api-token]', () => {
582
587
 
583
588
  describe('authentication with API token', () => {
584
589
 
585
- it('must return OK 200 and produce an ApiTokenInvalidated event', async () => {
590
+ it('must return OK 200 and produce a Revoked event', async () => {
586
591
  const user = await TestUtil.GetAuthenticatedTestUser()
587
592
  const token = await createApiToken(user)
588
593
 
589
594
  const response = await TestUtil.InvokeAction({
590
- actionId: 'users/invalidate-api-token',
595
+ actionId: '@ossy/users/actions/invalidate-api-token',
591
596
  headers: authHeaders(token.token),
592
597
  payload: { tokenId: token.id },
593
598
  })
@@ -595,9 +600,9 @@ describe('[users/invalidate-api-token]', () => {
595
600
  const responseBody = await response.json()
596
601
 
597
602
  const event = await TestUtil.GetEvent({
598
- aggregateId: token.id,
599
- aggregateType: 'Token',
600
- type: 'Revoked',
603
+ resourceId: token.id,
604
+ type: TOKEN_SCHEMA,
605
+ event: 'Revoked',
601
606
  })
602
607
 
603
608
  expect(response.status).toEqual(200)
@@ -611,13 +616,13 @@ describe('[users/invalidate-api-token]', () => {
611
616
  const token = await createApiToken(user)
612
617
 
613
618
  await TestUtil.InvokeAction({
614
- actionId: 'users/invalidate-api-token',
619
+ actionId: '@ossy/users/actions/invalidate-api-token',
615
620
  headers: authHeaders(token.token),
616
621
  payload: { tokenId: token.id },
617
622
  })
618
623
 
619
624
  const response = await TestUtil.InvokeAction({
620
- actionId: 'users/get-api-tokens',
625
+ actionId: '@ossy/users/actions/get-api-tokens',
621
626
  headers: authHeaders(token.token),
622
627
  })
623
628
 
@@ -1,52 +1,37 @@
1
1
  import { EventStore, Aggregate } from '@ossy/event-store'
2
+ import { UserSchema } from './schema-ids.js'
2
3
  import { createLogger } from '@ossy/observability'
3
4
 
4
5
  const log = createLogger('users')
5
6
 
6
- /**
7
- * Database queries related to user aggregates.
8
- * @class
9
- */
10
7
  export class UsersQueries {
11
8
 
12
- static getByEmail(email) {
9
+ static getByEmail (email) {
13
10
  log.debug(`[UsersQueries][getByEmail()] Fetch user for ${email}`)
14
11
 
15
12
  return Aggregate.Collection.findOne({
16
- type: 'User',
17
- 'state.email': email
13
+ type: UserSchema.user,
14
+ 'state.email': email,
18
15
  }).then(aggregate => aggregate?.state)
19
- .then(user => {
20
- !!user
21
- ? log.debug('[UsersQueries][getByEmail()] Found user', { user })
22
- : log.debug('[UsersQueries][getByEmail()] No user found')
23
-
24
- return user
25
- })
26
16
  }
27
17
 
28
- static getIdByVerificationToken(token) {
18
+ static getIdByVerificationToken (token) {
29
19
  log.info('[UsersQueries] Using verificationToken to fetch userId')
30
20
 
31
21
  return EventStore.FindEvent({
32
- aggregateType: 'User',
33
- type: { $in: [ 'SignedUp', 'SignInRequested' ] },
34
- 'payload.verificationToken': token
35
- }).then(event => event.aggregateId)
22
+ type: UserSchema.user,
23
+ event: 'Created',
24
+ 'payload.verificationToken': token,
25
+ }).then(event => event?.resourceId)
36
26
  }
37
27
 
38
- static getIdByAuthToken(token) {
28
+ static getIdByAuthToken (token) {
39
29
  log.info('[UsersQueries] Using authToken to fetch userId')
40
- log.debug('[UsersQueries] token', { token })
41
30
 
42
31
  return EventStore.FindEvent({
43
- aggregateType: 'User',
44
- type: { $in: [ 'SignInVerified' ] },
45
- 'payload.token': token
46
- }).then(event => {
47
- log.debug('[UsersQueries] Found', { event })
48
- return event.aggregateId
49
- })
32
+ type: UserSchema.user,
33
+ event: 'SignInVerified',
34
+ 'payload.token': token,
35
+ }).then(event => event?.resourceId)
50
36
  }
51
-
52
37
  }
@@ -1,5 +1,5 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
- import { UsersEvents, User } from './server.js'
2
+ import { UsersEvents, User, UserSchema } from './server.js'
3
3
  import { createLogger } from '@ossy/observability'
4
4
 
5
5
  const log = createLogger('users')
@@ -19,7 +19,7 @@ export async function run ({ env }) {
19
19
  log.info('[users.startup][create-bot-user] Starting bot user creation script')
20
20
 
21
21
  const existing = await Aggregate.Collection.findOne({
22
- type: 'User',
22
+ type: UserSchema.user,
23
23
  'state.email': botEmail,
24
24
  })
25
25
 
@@ -30,12 +30,12 @@ export async function run ({ env }) {
30
30
 
31
31
  log.info('[users.startup][create-bot-user] No bot user found, creating')
32
32
 
33
- const signedUpEvent = UsersEvents.SignedUp({
33
+ const createdEvent = UsersEvents.Created({
34
34
  type: 'Bot',
35
35
  email: botEmail,
36
36
  userId: botUserId,
37
37
  })
38
38
 
39
- await Aggregate.Of(User, signedUpEvent)
39
+ await Aggregate.Of(User, createdEvent)
40
40
  log.info('[users.startup][create-bot-user] Bot user created')
41
41
  }