@ossy/workspaces 1.16.7 → 1.17.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 (53) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -4
  3. package/src/AuthenticationGuard.jsx +23 -10
  4. package/src/CreateWorkspacePage.jsx +18 -12
  5. package/src/Definition.js +1 -17
  6. package/src/GeneralSettings.jsx +27 -70
  7. package/src/Invitations.jsx +11 -19
  8. package/src/Invite.jsx +17 -14
  9. package/src/SelectWorkspacePage.jsx +17 -22
  10. package/src/Users.jsx +10 -15
  11. package/src/accept-invitation.action.js +1 -85
  12. package/src/accept-invitation.task.js +84 -0
  13. package/src/create-api-token.action.js +1 -17
  14. package/src/create-api-token.task.js +16 -0
  15. package/src/create.action.js +1 -30
  16. package/src/create.task.js +29 -0
  17. package/src/disable-service.action.js +1 -28
  18. package/src/disable-service.task.js +27 -0
  19. package/src/en.translations.json +72 -0
  20. package/src/enable-service.action.js +1 -28
  21. package/src/enable-service.task.js +27 -0
  22. package/src/get-api-tokens.action.js +1 -11
  23. package/src/get-api-tokens.task.js +10 -0
  24. package/src/get-invitations.action.js +1 -11
  25. package/src/get-invitations.task.js +10 -0
  26. package/src/get-resource-templates.action.js +1 -19
  27. package/src/get-resource-templates.task.js +18 -0
  28. package/src/get-users.action.js +1 -14
  29. package/src/get-users.task.js +13 -0
  30. package/src/get.action.js +1 -14
  31. package/src/get.task.js +13 -0
  32. package/src/import-resource-templates.action.js +1 -27
  33. package/src/import-resource-templates.task.js +26 -0
  34. package/src/index.js +14 -2
  35. package/src/invite-user.action.js +1 -85
  36. package/src/invite-user.task.js +84 -0
  37. package/src/list.action.js +1 -9
  38. package/src/list.task.js +8 -0
  39. package/src/remove-member.action.js +1 -17
  40. package/src/remove-member.task.js +16 -0
  41. package/src/server.js +3 -0
  42. package/src/sv.translations.json +72 -0
  43. package/src/sync-workspace-membership.task.js +4 -3
  44. package/src/workspaces.spec.js +71 -80
  45. package/src/current.api.js +0 -30
  46. package/src/invitations.api.js +0 -46
  47. package/src/item.api.js +0 -32
  48. package/src/list.api.js +0 -35
  49. package/src/members.api.js +0 -42
  50. package/src/resource-templates.api.js +0 -35
  51. package/src/services-disable.api.js +0 -23
  52. package/src/services-enable.api.js +0 -23
  53. package/src/tokens.api.js +0 -35
@@ -0,0 +1,26 @@
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
+ }
package/src/index.js CHANGED
@@ -1,3 +1,17 @@
1
+ export { metadata as ListWorkspaces } from './list.action.js'
2
+ export { metadata as GetWorkspace } from './get.action.js'
3
+ export { metadata as CreateWorkspace } from './create.action.js'
4
+ export { metadata as InviteUser } from './invite-user.action.js'
5
+ export { metadata as AcceptInvitation } from './accept-invitation.action.js'
6
+ export { metadata as GetInvitations } from './get-invitations.action.js'
7
+ export { metadata as GetUsers } from './get-users.action.js'
8
+ export { metadata as RemoveMember } from './remove-member.action.js'
9
+ export { metadata as CreateWorkspaceApiToken } from './create-api-token.action.js'
10
+ export { metadata as GetWorkspaceApiTokens } from './get-api-tokens.action.js'
11
+ export { metadata as GetResourceTemplates } from './get-resource-templates.action.js'
12
+ export { metadata as ImportResourceTemplates } from './import-resource-templates.action.js'
13
+ export { metadata as EnableService } from './enable-service.action.js'
14
+ export { metadata as DisableService } from './disable-service.action.js'
1
15
  export { CreateWorkspacePage } from './CreateWorkspacePage.jsx'
