@ossy/workspaces 1.17.0 → 1.17.2

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 (52) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -4
  3. package/src/AuthenticationGuard.jsx +17 -5
  4. package/src/CreateWorkspacePage.jsx +11 -7
  5. package/src/GeneralSettings.jsx +6 -3
  6. package/src/Invitations.jsx +4 -2
  7. package/src/Invite.jsx +6 -4
  8. package/src/SelectWorkspacePage.jsx +5 -3
  9. package/src/Users.jsx +4 -2
  10. package/src/accept-invitation.action.js +1 -85
  11. package/src/accept-invitation.task.js +84 -0
  12. package/src/create-api-token.action.js +1 -17
  13. package/src/create-api-token.task.js +16 -0
  14. package/src/create.action.js +1 -30
  15. package/src/create.task.js +29 -0
  16. package/src/disable-service.action.js +1 -28
  17. package/src/disable-service.task.js +27 -0
  18. package/src/en.translations.json +6 -6
  19. package/src/enable-service.action.js +1 -28
  20. package/src/enable-service.task.js +27 -0
  21. package/src/get-api-tokens.action.js +1 -11
  22. package/src/get-api-tokens.task.js +10 -0
  23. package/src/get-invitations.action.js +1 -11
  24. package/src/get-invitations.task.js +10 -0
  25. package/src/get-resource-templates.action.js +1 -19
  26. package/src/get-resource-templates.task.js +18 -0
  27. package/src/get-users.action.js +1 -14
  28. package/src/get-users.task.js +13 -0
  29. package/src/get.action.js +1 -14
  30. package/src/get.task.js +13 -0
  31. package/src/import-resource-templates.action.js +1 -27
  32. package/src/import-resource-templates.task.js +26 -0
  33. package/src/index.js +14 -2
  34. package/src/invite-user.action.js +1 -85
  35. package/src/invite-user.task.js +84 -0
  36. package/src/list.action.js +1 -9
  37. package/src/list.task.js +8 -0
  38. package/src/remove-member.action.js +1 -17
  39. package/src/remove-member.task.js +16 -0
  40. package/src/server.js +3 -0
  41. package/src/sv.translations.json +6 -6
  42. package/src/sync-workspace-membership.task.js +4 -3
  43. package/src/workspaces.spec.js +71 -80
  44. package/src/current.api.js +0 -30
  45. package/src/invitations.api.js +0 -46
  46. package/src/item.api.js +0 -32
  47. package/src/list.api.js +0 -35
  48. package/src/members.api.js +0 -42
  49. package/src/resource-templates.api.js +0 -35
  50. package/src/services-disable.api.js +0 -23
  51. package/src/services-enable.api.js +0 -23
  52. package/src/tokens.api.js +0 -35
