@ossy/workspaces 1.17.2 → 3.0.1

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.
Files changed (67) hide show
  1. package/README.md +4 -0
  2. package/package.json +8 -4
  3. package/src/AuthenticationGuard.jsx +3 -3
  4. package/src/CreateWorkspacePage.jsx +7 -16
  5. package/src/Definition.js +2 -1
  6. package/src/GeneralSettings.jsx +77 -19
  7. package/src/Invitations.jsx +7 -7
  8. package/src/Invitations.page.jsx +1 -1
  9. package/src/Invite.jsx +13 -25
  10. package/src/Invite.page.jsx +1 -1
  11. package/src/SchemasReadBootstrap.jsx +20 -0
  12. package/src/SelectWorkspacePage.jsx +8 -11
  13. package/src/Users.jsx +7 -7
  14. package/src/Users.page.jsx +3 -3
  15. package/src/WorkspaceSettings.page.jsx +1 -1
  16. package/src/accept-invitation.action.js +1 -1
  17. package/src/accept-invitation.api.js +46 -0
  18. package/src/accept-invitation.task.js +6 -5
  19. package/src/create-api-token.action.js +1 -1
  20. package/src/create-api-token.task.js +1 -1
  21. package/src/create.action.js +1 -1
  22. package/src/create.task.js +1 -1
  23. package/src/disable-service.action.js +1 -1
  24. package/src/disable-service.task.js +3 -15
  25. package/src/en.translations.json +36 -30
  26. package/src/enable-service.action.js +1 -1
  27. package/src/enable-service.task.js +3 -15
  28. package/src/entitlements.js +199 -0
  29. package/src/entitlements.spec.js +88 -0
  30. package/src/get-api-tokens.action.js +1 -1
  31. package/src/get-api-tokens.task.js +1 -1
  32. package/src/get-invitations.action.js +1 -1
  33. package/src/get-invitations.task.js +1 -1
  34. package/src/get-schemas.action.js +1 -0
  35. package/src/get-schemas.task.js +14 -0
  36. package/src/get-users.action.js +1 -1
  37. package/src/get-users.task.js +1 -1
  38. package/src/get.action.js +1 -1
  39. package/src/get.task.js +1 -1
  40. package/src/import-schemas.action.js +1 -0
  41. package/src/import-schemas.task.js +26 -0
  42. package/src/index.js +3 -2
  43. package/src/invite-user.action.js +1 -1
  44. package/src/invite-user.task.js +4 -3
  45. package/src/list.action.js +1 -1
  46. package/src/list.task.js +1 -1
  47. package/src/merge-workspace-schemas.js +13 -0
  48. package/src/merge-workspace-schemas.spec.js +16 -0
  49. package/src/remove-member.action.js +1 -1
  50. package/src/remove-member.task.js +1 -1
  51. package/src/schema-ids.js +3 -0
  52. package/src/select-workspace.api.js +50 -0
  53. package/src/selectWorkspace.js +11 -0
  54. package/src/service-keys.js +1 -0
  55. package/src/sv.translations.json +36 -30
  56. package/src/sync-workspace-membership.task.js +7 -6
  57. package/src/workspace-invitation.email.jsx +1 -1
  58. package/src/workspace-users.component.jsx +3 -3
  59. package/src/workspace.aggregate.js +35 -47
  60. package/src/workspace.schema.js +9 -0
  61. package/src/workspaces.events.js +61 -155
  62. package/src/{workspaces.spec.js → workspaces.integration.spec.js} +40 -38
  63. package/src/workspaces.queries.js +27 -12
  64. package/src/get-resource-templates.action.js +0 -1
  65. package/src/get-resource-templates.task.js +0 -18
  66. package/src/import-resource-templates.action.js +0 -1
  67. package/src/import-resource-templates.task.js +0 -26
@@ -1,6 +1,8 @@
1
1
  import casual from 'casual'
2
2
  import { TestUtil } from '@ossy/platform/test'
3
3
 