2
16
  export { SelectWorkspacePage } from './SelectWorkspacePage.jsx'
3
17
  export { GeneralSettings } from './GeneralSettings.jsx'
@@ -7,5 +21,3 @@ export { Users } from './Users.jsx'
7
21
  export { AuthenticationGuard } from './AuthenticationGuard.jsx'
8
22
  export { patchUserWorkspaceId } from './patchUserWorkspaceId.js'
9
23
  export { Definition } from './Definition.js'
10
- export { Workspace } from './workspace.aggregate.js'
11
- export { WorkspacesEvents } from './workspaces.events.js'
@@ -1,85 +1 @@
1
- import jwt from 'jsonwebtoken'
2
- import { Aggregate } from '@ossy/event-store'
3
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
4
- import { Token, TokenEvents } from '@ossy/tokens'
5
- import { EmailRenderer } from '@ossy/email'
6
- import { ConfigService } from '@ossy/platform'
7
- import { createLogger } from '@ossy/observability'
8
- import WorkspaceInvitationEmail, { subject as workspaceInvitationSubject } from './workspace-invitation.email.jsx'
9
-
10
- const log = createLogger('workspaces/invite-user')
11
-
12
- const isEmailLike = maybeEmail => /.@.+\.../.test(maybeEmail)
13
-
14
- function createVerificationToken(workspaceId) {
15
- const expiresIn = ConfigService.TokenValidity
16
- return jwt.sign({ aud: workspaceId }, ConfigService.TokenSecret, { expiresIn })
17
- }
18
-
19
- export const id = 'workspaces/invite-user'
20
- export const access = 'workspace'
21
-
22
- export async function run({ payload, integrations, req }) {
23
- const createdBy = payload?.userId ?? req?.userId
24
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
25
- const email = payload?.email ?? req?.body
26
- const inviterEmail = payload?.inviterEmail ?? req?.user?.email
27
-
28
- if (!isEmailLike(email)) {
29
- throw Object.assign(new Error('Invalid email'), { status: 400 })
30
- }
31
-
32
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
33
-
34
- const userInvitation = workspace.invitations.find(inv => inv.email === email)
35
- if (userInvitation && userInvitation.expiresAt > Date.now()) {
36
- throw Object.assign(new Error('User already invited'), { status: 400 })
37
- }
38
-
39
- const expiresIn = ConfigService.TokenValidity
40
- const expiresAt = Date.now() + expiresIn * 1000
41
- const inviteEvent = WorkspacesEvents.UserInvited({ createdBy, email, expiresAt })
42
-
43
- const rawToken = createVerificationToken(workspaceId)
44
- const tokenEvent = TokenEvents.Created({
45
- type: 'Verification',
46
- createdBy,
47
- name: 'Workspace Invitation',
48
- subject: email,
49
- audience: workspace.id,
50
- token: rawToken,
51
- expiresAt,
52
- })
53
-
54
- const existingUser = await Aggregate.Collection.findOne({ type: 'User', 'state.email': email }).then(agg => agg?.state)
55
- if (existingUser && workspace.users.includes(existingUser.id)) {
56
- throw Object.assign(new Error('User already in workspace'), { status: 400 })
57
- }
58
-
59
- await Promise.all([
60
- Aggregate.Of(Workspace, workspace.id).then(Aggregate.Add(inviteEvent)),
61
- Aggregate.Of(Token, tokenEvent),
62
- ])
63
-
64
- const baseUrl = ConfigService.getWebClientBaseUrl(req)
65
- const { html, text } = EmailRenderer.render(WorkspaceInvitationEmail, {
66
- workspace,
67
- verificationToken: rawToken,
68
- invitedBy: inviterEmail,
69
- baseUrl,
70
- })
71
-
72
- const emailClient = integrations?.get?.('email')
73
- if (emailClient) {
74
- await emailClient.send({ to: email, from: 'noreply@ossy.se', subject: workspaceInvitationSubject, html, text })
75
- } else if (ConfigService.BuildEnvironment === 'local') {
76
- log.info('-----------YOU GOT MAIL-----------')
77
- log.info(`To: ${email}`)
78
- log.info(`From: noreply@ossy.se`)
79
- log.info(`Subject: ${workspaceInvitationSubject}`)
80
- log.info(text || html)
81
- log.info('----------------------------------')
82
- }
83
-
84
- return ''
85
- }
1
+ export const metadata = { id: 'workspaces/invite-user', access: 'workspace' }
@@ -0,0 +1,84 @@
1
+ import jwt from 'jsonwebtoken'
2
+ import { Aggregate } from '@ossy/event-store'
3
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
4
+ import { Token, TokenEvents } from '@ossy/tokens/server'
5
+ import { EmailRenderer } from '@ossy/email'
6
+ import { ConfigService } from '@ossy/platform'
7
+ import { createLogger } from '@ossy/observability'
8
+ import WorkspaceInvitationEmail, { subject as workspaceInvitationSubject } from './workspace-invitation.email.jsx'
9
+
10
+ export const metadata = { id: 'workspaces/invite-user' }
11
+
12
+ const log = createLogger('workspaces/invite-user')
13
+
14
+ const isEmailLike = maybeEmail => /.@.+\.../.test(maybeEmail)
15
+
16
+ function createVerificationToken(workspaceId) {
17
+ const expiresIn = ConfigService.TokenValidity
18
+ return jwt.sign({ aud: workspaceId }, ConfigService.TokenSecret, { expiresIn })
19
+ }
20
+
21
+ export async function run({ payload, integrations, req }) {
22
+ const createdBy = payload?.userId ?? req?.userId
23
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
24
+ const email = payload?.email ?? req?.body
25
+ const inviterEmail = payload?.inviterEmail ?? req?.user?.email
26
+
27
+ if (!isEmailLike(email)) {
28
+ throw Object.assign(new Error('Invalid email'), { status: 400 })
29
+ }
30
+
31
+ const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
32
+
33
+ const userInvitation = workspace.invitations.find(inv => inv.email === email)
34
+ if (userInvitation && userInvitation.expiresAt > Date.now()) {
35
+ throw Object.assign(new Error('User already invited'), { status: 400 })
36
+ }
37
+
38
+ const expiresIn = ConfigService.TokenValidity
39
+ const expiresAt = Date.now() + expiresIn * 1000
40
+ const inviteEvent = WorkspacesEvents.UserInvited({ createdBy, email, expiresAt })
41
+
42
+ const rawToken = createVerificationToken(workspaceId)
43
+ const tokenEvent = TokenEvents.Created({
44
+ type: 'Verification',
45
+ createdBy,
46
+ name: 'Workspace Invitation',
47
+ subject: email,
48
+ audience: workspace.id,
49
+ token: rawToken,
50
+ expiresAt,
51
+ })
52
+
53
+ const existingUser = await Aggregate.Collection.findOne({ type: 'User', 'state.email': email }).then(agg => agg?.state)
54
+ if (existingUser && workspace.users.includes(existingUser.id)) {
55
+ throw Object.assign(new Error('User already in workspace'), { status: 400 })
56
+ }
57
+
58
+ await Promise.all([
59
+ Aggregate.Of(Workspace, workspace.id).then(Aggregate.Add(inviteEvent)),
60
+ Aggregate.Of(Token, tokenEvent),
61
+ ])
62
+
63
+ const baseUrl = ConfigService.getWebClientBaseUrl(req)
64
+ const { html, text } = EmailRenderer.render(WorkspaceInvitationEmail, {
65
+ workspace,
66
+ verificationToken: rawToken,
67
+ invitedBy: inviterEmail,
68
+ baseUrl,
69
+ })
70
+
71
+ const emailClient = integrations?.get?.('email')
72
+ if (emailClient) {
73
+ await emailClient.send({ to: email, from: 'noreply@ossy.se', subject: workspaceInvitationSubject, html, text })
74
+ } else if (ConfigService.BuildEnvironment === 'local') {
75
+ log.info('-----------YOU GOT MAIL-----------')
76
+ log.info(`To: ${email}`)
77
+ log.info(`From: noreply@ossy.se`)
78
+ log.info(`Subject: ${workspaceInvitationSubject}`)
79
+ log.info(text || html)
80
+ log.info('----------------------------------')
81
+ }
82
+
83
+ return ''
84
+ }
@@ -1,9 +1 @@
1
- import { WorkspacesQueries } from './workspaces.queries.js'
2
-
3
- export const id = 'workspaces/list'
4
- export const access = 'authenticated'
5
-
6
- export async function run({ payload, req }) {
7
- const userId = payload?.userId ?? req?.userId
8
- return WorkspacesQueries.getAllByUserIncluded(userId)
9
- }
1
+ export const metadata = { id: 'workspaces/list', access: 'authenticated' }
@@ -0,0 +1,8 @@
1
+ import { WorkspacesQueries } from './workspaces.queries.js'
2
+
3
+ export const metadata = { id: 'workspaces/list' }
4
+
5
+ export async function run({ payload, req }) {
6
+ const userId = payload?.userId ?? req?.userId
7
+ return WorkspacesQueries.getAllByUserIncluded(userId)
8
+ }
@@ -1,17 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Workspace, WorkspacesEvents } from '@ossy/workspaces'
3
-
4
- export const id = 'workspaces/remove-member'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
10
- const memberId = payload?.memberId ?? req?.memberId
11
-
12
- if (!memberId) throw Object.assign(new Error('memberId is required'), { status: 400 })
13
-
14
- const event = WorkspacesEvents.UserRemoved({ createdBy, userId: memberId })
15
- await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
16
- return null
17
- }
1
+ export const metadata = { id: 'workspaces/remove-member', access: 'workspace' }
@@ -0,0 +1,16 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
3
+
4
+ export const metadata = { id: 'workspaces/remove-member' }
5
+
6
+ export async function run({ payload, req }) {
7
+ const createdBy = payload?.userId ?? req?.userId
8
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
+ const memberId = payload?.memberId ?? req?.memberId
10
+
11
+ if (!memberId) throw Object.assign(new Error('memberId is required'), { status: 400 })
12
+
13
+ const event = WorkspacesEvents.UserRemoved({ createdBy, userId: memberId })
14
+ await Aggregate.Of(Workspace, workspaceId).then(Aggregate.Add(event))
15
+ return null
16
+ }
package/src/server.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Workspace } from './workspace.aggregate.js'
2
+ export { WorkspacesEvents } from './workspaces.events.js'
3
+ export * from './workspaces.queries.js'
@@ -0,0 +1,72 @@
1
+ {
2
+ "profile/workspaces/create.documentTitle": "Skapa workspace",
3
+ "workspaces.create.title": "Skapa ett nytt workspace",
4
+ "workspaces.create.namePlaceholder": "Workspace-namn",
5
+ "workspaces.create.cancel": "Avbryt",
6
+ "workspaces.create.submit": "Skapa",
7
+ "profile/workspaces.documentTitle": "Välj workspace",
8
+ "workspaces.select.title": "Mina workspaces",
9
+ "workspaces.select.newWorkspace": "Nytt workspace",
10
+ "workspaces.select.loading": "Laddar...",
11
+ "workspaces.select.description": "Workspaces är separata miljöer där du kan hantera resurser, mallar, tjänster och fakturering.",
12
+ "workspaces.select.errorTitle": "Vi kunde inte ladda dina workspaces",
13
+ "workspaces.select.errorText": "Något gick fel när dina workspaces skulle laddas. Kanske har din session gått ut, eller så har vi klantat till det. Prova att börja om från startsidan.",
14
+ "workspaces.select.errorAction": "Gå till startsidan",
15
+ "workspaces.select.emptyTitle": "Nu sätter vi igång",
16
+ "workspaces.select.emptyText": "För att komma igång behöver vi skapa ett workspace. Ett workspace kapslar in resurser, resursmallar, faktureringsinformation och vilka användare som har tillgång.",
17
+ "workspaces.select.emptyAction": "Skapa ett workspace",
18
+ "workspaces/create.label": "Skapa workspace",
19
+ "workspaces/create.description": "Skapa ett nytt workspace för den inloggade användaren",
20
+ "workspaces/accept-invitation.label": "Acceptera inbjudan",
21
+ "workspaces/accept-invitation.description": "Acceptera en workspace-inbjudan",
22
+ "workspaces/create-api-token.label": "Skapa workspace API-nyckel",
23
+ "workspaces/create-api-token.description": "Skapa en API-nyckel kopplad till workspace",
24
+ "workspaces/disable-service.label": "Inaktivera tjänst",
25
+ "workspaces/disable-service.description": "Inaktivera en tjänst för workspace",
26
+ "workspaces/enable-service.label": "Aktivera tjänst",
27
+ "workspaces/enable-service.description": "Aktivera en tjänst för workspace",
28
+ "workspaces/get-api-tokens.label": "Hämta workspace API-nycklar",
29
+ "workspaces/get-api-tokens.description": "Lista API-nycklar för workspace",
30
+ "workspaces/get-invitations.label": "Hämta inbjudningar",
31
+ "workspaces/get-invitations.description": "Lista väntande workspace-inbjudningar",
32
+ "workspaces/get-resource-templates.label": "Hämta resursmallar",
33
+ "workspaces/get-resource-templates.description": "Lista resursmallar tillgängliga för workspace",
34
+ "workspaces/get-users.label": "Hämta workspace-användare",
35
+ "workspaces/get-users.description": "Lista medlemmar i workspace",
36
+ "workspaces/get.label": "Hämta workspace",
37
+ "workspaces/get.description": "Ladda ett workspace via id",
38
+ "workspaces/import-resource-templates.label": "Importera resursmallar",
39
+ "workspaces/import-resource-templates.description": "Importera resursmallar till workspace",
40
+ "workspaces/invite-user.label": "Bjud in användare",
41
+ "workspaces/invite-user.description": "Skicka en workspace-inbjudan till en användare",
42
+ "workspaces/remove-member.label": "Ta bort medlem",
43
+ "workspaces/remove-member.description": "Ta bort en användare från workspace",
44
+ "workspaces/list.label": "Lista workspaces",
45
+ "workspaces/list.description": "Lista workspaces för den inloggade användaren",
46
+ "workspace/settings.documentTitle": "Workspace-inställningar",
47
+ "workspace.settings.title": "Workspace-inställningar",
48
+ "workspace/users.documentTitle": "Användare",
49
+ "workspace.users.title": "Användare",
50
+ "workspace.users.description": "Hantera användare i ditt workspace.",
51
+ "workspace.users.invite": "Bjud in användare",
52
+ "workspace.users.loading": "Laddar",
53
+ "workspace/invitations.documentTitle": "Inbjudningar",
54
+ "workspace/invitations/add.documentTitle": "Bjud in användare",
55
+ "workspace.invitations.title": "Inbjudningar",
56
+ "workspace.settings.detailsTitle": "Workspace-detaljer",
57
+ "workspace.settings.edit": "Redigera",
58
+ "workspace.settings.name": "Namn",
59
+ "workspace.settings.created": "Skapad",
60
+ "workspace.settings.services": "Tjänster",
61
+ "workspace.settings.loading": "Laddar...",
62
+ "workspace.invitations.description": "Bjud in användare till ditt workspace för att samarbeta.",
63
+ "workspace.invitations.invite": "Bjud in användare",
64
+ "workspace.invitations.empty": "Inga inbjudningar",
65
+ "workspace.invite.title": "Bjud in användare",
66
+ "workspace.invite.description": "Bjud in en användare att hantera och bidra med innehåll i detta workspace",
67
+ "workspace.invite.emailPlaceholder": "E-postadress",
68
+ "workspace.invite.submit": "Skicka inbjudan",
69
+ "workspace.invite.successTitle": "Klart",
70
+ "workspace.invite.successBody": "Vi skickade en inbjudan via e-post. När de har accepterat visas de i användarlistan.",
71
+ "workspace.invite.error": "Något gick fel — försök igen om några minuter"
72
+ }
@@ -1,5 +1,6 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
- import { User, UsersEvents } from '@ossy/users'
2
+ import { User, UsersEvents } from '@ossy/users/server'
3
+ import { JoinWorkspace, LeaveWorkspace } from '@ossy/users'
3
4
 
