@ossy/resources 1.12.0 → 1.12.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 (67) hide show
  1. package/package.json +10 -4
  2. package/src/AudioResource.jsx +4 -2
  3. package/src/CreateDirectory.jsx +4 -3
  4. package/src/CreateDocument.jsx +4 -3
  5. package/src/DocumentEdit.jsx +9 -6
  6. package/src/DocumentView.jsx +8 -4
  7. package/src/GenericResourceCard.jsx +4 -2
  8. package/src/GenericResourceDetail.jsx +9 -4
  9. package/src/GenericResourceForm.jsx +5 -2
  10. package/src/ImageResource.jsx +4 -2
  11. package/src/PDFResource.jsx +4 -2
  12. package/src/ResourceContentPage.jsx +4 -2
  13. package/src/ResourceDescription.jsx +4 -3
  14. package/src/ResourceDetails.jsx +9 -4
  15. package/src/ResourceDialogMove.jsx +4 -3
  16. package/src/ResourceFactory.jsx +6 -3
  17. package/src/ResourceGenericView.jsx +4 -2
  18. package/src/ResourceList.jsx +4 -2
  19. package/src/ResourcePanel.jsx +9 -12
  20. package/src/ResourceTags.jsx +4 -3
  21. package/src/ResourcesPage.jsx +4 -2
  22. package/src/UploadResources.jsx +4 -3
  23. package/src/VideoResource.jsx +4 -2
  24. package/src/create-page-view.action.js +1 -38
  25. package/src/create-page-view.task.js +38 -0
  26. package/src/create.action.js +1 -98
  27. package/src/create.task.js +100 -0
  28. package/src/delete.action.js +1 -31
  29. package/src/delete.task.js +31 -0
  30. package/src/en.translations.json +5 -5
  31. package/src/get-page-view-stats.action.js +1 -65
  32. package/src/get-page-view-stats.task.js +64 -0
  33. package/src/get.action.js +1 -14
  34. package/src/get.task.js +14 -0
  35. package/src/index.js +13 -4
  36. package/src/list.action.js +1 -19
  37. package/src/list.task.js +20 -0
  38. package/src/resource-create.page.jsx +4 -3
  39. package/src/resource.helpers.js +83 -0
  40. package/src/resources.action-media.js +18 -0
  41. package/src/resources.attach-media-urls.js +1 -1
  42. package/src/resources.events.js +1 -0
  43. package/src/resources.spec.js +54 -55
  44. package/src/search.action.js +1 -19
  45. package/src/search.task.js +20 -0
  46. package/src/server.js +4 -0
  47. package/src/sv.translations.json +5 -5
  48. package/src/update-access.action.js +1 -20
  49. package/src/update-access.task.js +22 -0
  50. package/src/update-content.action.js +1 -39
  51. package/src/update-content.task.js +41 -0
  52. package/src/update-location.action.js +1 -18
  53. package/src/update-location.task.js +20 -0
  54. package/src/update-name.action.js +1 -18
  55. package/src/update-name.task.js +20 -0
  56. package/src/upload-named-version.action.js +1 -42
  57. package/src/upload-named-version.task.js +46 -0
  58. package/src/item-access.api.js +0 -34
  59. package/src/item-content.api.js +0 -35
  60. package/src/item-location.api.js +0 -34
  61. package/src/item-name.api.js +0 -34
  62. package/src/item-version.api.js +0 -48
  63. package/src/item.api.js +0 -58
  64. package/src/list.api.js +0 -43
  65. package/src/page-views-stats.api.js +0 -23
  66. package/src/page-views.api.js +0 -23
  67. package/src/search.api.js +0 -27
@@ -9,72 +9,70 @@ function workspaceHeaders(userToken, workspaceId) {
9
9
  }
10
10
  }
11
11
 