@@ -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()
@@ -1,30 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.current',
5
- path: '/api/v0/workspaces/current',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method !== 'GET') {
10
- res.setHeader('Allow', 'GET')
11
- res.status(405).json({ error: 'Method Not Allowed' })
12
- return
13
- }
14
-
15
- const workspaceId = req.workspaceId || req.user?.workspaces?.[0]
16
- if (!workspaceId) {
17
- res.status(404).json(null)
18
- return
19
- }
20
-
21
- try {
22
- const workspace = await ActionService.invoke('workspaces/get', {
23
- payload: { workspaceId },
24
- req: { ...req, workspaceId },
25
- })
26
- res.json(workspace)
27
- } catch (err) {
28
- res.status(err?.status ?? 500).json({ error: err?.message })
29
- }
30
- }
@@ -1,46 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'invitations.root',
5
- path: '/api/v0/invitations',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method === 'POST') {
10
- try {
11
- await ActionService.invoke('workspaces/invite-user', {
12
- payload: {
13
- email: req.body,
14
- workspaceId: req.workspaceId,
15
- userId: req.userId,
16
- inviterEmail: req.user?.email,
17
- },
18
- integrations: req.integrations,
19
- req,
20
- })
21
- res.json('')
22
- } catch (err) {
23
- res.status(err?.status ?? 400).json(err?.message ?? 'Could not invite user')
24
- }
25
- return
26
- }
27
- if (req.method === 'GET') {
28
- try {
29
- const result = await ActionService.invoke('workspaces/accept-invitation', {
30
- payload: { workspaceId: req.workspaceId, token: req.query?.token },
31
- req,
32
- })
33
- res.cookie('auth', result.authToken, {
34
- httpOnly: true,
35
- signed: true,
36
- expires: new Date(result.authExpiresAt),
37
- })
38
- res.json('')
39
- } catch (err) {
40
- res.status(err?.status ?? 401).json('')
41
- }
42
- return
43
- }
44
- res.setHeader('Allow', 'GET, POST')
45
- res.status(405).json({ error: 'Method Not Allowed' })
46
- }
package/src/item.api.js DELETED
@@ -1,32 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.item',
5
- path: '/api/v0/workspaces/:workspaceId',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method !== 'GET') {
10
- res.setHeader('Allow', 'GET')
11
- res.status(405).json({ error: 'Method Not Allowed' })
12
- return
13
- }
14
-
15
- const pathname = (req.path || new URL(req.originalUrl, 'http://localhost').pathname).replace(/\/+$/, '')
16
- const m = pathname.match(/^\/api\/v0\/workspaces\/([^/]+)$/)
17
- if (!m || m[1] === 'current') {
18
- res.status(404).json('')
19
- return
20
- }
21
-
22
- const workspaceId = decodeURIComponent(m[1])
23
- try {
24
- const workspace = await ActionService.invoke('workspaces/get', {
25
- payload: { workspaceId },
26
- req: { ...req, workspaceId },
27
- })
28
- res.json(workspace)
29
- } catch (err) {
30
- res.status(err?.status ?? 500).json({ error: err?.message })
31
- }
32
- }
package/src/list.api.js DELETED
@@ -1,35 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.list',
5
- path: '/api/v0/workspaces',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method === 'POST') {
10
- try {
11
- const workspace = await ActionService.invoke('workspaces/create', {
12
- payload: { name: req.body?.name, userId: req.userId },
13
- req,
14
- })
15
- res.json(workspace)
16
- } catch (err) {
17
- res.status(err?.status ?? 400).json({ error: err?.message })
18
- }
19
- return
20
- }
21
- if (req.method === 'GET') {
22
- try {
23
- const workspaces = await ActionService.invoke('workspaces/list', {
24
- payload: { userId: req.userId },
25
- req,
26
- })
27
- res.json(workspaces)
28
- } catch (err) {
29
- res.status(err?.status ?? 400).json({ error: err?.message })
30
- }
31
- return
32
- }
33
- res.setHeader('Allow', 'GET, POST')
34
- res.status(405).json({ error: 'Method Not Allowed' })
35
- }
@@ -1,42 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.members',
5
- path: '/api/v0/workspaces/:workspaceId/members/:userId',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method !== 'DELETE') {
10
- res.setHeader('Allow', 'DELETE')
11
- res.status(405).json({ error: 'Method Not Allowed' })
12
- return
13
- }
14
-
15
- const pathname = (req.path || new URL(req.originalUrl, 'http://localhost').pathname).replace(/\/+$/, '')
16
- const m = pathname.match(/^\/api\/v0\/workspaces\/([^/]+)\/members\/([^/]+)$/)
17
- if (!m) {
18
- res.status(404).json('')
19
- return
20
- }
21
-
22
- const workspaceId = decodeURIComponent(m[1])
23
- const memberId = decodeURIComponent(m[2])
24
-
25
- const isMember = Array.isArray(req.user?.workspaces) && req.user.workspaces.includes(workspaceId)
26
- const isSelf = req.user?.id === memberId
27
-
28
- if (!isMember && !isSelf) {
29
- res.status(403).json({ error: 'Forbidden' })
30
- return
31
- }
32
-
33
- try {
34
- await ActionService.invoke('workspaces/remove-member', {
35
- payload: { workspaceId, memberId, userId: req.userId },
36
- req,
37
- })
38
- res.json()
39
- } catch (err) {
40
- res.status(err?.status ?? 500).json({ error: err?.message })
41
- }
42
- }
@@ -1,35 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'resourceTemplates.root',
5
- path: '/api/v0/resource-templates',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method === 'POST') {
10
- try {
11
- const templates = await ActionService.invoke('workspaces/import-resource-templates', {
12
- payload: { templates: req.body, workspaceId: req.workspaceId, userId: req.userId },
13
- req,
14
- })
15
- res.json(templates)
16
- } catch (err) {
17
- res.status(err?.status ?? 500).json({ type: err?.type, message: err?.message })
18
- }
19
- return
20
- }
21
- if (req.method === 'GET') {
22
- try {
23
- const templates = await ActionService.invoke('workspaces/get-resource-templates', {
24
- payload: { workspaceId: req.workspaceId },
25
- req,
26
- })
27
- res.json(templates)
28
- } catch (err) {
29
- res.status(err?.status ?? 500).json()
30
- }
31
- return
32
- }
33
- res.setHeader('Allow', 'GET, POST')
34
- res.status(405).json({ error: 'Method Not Allowed' })
35
- }
@@ -1,23 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.services.disable',
5
- path: '/api/v0/services/disable',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method !== 'POST') {
10
- res.setHeader('Allow', 'POST')
11
- res.status(405).json({ error: 'Method Not Allowed' })
12
- return
13
- }
14
- try {
15
- await ActionService.invoke('workspaces/disable-service', {
16
- payload: { service: req.body?.service, workspaceId: req.workspaceId, userId: req.userId },
17
- req,
18
- })
19
- res.json()
20
- } catch (err) {
21
- res.status(err?.status ?? 400).json({ error: err?.message })
22
- }
23
- }
@@ -1,23 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.services.enable',
5
- path: '/api/v0/services/enable',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method !== 'POST') {
10
- res.setHeader('Allow', 'POST')
11
- res.status(405).json({ error: 'Method Not Allowed' })
12
- return
13
- }
14
- try {
15
- await ActionService.invoke('workspaces/enable-service', {
16
- payload: { service: req.body, workspaceId: req.workspaceId, userId: req.userId },
17
- req,
18
- })
19
- res.json()
20
- } catch (err) {
21
- res.status(err?.status ?? 400).json({ error: err?.message })
22
- }
23
- }
package/src/tokens.api.js DELETED
@@ -1,35 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
-
3
- export const metadata = {
4
- id: 'workspaces.tokens',
5
- path: '/api/v0/tokens',
6
- }
7
-
8
- export default async function handle(req, res) {
9
- if (req.method === 'GET') {
10
- try {
11
- const tokens = await ActionService.invoke('workspaces/get-api-tokens', {
12
- payload: { workspaceId: req.workspaceId },
13
- req,
14
- })
15
- res.json(tokens)
16
- } catch (err) {
17
- res.status(err?.status ?? 500).json({ error: err?.message })
18
- }
19
- return
20
- }
21
- if (req.method === 'POST') {
22
- try {
23
- const token = await ActionService.invoke('workspaces/create-api-token', {
24
- payload: { workspaceId: req.workspaceId, userId: req.userId, description: req.body?.description },
25
- req,
26
- })
27
- res.json(token)
28
- } catch (err) {
29
- res.status(err?.status ?? 500).json({ error: err?.message })
30
- }
31
- return
32
- }
33
- res.setHeader('Allow', 'GET, POST')
34
- res.status(405).json({ error: 'Method Not Allowed' })
35
- }