4
5
  export const metadata = {
5
6
  id: 'sync-workspace-membership',
@@ -17,7 +18,7 @@ export async function run ({ event, sdk }) {
17
18
  const userId = event.payload.userId
18
19
 
19
20
  if (sdk) {
20
- await sdk.users.removeWorkspace({ userId, workspaceId })
21
+ await sdk.invoke(LeaveWorkspace, { userId, workspaceId })
21
22
  return
22
23
  }
23
24
 
@@ -30,7 +31,7 @@ export async function run ({ event, sdk }) {
30
31
  const userId = event.createdBy
31
32
 
32
33
  if (sdk) {
33
- await sdk.users.addWorkspace({ userId, workspaceId, createdBy: userId })
34
+ await sdk.invoke(JoinWorkspace, { userId, workspaceId, createdBy: userId })
34
35
  return
35
36
  }
36
37
 
@@ -1,6 +1,13 @@
1
1
  import casual from 'casual'
2
2
  import { TestUtil } from '@ossy/platform/test'
3
3
 
4
+ function authHeaders(token) {
5
+ return {
6
+ 'Content-Type': 'application/json',
7
+ Authorization: token,
8
+ }
9
+ }
10
+
4
11
  function workspaceHeaders(userToken, workspaceId) {
5
12
  return {
6
13
  'Content-Type': 'application/json',
@@ -9,12 +16,10 @@ function workspaceHeaders(userToken, workspaceId) {
9
16
  }
10
17
  }
11
18
 
12
- describe('[/workspaces][POST]', () => {
19
+ describe('[workspaces/create]', () => {
13
20
 
14
- TestUtil.AssertAuthenticationNeeded({
15
- endpoint: '/workspaces',
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json' },
21
+ TestUtil.AssertActionAuthenticationNeeded({
22
+ actionId: 'workspaces/create',
18
23
  })
19
24
 
20
25
  describe('given a workspace name', () => {
@@ -22,11 +27,10 @@ describe('[/workspaces][POST]', () => {
22
27
  const user = await TestUtil.GetAuthenticatedTestUser()
23
28
  const workspaceName = casual.word
24
29
 
25
- const response = await TestUtil.MakeRequest({
26
- endpoint: '/workspaces',
27
- method: 'POST',
28
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
29
- body: JSON.stringify({ name: workspaceName }),
30
+ const response = await TestUtil.InvokeAction({
31
+ actionId: 'workspaces/create',
32
+ headers: authHeaders(user.token),
33
+ payload: { name: workspaceName },
30
34
  })
31
35
 
32
36
  const body = await response.json()
@@ -53,28 +57,24 @@ describe('[/workspaces][POST]', () => {
53
57
 
54
58
  })
55
59
 
56
- describe('[/workspaces][GET]', () => {
60
+ describe('[workspaces/list]', () => {
57
61
 
58
- TestUtil.AssertAuthenticationNeeded({
59
- endpoint: '/workspaces',
60
- method: 'GET',
61
- headers: { 'Content-Type': 'application/json' },
62
+ TestUtil.AssertActionAuthenticationNeeded({
63
+ actionId: 'workspaces/list',
62
64
  })
63
65
 
64
66
  it('must return a minified list of workspaces the user have created', async () => {
65
67
  const user = await TestUtil.GetAuthenticatedTestUser()
66
68
 
67
- const workspace = await TestUtil.MakeRequest({
68
- endpoint: '/workspaces',
69
- method: 'POST',
70
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
71
- body: JSON.stringify({ name: casual.word }),
69
+ const workspace = await TestUtil.InvokeAction({
70
+ actionId: 'workspaces/create',
71
+ headers: authHeaders(user.token),
72
+ payload: { name: casual.word },
72
73
  }).then(response => response.json())
73
74
 
74
- const response = await TestUtil.MakeRequest({
75
- endpoint: '/workspaces',
76
- method: 'GET',
77
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
75
+ const response = await TestUtil.InvokeAction({
76
+ actionId: 'workspaces/list',
77
+ headers: authHeaders(user.token),
78
78
  })
79
79
 
80
80
  const responseBody = await response.json()
@@ -89,28 +89,26 @@ describe('[/workspaces][GET]', () => {
89
89
 
90
90
  })
91
91
 
92
- describe('[/workspaces/:workspaceId][GET]', () => {
92
+ describe('[workspaces/get]', () => {
93
93
 
94
- TestUtil.AssertAuthenticationNeeded({
95
- endpoint: '/workspaces/123',
96
- method: 'GET',
97
- headers: { 'Content-Type': 'application/json' },
94
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
95
+ actionId: 'workspaces/get',
96
+ payload: { workspaceId: '123' },
98
97
  })
99
98
 
100
99
  it('must return the requested workspace', async () => {
101
100
  const user = await TestUtil.GetAuthenticatedTestUser()
102
101
 
103
- const workspace = await TestUtil.MakeRequest({
104
- endpoint: '/workspaces',
105
- method: 'POST',
106
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
107
- body: JSON.stringify({ name: casual.word }),
102
+ const workspace = await TestUtil.InvokeAction({
103
+ actionId: 'workspaces/create',
104
+ headers: authHeaders(user.token),
105
+ payload: { name: casual.word },
108
106
  }).then(response => response.json())
109
107
 
110
- const response = await TestUtil.MakeRequest({
111
- endpoint: `/workspaces/${workspace.id}`,
112
- method: 'GET',
113
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
108
+ const response = await TestUtil.InvokeAction({
109
+ actionId: 'workspaces/get',
110
+ headers: workspaceHeaders(user.token, workspace.id),
111
+ payload: { workspaceId: workspace.id },
114
112
  })
115
113
 
116
114
  const responseBody = await response.json()
@@ -122,22 +120,21 @@ describe('[/workspaces/:workspaceId][GET]', () => {
122
120
 
123
121
  })
124
122
 
125
- describe('[/resource-templates][POST]', () => {
123
+ describe('[workspaces/import-resource-templates]', () => {
126
124
 
127
- TestUtil.AssertAuthenticationNeeded({
128
- endpoint: '/resource-templates',
129
- method: 'POST',
130
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
125
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
126
+ actionId: 'workspaces/import-resource-templates',
127
+ payload: { templates: [] },
128
+ headers: { workspaceId: 'ws-test' },
131
129
  })
132
130
 
133
131
  it('must return the saved templates and create a ResourceTemplatesImported event', async () => {
134
132
  const user = await TestUtil.GetAuthenticatedTestUser()
135
133
 
136
- const workspace = await TestUtil.MakeRequest({
137
- endpoint: '/workspaces',
138
- method: 'POST',
139
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
140
- body: JSON.stringify({ name: casual.word }),
134
+ const workspace = await TestUtil.InvokeAction({
135
+ actionId: 'workspaces/create',
136
+ headers: authHeaders(user.token),
137
+ payload: { name: casual.word },
141
138
  }).then(response => response.json())
142
139
 
143
140
  const imported = [
@@ -148,11 +145,10 @@ describe('[/resource-templates][POST]', () => {
148
145
  },
149
146
  ]
150
147
 
151
- const response = await TestUtil.MakeRequest({
152
- endpoint: '/resource-templates',
153
- method: 'POST',
148
+ const response = await TestUtil.InvokeAction({
149
+ actionId: 'workspaces/import-resource-templates',
154
150
  headers: workspaceHeaders(user.token, workspace.id),
155
- body: JSON.stringify(imported)
151
+ payload: { templates: imported },
156
152
  })
157
153
 
158
154
  const responseBody = await response.json()
@@ -172,22 +168,20 @@ describe('[/resource-templates][POST]', () => {
172
168
 
173
169
  })
174
170
 
175
- describe('[/resource-templates][GET]', () => {
171
+ describe('[workspaces/get-resource-templates]', () => {
176
172
 
177
- TestUtil.AssertAuthenticationNeeded({
178
- endpoint: '/resource-templates',
179
- method: 'GET',
180
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
173
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
174
+ actionId: 'workspaces/get-resource-templates',
175
+ headers: { workspaceId: 'ws-test' },
181
176
  })
182
177
 
183
178
  it('must return the saved templates event', async () => {
184
179
  const user = await TestUtil.GetAuthenticatedTestUser()
185
180
 
186
- const workspace = await TestUtil.MakeRequest({
187
- endpoint: '/workspaces',
188
- method: 'POST',
189
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
190
- body: JSON.stringify({ name: casual.word }),
181
+ const workspace = await TestUtil.InvokeAction({
182
+ actionId: 'workspaces/create',
183
+ headers: authHeaders(user.token),
184
+ payload: { name: casual.word },
191
185
  }).then(response => response.json())
192
186
 
193
187
  const imported = [
@@ -198,11 +192,10 @@ describe('[/resource-templates][GET]', () => {
198
192
  },
199
193
  ]
200
194
 
201
- const response = await TestUtil.MakeRequest({
202
- endpoint: '/resource-templates',
203
- method: 'POST',
195
+ const response = await TestUtil.InvokeAction({
196
+ actionId: 'workspaces/import-resource-templates',
204
197
  headers: workspaceHeaders(user.token, workspace.id),
205
- body: JSON.stringify(imported)
198
+ payload: { templates: imported },
206
199
  })
207
200
 
208
201
  const responseBody = await response.json()
@@ -214,29 +207,27 @@ describe('[/resource-templates][GET]', () => {
214
207
 
215
208
  })
216
209
 
217
- describe('[/tokens][POST]', () => {
210
+ describe('[workspaces/create-api-token]', () => {
218
211
 
219
- TestUtil.AssertAuthenticationNeeded({
220
- endpoint: '/tokens',
221
- method: 'POST',
222
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
212
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
213
+ actionId: 'workspaces/create-api-token',
214
+ payload: { description: 'Api token for GitHub Actions' },
215
+ headers: { workspaceId: 'ws-test' },
223
216
  })
224
217
 
225
218
  it('must return an api token and create an ApiTokenCreated event', async () => {
226
219
  const user = await TestUtil.GetAuthenticatedTestUser()
227
220
 
228
- const workspace = await TestUtil.MakeRequest({
229
- endpoint: '/workspaces',
230
- method: 'POST',
231
- headers: { 'Content-Type': 'application/json', 'Authorization': user.token },
232
- body: JSON.stringify({ name: casual.word }),
221
+ const workspace = await TestUtil.InvokeAction({
222
+ actionId: 'workspaces/create',
223
+ headers: authHeaders(user.token),
224
+ payload: { name: casual.word },
233
225
  }).then(response => response.json())
234
226
 
235
- const response = await TestUtil.MakeRequest({
236
- endpoint: '/tokens',
237
- method: 'POST',
227
+ const response = await TestUtil.InvokeAction({
228
+ actionId: 'workspaces/create-api-token',
238
229
  headers: workspaceHeaders(user.token, workspace.id),
239
- body: JSON.stringify({ description: 'Api token for GitHub Actions' })
230
+ payload: { description: 'Api token for GitHub Actions' },
240
231
  })
241
232
 
242
233
  const responseBody = await response.json()