12
- describe('[/resources][POST]', () => {
12
+ describe('[resources/create]', () => {
13
13
 
14
- TestUtil.AssertAuthenticationNeeded({
15
- endpoint: '/resources',
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
14
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
15
+ actionId: 'resources/create',
16
+ payload: { type: 'directory', location: '/', name: 'test' },
17
+ headers: { workspaceId: 'ws-test' },
18
18
  })
19
19
 
20
20
  })
21
21
 
22
- describe('[/resources][GET]', () => {
23
- TestUtil.AssertAuthenticationNeeded({
24
- endpoint: '/resources',
25
- method: 'GET',
26
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
22
+ describe('[resources/list]', () => {
23
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
24
+ actionId: 'resources/list',
25
+ headers: { workspaceId: 'ws-test' },
27
26
  })
28
27
  })
29
28
 
30
- describe('[/resources/:resourceId][GET]', () => {
31
- TestUtil.AssertAuthenticationNeeded({
32
- endpoint: '/resources/r1',
33
- method: 'GET',
34
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
29
+ describe('[resources/get]', () => {
30
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
31
+ actionId: 'resources/get',
32
+ payload: { resourceId: 'r1' },
33
+ headers: { workspaceId: 'ws-test' },
35
34
  })
36
35
  })
37
36
 
38
- describe('[/resources/:resourceId/name][PUT]', () => {
39
- TestUtil.AssertAuthenticationNeeded({
40
- endpoint: '/resources/r1/name',
41
- method: 'PUT',
42
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
37
+ describe('[resources/update-name]', () => {
38
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
39
+ actionId: 'resources/update-name',
40
+ payload: { resourceId: 'r1', name: 'x' },
41
+ headers: { workspaceId: 'ws-test' },
43
42
  })
44
43
  })
45
44
 
46
- describe('[/resources/:resourceId/location][PUT]', () => {
47
- TestUtil.AssertAuthenticationNeeded({
48
- endpoint: '/resources/r1/location',
49
- method: 'PUT',
50
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
45
+ describe('[resources/update-location]', () => {
46
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
47
+ actionId: 'resources/update-location',
48
+ payload: { resourceId: 'r1', target: '/other/' },
49
+ headers: { workspaceId: 'ws-test' },
51
50
  })
52
51
  })
53
52
 
54
- describe('[/resources/:resourceId/content][PUT]', () => {
55
- TestUtil.AssertAuthenticationNeeded({
56
- endpoint: '/resources/r1/content',
57
- method: 'PUT',
58
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
53
+ describe('[resources/update-content]', () => {
54
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
55
+ actionId: 'resources/update-content',
56
+ payload: { resourceId: 'r1', content: {} },
57
+ headers: { workspaceId: 'ws-test' },
59
58
  })
60
59
  })
61
60
 
62
- describe('[/resources/:resourceId][DELETE]', () => {
63
- TestUtil.AssertAuthenticationNeeded({
64
- endpoint: '/resources/r1',
65
- method: 'DELETE',
66
- headers: { 'Content-Type': 'application/json', 'workspaceId': 'ws-test' },
61
+ describe('[resources/delete]', () => {
62
+ TestUtil.AssertWorkspaceActionAuthenticationNeeded({
63
+ actionId: 'resources/delete',
64
+ payload: { resourceId: 'r1' },
65
+ headers: { workspaceId: 'ws-test' },
67
66
  })
68
67
  })
69
68
 
70
69
  describe('E2E: template-backed resource (workspaceId header)', () => {
71
70
  it('create → update content → delete', async () => {
72
71
  const user = await TestUtil.GetAuthenticatedTestUser()
73
- const workspace = await TestUtil.MakeRequest({
74
- endpoint: '/workspaces',
75
- method: 'POST',
72
+ const workspace = await TestUtil.InvokeAction({
73
+ actionId: 'workspaces/create',
76
74
  headers: { 'Content-Type': 'application/json', Authorization: user.token },
77
- body: JSON.stringify({ name: casual.word }),
75
+ payload: { name: casual.word },
78
76
  }).then(r => r.json())
79
77
 
80
78
  const templateType = '@ossy/web/page'
@@ -89,11 +87,10 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
89
87
  },
90
88
  }
91
89
 
92
- const createRes = await TestUtil.MakeRequest({
93
- endpoint: '/resources',
94
- method: 'POST',
90
+ const createRes = await TestUtil.InvokeAction({
91
+ actionId: 'resources/create',
95
92
  headers: workspaceHeaders(user.token, workspace.id),
96
- body: JSON.stringify(createBody),
93
+ payload: createBody,
97
94
  })
98
95
 
99
96
  expect(createRes.status).toEqual(200)
@@ -105,17 +102,17 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
105
102
  body: '<p>initial</p>',
106
103
  })
107
104
 
108
- const updateRes = await TestUtil.MakeRequest({
109
- endpoint: `/resources/${created.id}/content`,
110
- method: 'PUT',
105
+ const updateRes = await TestUtil.InvokeAction({
106
+ actionId: 'resources/update-content',
111
107
  headers: workspaceHeaders(user.token, workspace.id),
112
- body: JSON.stringify({
108
+ payload: {
109
+ resourceId: created.id,
113
110
  content: {
114
111
  title: 'Updated',
115
112
  description: 'D2',
116
113
  body: '<p>revised</p>',
117
114
  },
118
- }),
115
+ },
119
116
  })
120
117
 
121
118
  expect(updateRes.status).toEqual(200)
@@ -126,18 +123,20 @@ describe('E2E: template-backed resource (workspaceId header)', () => {
126
123
  body: '<p>revised</p>',
127
124
  })
128
125
 
129
- const deleteRes = await TestUtil.MakeRequest({
130
- endpoint: `/resources/${created.id}`,
131
- method: 'DELETE',
126
+ const deleteRes = await TestUtil.InvokeAction({
127
+ actionId: 'resources/delete',
132
128
  headers: workspaceHeaders(user.token, workspace.id),
129
+ payload: { resourceId: created.id },
133
130
  })
134
131
 
135
- expect(deleteRes.status).toEqual(204)
132
+ expect(deleteRes.status).toEqual(200)
133
+ const deleteBody = await deleteRes.json()
134
+ expect(deleteBody).toEqual({ ok: true })
136
135
 
137
- const listRes = await TestUtil.MakeRequest({
138
- endpoint: '/resources?location=/',
139
- method: 'GET',
136
+ const listRes = await TestUtil.InvokeAction({
137
+ actionId: 'resources/list',
140
138
  headers: workspaceHeaders(user.token, workspace.id),
139
+ payload: { query: { location: '/' } },
141
140
  })
142
141
 
143
142
  expect(listRes.status).toEqual(200)
@@ -1,19 +1 @@
1
- import { ResourcesQueries } from './resources.queries.js'
2
-
3
- export const id = 'resources/search'
4
- export const access = 'workspace'
5
-
6
- export async function run({ payload, req }) {
7
- const query = { ...(payload ?? {}) }
8
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
- const user = payload?.user ?? req?.user
10
- let location = query.location
11
-
12
- if (location && !location.endsWith('/')) location += '/'
13
- if (location && !location.startsWith('/')) location = '/' + location
14
- if (location) query.location = location
15
-
16
- if (workspaceId) query.belongsTo = workspaceId
17
-
18
- return ResourcesQueries.GetResources(query, user)
19
- }
1
+ export const metadata = { id: 'resources/search', access: 'workspace' }
@@ -0,0 +1,20 @@
1
+ import { ResourcesQueries } from './resources.queries.js'
2
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
3
+
4
+ export const metadata = { id: 'resources/search' }
5
+
6
+ export async function run({ payload, req }) {
7
+ const query = { ...(payload ?? {}) }
8
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
9
+ const user = payload?.user ?? req?.user
10
+ let location = query.location
11
+
12
+ if (location && !location.endsWith('/')) location += '/'
13
+ if (location && !location.startsWith('/')) location = '/' + location
14
+ if (location) query.location = location
15
+
16
+ if (workspaceId) query.belongsTo = workspaceId
17
+
18
+ const resources = await ResourcesQueries.GetResources(query, user)
19
+ return withResourceMedia(resources, mediaContextFromRun({ payload, req }))
20
+ }
package/src/server.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './resource.aggregate.js'
2
+ export * from './resources.events.js'
3
+ export * from './resources.queries.js'
4
+ export { attachResourceMediaUrls } from './resources.attach-media-urls.js'
@@ -1,5 +1,5 @@
1
1
  {
2
- "resources.home.documentTitle": "Gratis resurser",
2
+ "resources/home.documentTitle": "Gratis resurser",
3
3
  "resources.home.title": "Gratis resurser",
4
4
  "resources.home.description": "Vi tror på att leverera värde i varje steg av din resa med oss. Därför har vi samlat gratis resurser. Oavsett om du vill förbättra dina kreativa projekt eller bara söker inspiration kan du utforska galleriet nedan. Vi hoppas att du hittar något användbart — hör av dig om du har frågor!",
5
5
  "resources.form.nameRequired": "Namn krävs",
@@ -31,10 +31,10 @@
31
31
  "resources/create-page-view.description": "Registrera en sidvisningshändelse för analys",
32
32
  "resources/get-page-view-stats.label": "Hämta sidvisningsstatistik",
33
33
  "resources/get-page-view-stats.description": "Hämta aggregerad statistik för sidvisningar",
34
- "resources.create.generic.documentTitle": "Skapa resurs",
35
- "createDirectory.documentTitle": "Skapa mapp",
36
- "createDocument.documentTitle": "Skapa dokument",
37
- "uploadMedia.documentTitle": "Ladda upp",
34
+ "resources/create/generic.documentTitle": "Skapa resurs",
35
+ "create-directory.documentTitle": "Skapa mapp",
36
+ "create-document.documentTitle": "Skapa dokument",
37
+ "upload-media.documentTitle": "Ladda upp",
38
38
  "@resource.documentTitle": "Resurs",
39
39
  "resources.upload.title": "Ladda upp",
40
40
  "resources.upload.location": "Plats: {location}",
@@ -1,20 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
-
4
- export const id = 'resources/update-access'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
10
- const resourceAccess = payload?.access
11
-
12
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
13
- if (!['public', 'restricted'].includes(resourceAccess)) {
14
- throw Object.assign(new Error(`Invalid access value: ${resourceAccess}`), { status: 400 })
15
- }
16
-
17
- return Aggregate.Of(Resource, resourceId)
18
- .then(Aggregate.Add(ResourcesEvents.AccessUpdated({ createdBy, access: resourceAccess })))
19
- .then(Aggregate.View())
20
- }
1
+ export const metadata = { id: 'resources/update-access', access: 'workspace' }
@@ -0,0 +1,22 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
5
+
6
+ export const metadata = { id: 'resources/update-access' }
7
+
8
+ export async function run({ payload, req }) {
9
+ const createdBy = payload?.userId ?? req?.userId
10
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
11
+ const resourceAccess = payload?.access
12
+
13
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
14
+ if (!['public', 'restricted'].includes(resourceAccess)) {
15
+ throw Object.assign(new Error(`Invalid access value: ${resourceAccess}`), { status: 400 })
16
+ }
17
+
18
+ const resource = await Aggregate.Of(Resource, resourceId)
19
+ .then(Aggregate.Add(ResourcesEvents.AccessUpdated({ createdBy, access: resourceAccess })))
20
+ .then(Aggregate.View())
21
+ return withResourceMedia(resource, mediaContextFromRun({ payload, req }))
22
+ }
@@ -1,39 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
- import { Workspace } from '@ossy/workspaces'
4
- import { getSystemResourceTemplates, normalizeAndValidateDocumentContent } from '@ossy/platform'
5
-
6
- export const id = 'resources/update-content'
7
- export const access = 'workspace'
8
-
9
- export async function run({ payload, req }) {
10
- const createdBy = payload?.userId ?? req?.userId
11
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
12
- const content = payload?.content
13
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
14
-
15
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
16
-
17
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
18
-
19
- return Aggregate.Of(Resource, resourceId)
20
- .then(aggregate => {
21
- const resource = Aggregate.View()(aggregate)
22
- const systemTemplates = getSystemResourceTemplates()
23
- const allTemplates = [
24
- ...systemTemplates,
25
- ...(workspace.resourceTemplates || []).filter(t => !systemTemplates.find(s => s.id === t.id)),
26
- ]
27
- const template = allTemplates.find(t => t.id === resource.type)
28
- let finalContent = content
29
- if (template) {
30
- const normalized = normalizeAndValidateDocumentContent(content, template)
31
- if (!normalized.ok) {
32
- throw Object.assign(new Error(normalized.message), { status: 400, type: normalized.code })
33
- }
34
- finalContent = normalized.content
35
- }
36
- return Aggregate.Add(ResourcesEvents.ContentUpdated({ createdBy, content: finalContent }))(aggregate)
37
- })
38
- .then(Aggregate.View())
39
- }
1
+ export const metadata = { id: 'resources/update-content', access: 'workspace' }
@@ -0,0 +1,41 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { Workspace } from '@ossy/workspaces/server'
5
+ import { getSystemResourceTemplates, normalizeAndValidateDocumentContent } from '@ossy/platform'
6
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
7
+
8
+ export const metadata = { id: 'resources/update-content' }
9
+
10
+ export async function run({ payload, req }) {
11
+ const createdBy = payload?.userId ?? req?.userId
12
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
13
+ const content = payload?.content
14
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
15
+
16
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
17
+
18
+ const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
19
+
20
+ return Aggregate.Of(Resource, resourceId)
21
+ .then(aggregate => {
22
+ const resource = Aggregate.View()(aggregate)
23
+ const systemTemplates = getSystemResourceTemplates()
24
+ const allTemplates = [
25
+ ...systemTemplates,
26
+ ...(workspace.resourceTemplates || []).filter(t => !systemTemplates.find(s => s.id === t.id)),
27
+ ]
28
+ const template = allTemplates.find(t => t.id === resource.type)
29
+ let finalContent = content
30
+ if (template) {
31
+ const normalized = normalizeAndValidateDocumentContent(content, template)
32
+ if (!normalized.ok) {
33
+ throw Object.assign(new Error(normalized.message), { status: 400, type: normalized.code })
34
+ }
35
+ finalContent = normalized.content
36
+ }
37
+ return Aggregate.Add(ResourcesEvents.ContentUpdated({ createdBy, content: finalContent }))(aggregate)
38
+ })
39
+ .then(Aggregate.View())
40
+ .then(resource => withResourceMedia(resource, mediaContextFromRun({ payload, req })))
41
+ }
@@ -1,18 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
-
4
- export const id = 'resources/update-location'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
10
- const location = payload?.target ?? payload?.location
11
-
12
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
13
- if (!location) throw Object.assign(new Error('target location is required'), { status: 400 })
14
-
15
- return Aggregate.Of(Resource, resourceId)
16
- .then(Aggregate.Add(ResourcesEvents.LocationUpdated({ createdBy, location })))
17
- .then(Aggregate.View())
18
- }
1
+ export const metadata = { id: 'resources/update-location', access: 'workspace' }
@@ -0,0 +1,20 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
5
+
6
+ export const metadata = { id: 'resources/update-location' }
7
+
8
+ export async function run({ payload, req }) {
9
+ const createdBy = payload?.userId ?? req?.userId
10
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
11
+ const location = payload?.target ?? payload?.location
12
+
13
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
14
+ if (!location) throw Object.assign(new Error('target location is required'), { status: 400 })
15
+
16
+ const resource = await Aggregate.Of(Resource, resourceId)
17
+ .then(Aggregate.Add(ResourcesEvents.LocationUpdated({ createdBy, location })))
18
+ .then(Aggregate.View())
19
+ return withResourceMedia(resource, mediaContextFromRun({ payload, req }))
20
+ }
@@ -1,18 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
-
4
- export const id = 'resources/update-name'
5
- export const access = 'workspace'
6
-
7
- export async function run({ payload, req }) {
8
- const createdBy = payload?.userId ?? req?.userId
9
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
10
- const name = payload?.name
11
-
12
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
13
- if (!name) throw Object.assign(new Error('name is required'), { status: 400 })
14
-
15
- return Aggregate.Of(Resource, resourceId)
16
- .then(Aggregate.Add(ResourcesEvents.NameUpdated({ createdBy, name })))
17
- .then(Aggregate.View())
18
- }
1
+ export const metadata = { id: 'resources/update-name', access: 'workspace' }
@@ -0,0 +1,20 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
5
+
6
+ export const metadata = { id: 'resources/update-name' }
7
+
8
+ export async function run({ payload, req }) {
9
+ const createdBy = payload?.userId ?? req?.userId
10
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
11
+ const name = payload?.name
12
+
13
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
14
+ if (!name) throw Object.assign(new Error('name is required'), { status: 400 })
15
+
16
+ const resource = await Aggregate.Of(Resource, resourceId)
17
+ .then(Aggregate.Add(ResourcesEvents.NameUpdated({ createdBy, name })))
18
+ .then(Aggregate.View())
19
+ return withResourceMedia(resource, mediaContextFromRun({ payload, req }))
20
+ }
@@ -1,42 +1 @@
1
- import { Aggregate } from '@ossy/event-store'
2
- import { Resource, ResourcesEvents } from '@ossy/resources'
3
- import { Workspace } from '@ossy/workspaces'
4
-
5
- export const id = 'resources/upload-named-version'
6
- export const access = 'workspace'
7
-
8
- export async function run({ payload, integrations, req }) {
9
- const createdBy = payload?.userId ?? req?.userId
10
- const workspaceId = payload?.workspaceId ?? req?.workspaceId
11
- const resourceId = payload?.resourceId ?? req?.params?.resourceId
12
- const namedVersion = payload?.namedVersion ?? req?.params?.namedVersion
13
- const type = payload?.type
14
- const size = payload?.size
15
-
16
- if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
17
- if (!namedVersion) throw Object.assign(new Error('namedVersion is required'), { status: 400 })
18
-
19
- const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
20
-
21
- const event = ResourcesEvents.NamedVersionUploaded({
22
- createdBy,
23
- namedVersion,
24
- Key: `media/${workspace.id}/${resourceId}/${namedVersion}.${type.replace('image/', '')}`,
25
- ContentLength: size,
26
- ContentType: type,
27
- })
28
-
29
- const storageClient = integrations?.get?.('storage')
30
- const uploadHeaders = {
31
- ContentLength: event.payload.ContentLength,
32
- ContentType: event.payload.ContentType,
33
- Key: event.payload.Key,
34
- }
35
- const uploadUrl = storageClient ? await storageClient.createUploadUrl(uploadHeaders) : null
36
- const resource = await Aggregate.Of(Resource, resourceId)
37
- .then(Aggregate.Add(event))
38
- .then(Aggregate.View())
39
- return uploadUrl
40
- ? { ...resource, content: { ...resource.content, uploadUrl } }
41
- : resource
42
- }
1
+ export const metadata = { id: 'resources/upload-named-version', access: 'workspace' }
@@ -0,0 +1,46 @@
1
+ import { Aggregate } from '@ossy/event-store'
2
+ import { Resource } from './resource.aggregate.js'
3
+ import { ResourcesEvents } from './resources.events.js'
4
+ import { Workspace } from '@ossy/workspaces/server'
5
+ import { mediaContextFromRun, withResourceMedia } from './resources.action-media.js'
6
+
7
+ export const metadata = { id: 'resources/upload-named-version' }
8
+
9
+ // TODO: remove named version concept
10
+
11
+ export async function run({ payload, integrations, req }) {
12
+ const createdBy = payload?.userId ?? req?.userId
13
+ const workspaceId = payload?.workspaceId ?? req?.workspaceId
14
+ const resourceId = payload?.resourceId ?? req?.params?.resourceId
15
+ const namedVersion = payload?.namedVersion ?? req?.params?.namedVersion
16
+ const type = payload?.type
17
+ const size = payload?.size
18
+
19
+ if (!resourceId) throw Object.assign(new Error('resourceId is required'), { status: 400 })
20
+ if (!namedVersion) throw Object.assign(new Error('namedVersion is required'), { status: 400 })
21
+
22
+ const workspace = await Aggregate.Of(Workspace, workspaceId).then(Aggregate.View())
23
+
24
+ const event = ResourcesEvents.NamedVersionUploaded({
25
+ createdBy,
26
+ namedVersion,
27
+ Key: `media/${workspace.id}/${resourceId}/${namedVersion}.${type.replace('image/', '')}`,
28
+ ContentLength: size,
29
+ ContentType: type,
30
+ })
31
+
32
+ const storageClient = integrations?.get?.('storage')
33
+ const uploadHeaders = {
34
+ ContentLength: event.payload.ContentLength,
35
+ ContentType: event.payload.ContentType,
36
+ Key: event.payload.Key,
37
+ }
38
+ const uploadUrl = storageClient ? await storageClient.createUploadUrl(uploadHeaders) : null
39
+ const resource = await Aggregate.Of(Resource, resourceId)
40
+ .then(Aggregate.Add(event))
41
+ .then(Aggregate.View())
42
+ const withUpload = uploadUrl
43
+ ? { ...resource, content: { ...resource.content, uploadUrl } }
44
+ : resource
45
+ return withResourceMedia(withUpload, mediaContextFromRun({ payload, req }))
46
+ }
@@ -1,34 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
- import { attachResourceMediaUrls } from './resources.attach-media-urls.js'
3
-
4
- const pathRe = /^\/api\/v0\/resources\/([^/]+)\/access$/
5
-
6
- export const metadata = {
7
- id: 'resources.item.access',
8
- path: '/api/v0/resources/:resourceId/access',
9
- }
10
-
11
- export default async function handle(req, res) {
12
- if (req.method !== 'PUT') {
13
- res.setHeader('Allow', 'PUT')
14
- res.status(405).json({ error: 'Method Not Allowed' })
15
- return
16
- }
17
- const pathname = (req.path || new URL(req.originalUrl, 'http://localhost').pathname).replace(/\/+$/, '')
18
- const m = pathname.match(pathRe)
19
- if (!m) {
20
- res.status(404).json('')
21
- return
22
- }
23
- const resourceId = decodeURIComponent(m[1])
24
- try {
25
- const resource = await ActionService.invoke('resources/update-access', {
26
- payload: { resourceId, access: req.body?.access, userId: req.userId },
27
- req,
28
- })
29
- const withUrls = await attachResourceMediaUrls(resource, { userId: req.userId, workspaceId: req.workspaceId })
30
- res.json(withUrls)
31
- } catch (err) {
32
- res.status(err?.status ?? 500).json({ error: err?.message })
33
- }
34
- }
@@ -1,35 +0,0 @@
1
- import { ActionService } from '@ossy/platform'
2
- import { attachResourceMediaUrls } from './resources.attach-media-urls.js'
3
-
4
- const pathRe = /^\/api\/v0\/resources\/([^/]+)\/content$/
5
-
6
- export const metadata = {
7
- id: 'resources.item.content',
8
- path: '/api/v0/resources/:resourceId/content',
9
- }
10
-
11
- export default async function handle(req, res) {
12
- if (req.method !== 'PUT') {
13
- res.setHeader('Allow', 'PUT')
14
- res.status(405).json({ error: 'Method Not Allowed' })
15
- return
16
- }
17
- const pathname = (req.path || new URL(req.originalUrl, 'http://localhost').pathname).replace(/\/+$/, '')
18
- const m = pathname.match(pathRe)
19
- if (!m) {
20
- res.status(404).json('')
21
- return
22
- }
23
- const resourceId = decodeURIComponent(m[1])
24
- try {
25
- const resource = await ActionService.invoke('resources/update-content', {
26
- payload: { resourceId, content: req.body?.content, userId: req.userId, workspaceId: req.workspaceId },
27
- req,
28
- })
29
- if (resource === null || resource === undefined) return
30
- const withUrls = await attachResourceMediaUrls(resource, { userId: req.userId, workspaceId: req.workspaceId })
31
- res.json(withUrls)
32
- } catch (err) {
33
- res.status(err?.status ?? 400).json({ type: err?.type, message: err?.message })
34
- }
35
- }