4
+ const WORKSPACE_SCHEMA = '@ossy/workspaces/schema/workspace'
5
+
4
6
  function authHeaders(token) {
5
7
  return {
6
8
  'Content-Type': 'application/json',
@@ -16,10 +18,10 @@ function workspaceHeaders(userToken, workspaceId) {
16
18
  }
17
19
  }
18
20
 
19
- describe('[workspaces/create]', () => {
21
+ describe('[workspaces/actions/create]', () => {
20
22
 
21
23
  TestUtil.AssertActionAuthenticationNeeded({
22
- actionId: 'workspaces/create',
24
+ actionId: '@ossy/workspaces/actions/create',
23
25
  })
24
26
 
25
27
  describe('given a workspace name', () => {
@@ -28,7 +30,7 @@ describe('[workspaces/create]', () => {
28
30
  const workspaceName = casual.word
29
31
 
30
32
  const response = await TestUtil.InvokeAction({
31
- actionId: 'workspaces/create',
33
+ actionId: '@ossy/workspaces/actions/create',
32
34
  headers: authHeaders(user.token),
33
35
  payload: { name: workspaceName },
34
36
  })
@@ -36,8 +38,8 @@ describe('[workspaces/create]', () => {
36
38
  const body = await response.json()
37
39
 
38
40
  const workspaceCreatedEvent = await TestUtil.GetEvent({
39
- aggregateType: 'Workspace',
40
- type: 'Created',
41
+ type: WORKSPACE_SCHEMA,
42
+ event: 'Created',
41
43
  createdBy: user.id,
42
44
  'payload.name': workspaceName
43
45
  })
@@ -45,35 +47,35 @@ describe('[workspaces/create]', () => {
45
47
  expect(!!workspaceCreatedEvent).toEqual(true)
46
48
  expect(response.status).toEqual(200)
47
49
  expect(response.headers.get('Content-Type').includes('application/json')).toEqual(true)
48
- expect(body.id).toEqual(workspaceCreatedEvent.aggregateId)
50
+ expect(body.id).toEqual(workspaceCreatedEvent.resourceId)
49
51
  expect(body.name).toEqual(workspaceName)
50
52
  expect(body.createdBy).toEqual(user.id)
51
53
  expect(body.created).toEqual(workspaceCreatedEvent.created)
52
54
  expect(body.apiTokens).toEqual([])
53
- expect(Array.isArray(body.resourceTemplates)).toBe(true)
54
- expect(body.resourceTemplates.length).toEqual(0)
55
+ expect(Array.isArray(body.schemas)).toBe(true)
56
+ expect(body.schemas.length).toEqual(0)
55
57
  })
56
58
  })
57
59
 
58
60
  })
59
61
 
60
- describe('[workspaces/list]', () => {
62
+ describe('[workspaces/actions/list]', () => {
61
63
 
62
64
  TestUtil.AssertActionAuthenticationNeeded({
63
- actionId: 'workspaces/list',
65
+ actionId: '@ossy/workspaces/actions/list',
64
66
  })
65
67
 
66
68
  it('must return a minified list of workspaces the user have created', async () => {
67
69
  const user = await TestUtil.GetAuthenticatedTestUser()
68
70
 
69
71
  const workspace = await TestUtil.InvokeAction({
70
- actionId: 'workspaces/create',
72
+ actionId: '@ossy/workspaces/actions/create',
71
73
  headers: authHeaders(user.token),
72
74
  payload: { name: casual.word },
73
75
  }).then(response => response.json())
74
76
 
75
77
  const response = await TestUtil.InvokeAction({
76
- actionId: 'workspaces/list',
78
+ actionId: '@ossy/workspaces/actions/list',
77
79
  headers: authHeaders(user.token),
78
80
  })
79
81
 
@@ -89,10 +91,10 @@ describe('[workspaces/list]', () => {
89
91
 
90
92
  })
91
93
 
92
- describe('[workspaces/get]', () => {
94
+ describe('[workspaces/actions/get]', () => {
93
95
 
94
96
  TestUtil.AssertWorkspaceActionAuthenticationNeeded({
95
- actionId: 'workspaces/get',
97
+ actionId: '@ossy/workspaces/actions/get',
96
98
  payload: { workspaceId: '123' },
97
99
  })
98
100
 
@@ -100,13 +102,13 @@ describe('[workspaces/get]', () => {
100
102
  const user = await TestUtil.GetAuthenticatedTestUser()
101
103
 
102
104
  const workspace = await TestUtil.InvokeAction({
103
- actionId: 'workspaces/create',
105
+ actionId: '@ossy/workspaces/actions/create',
104
106
  headers: authHeaders(user.token),
105
107
  payload: { name: casual.word },
106
108
  }).then(response => response.json())
107
109
 
108
110
  const response = await TestUtil.InvokeAction({
109
- actionId: 'workspaces/get',
111
+ actionId: '@ossy/workspaces/actions/get',
110
112
  headers: workspaceHeaders(user.token, workspace.id),
111
113
  payload: { workspaceId: workspace.id },
112
114
  })
@@ -120,19 +122,19 @@ describe('[workspaces/get]', () => {
120
122
 
121
123
  })
122
124
 
123
- describe('[workspaces/import-resource-templates]', () => {
125
+ describe('[workspaces/actions/import-schemas]', () => {
124
126
 
125
127
  TestUtil.AssertWorkspaceActionAuthenticationNeeded({
126
- actionId: 'workspaces/import-resource-templates',
127
- payload: { templates: [] },
128
+ actionId: '@ossy/workspaces/actions/import-schemas',
129
+ payload: { schemas: [] },
128
130
  headers: { workspaceId: 'ws-test' },
129
131
  })
130
132
 
131
- it('must return the saved templates and create a ResourceTemplatesImported event', async () => {
133
+ it('must return the saved schemas and create a SchemasImported event', async () => {
132
134
  const user = await TestUtil.GetAuthenticatedTestUser()
133
135
 
134
136
  const workspace = await TestUtil.InvokeAction({
135
- actionId: 'workspaces/create',
137
+ actionId: '@ossy/workspaces/actions/create',
136
138
  headers: authHeaders(user.token),
137
139
  payload: { name: casual.word },
138
140
  }).then(response => response.json())
@@ -146,40 +148,40 @@ describe('[workspaces/import-resource-templates]', () => {
146
148
  ]
147
149
 
148
150
  const response = await TestUtil.InvokeAction({
149
- actionId: 'workspaces/import-resource-templates',
151
+ actionId: '@ossy/workspaces/actions/import-schemas',
150
152
  headers: workspaceHeaders(user.token, workspace.id),
151
- payload: { templates: imported },
153
+ payload: { schemas: imported },
152
154
  })
153
155
 
154
156
  const responseBody = await response.json()
155
157
 
156
- const resourceTemplatesImportedEvent = await TestUtil.GetEvent({
157
- aggregateType: 'Workspace',
158
- type: 'ResourceTemplatesImported',
158
+ const schemasImportedEvent = await TestUtil.GetEvent({
159
+ type: WORKSPACE_SCHEMA,
160
+ event: 'SchemasImported',
159
161
  createdBy: user.id,
160
- 'payload.templates': imported
162
+ 'payload.schemas': imported
161
163
  })
162
164
 
163
165
  expect(response.status).toEqual(200)
164
166
  expect(responseBody).toEqual(imported)
165
- expect(!!resourceTemplatesImportedEvent).toEqual(true)
167
+ expect(!!schemasImportedEvent).toEqual(true)
166
168
 
167
169
  })
168
170
 
169
171
  })
170
172
 
171
- describe('[workspaces/get-resource-templates]', () => {
173
+ describe('[workspaces/actions/get-schemas]', () => {
172
174
 
173
175
  TestUtil.AssertWorkspaceActionAuthenticationNeeded({
174
- actionId: 'workspaces/get-resource-templates',
176
+ actionId: '@ossy/workspaces/actions/get-schemas',
175
177
  headers: { workspaceId: 'ws-test' },
176
178
  })
177
179
 
178
- it('must return the saved templates event', async () => {
180
+ it('must return the saved schemas', async () => {
179
181
  const user = await TestUtil.GetAuthenticatedTestUser()
180
182
 
181
183
  const workspace = await TestUtil.InvokeAction({
182
- actionId: 'workspaces/create',
184
+ actionId: '@ossy/workspaces/actions/create',
183
185
  headers: authHeaders(user.token),
184
186
  payload: { name: casual.word },
185
187
  }).then(response => response.json())
@@ -193,9 +195,9 @@ describe('[workspaces/get-resource-templates]', () => {
193
195
  ]
194
196
 
195
197
  const response = await TestUtil.InvokeAction({
196
- actionId: 'workspaces/import-resource-templates',
198
+ actionId: '@ossy/workspaces/actions/import-schemas',
197
199
  headers: workspaceHeaders(user.token, workspace.id),
198
- payload: { templates: imported },
200
+ payload: { schemas: imported },
199
201
  })
200
202
 
201
203
  const responseBody = await response.json()
@@ -207,10 +209,10 @@ describe('[workspaces/get-resource-templates]', () => {
207
209
 
208
210
  })
209
211
 
210
- describe('[workspaces/create-api-token]', () => {
212
+ describe('[workspaces/actions/create-api-token]', () => {
211
213
 
212
214
  TestUtil.AssertWorkspaceActionAuthenticationNeeded({
213
- actionId: 'workspaces/create-api-token',
215
+ actionId: '@ossy/workspaces/actions/create-api-token',
214
216
  payload: { description: 'Api token for GitHub Actions' },
215
217
  headers: { workspaceId: 'ws-test' },
216
218
  })
@@ -219,13 +221,13 @@ describe('[workspaces/create-api-token]', () => {
219
221
  const user = await TestUtil.GetAuthenticatedTestUser()
220
222
 
221
223
  const workspace = await TestUtil.InvokeAction({
222
- actionId: 'workspaces/create',
224
+ actionId: '@ossy/workspaces/actions/create',
223
225
  headers: authHeaders(user.token),
224
226
  payload: { name: casual.word },
225
227
  }).then(response => response.json())
226
228
 
227
229
  const response = await TestUtil.InvokeAction({
228
- actionId: 'workspaces/create-api-token',
230
+ actionId: '@ossy/workspaces/actions/create-api-token',
229
231
  headers: workspaceHeaders(user.token, workspace.id),
230
232
  payload: { description: 'Api token for GitHub Actions' },
231
233
  })
@@ -1,33 +1,48 @@
1
1
  import { EventStore } from '@ossy/event-store'
2
2
  import { createLogger } from '@ossy/observability'
3
+ import { WorkspaceSchema } from './schema-ids.js'
3
4
 
4
5
  const log = createLogger('workspaces')
5
6
 
6
7
  export class WorkspacesQueries {
7
- static getAllByUserIncluded(userId) {
8
+ static getAllByUserIncluded (userId) {
8
9
  log.info('[WorkspacesQueries] getAllByUserIncluded()')
9
10
  return EventStore.Aggregate([
10
- { $group: { _id: { aggregateId: '$aggregateId', aggregateType: '$aggregateType' }, events: { $push: '$$ROOT' } } },
11
+ { $match: { type: WorkspaceSchema.workspace, resourceId: { $exists: true } } },
12
+ { $group: { _id: '$resourceId', events: { $push: '$$ROOT' } } },
11
13
  {
12
14
  $match: {
13
- '_id.aggregateType': 'Workspace',
14
- 'events.createdBy': userId,
15
- 'events.type': { $nin: ['Deleted'], $in: ['Created', 'UserInvitationAccepted'] },
15
+ $or: [
16
+ { 'events.createdBy': userId },
17
+ { 'events.event': 'UserInvitationAccepted', 'events.createdBy': userId },
18
+ ],
19
+ 'events.event': { $in: ['Created', 'UserInvitationAccepted'] },
16
20
  },
17
21
  },
18
22
  {
19
23
  $project: {
20
- events: '$events',
21
- currentState: {
22
- $reduce: {
23
- input: '$events',
24
- initialValue: {},
25
- in: { id: '$_id.aggregateId', name: { $ifNull: ['$$this.payload.name', '$$value.name'] } },
24
+ id: '$_id',
25
+ name: {
26
+ $let: {
27
+ vars: {
28
+ created: {
29
+ $arrayElemAt: [
30
+ {
31
+ $filter: {
32
+ input: '$events',
33
+ as: 'e',
34
+ cond: { $eq: ['$$e.event', 'Created'] },
35
+ },
36
+ },
37
+ 0,
38
+ ],
39
+ },
40
+ },
41
+ in: '$$created.payload.name',
26
42
  },
27
43
  },
28
44
  },
29
45
  },
30
- { $project: { _id: 0, id: '$currentState.id', name: '$currentState.name' } },
31
46
  ]).catch(() => [])
32
47
  }
33
48
  }
@@ -1 +0,0 @@
1
- export const metadata = { id: 'workspaces/get-resource-templates', access: 'workspace' }
@@ -1,18 +0,0 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace } from '@ossy/workspaces/server'
3
- import { getSystemResourceTemplates } from '@ossy/platform'
4
-
5
- export const metadata = { id: 'workspaces/get-resource-templates' }
6
-
7
- export async function run({ payload, req }) {
8
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
-
10
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
11
- const systemTemplates = getSystemResourceTemplates()
12
- const workspaceTemplates = workspace.resourceTemplates || []
13
- const workspaceIds = new Set(workspaceTemplates.map(t => t.id))
14
- return [
15
- ...systemTemplates.filter(t => !workspaceIds.has(t.id)),
16
- ...workspaceTemplates,
17
- ]
18
- }
@@ -1 +0,0 @@
1
- export const metadata = { id: 'workspaces/import-resource-templates', access: 'workspace' }
@@ -1,26 +0,0 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
3
- import { validateResourceTemplatesForImport } from '@ossy/platform'
4
-
5
- export const metadata = { id: 'workspaces/import-resource-templates' }
6
-
7
- export async function run({ payload, req }) {
8
- const userId = payload?.userId ?? req?.userId
9
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
10
- const templates = payload?.templates ?? payload
11
-
12
- if (!Array.isArray(templates)) {
13
- throw Object.assign(new Error('templates must be an array'), { status: 400 })
14
- }
15
-
16
- const reservedIds = new Set([])
17
- const validation = validateResourceTemplatesForImport(templates, reservedIds)
18
- if (!validation.ok) {
19
- throw Object.assign(new Error(validation.message), { status: 400, type: validation.code })
20
- }
21
-
22
- const event = WorkspacesEvents.ResourceTemplatesImported({ createdBy: userId, templates })
23
-
24
- await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
25
- return event.payload.templates
26
- }