@ossy/resources 1.11.7 → 1.12.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.
- package/package.json +10 -4
- package/src/AudioResource.jsx +4 -2
- package/src/CreateDirectory.jsx +14 -13
- package/src/CreateDocument.jsx +7 -5
- package/src/Definition.js +2 -8
- package/src/DocumentEdit.jsx +9 -6
- package/src/DocumentView.jsx +8 -4
- package/src/GenericResourceCard.jsx +4 -2
- package/src/GenericResourceDetail.jsx +9 -4
- package/src/GenericResourceForm.jsx +14 -7
- package/src/ImageResource.jsx +4 -2
- package/src/PDFResource.jsx +4 -2
- package/src/ResourceContentPage.jsx +4 -2
- package/src/ResourceDescription.jsx +4 -3
- package/src/ResourceDetails.jsx +9 -4
- package/src/ResourceDialogMove.jsx +4 -3
- package/src/ResourceFactory.jsx +6 -3
- package/src/ResourceGenericView.jsx +4 -2
- package/src/ResourceList.jsx +4 -2
- package/src/ResourcePanel.jsx +9 -12
- package/src/ResourceTags.jsx +4 -3
- package/src/ResourcesPage.jsx +8 -6
- package/src/UploadResources.jsx +9 -11
- package/src/VideoResource.jsx +4 -2
- package/src/create-page-view.action.js +1 -38
- package/src/create-page-view.task.js +38 -0
- package/src/create.action.js +1 -98
- package/src/create.task.js +100 -0
- package/src/delete.action.js +1 -31
- package/src/delete.task.js +31 -0
- package/src/en.translations.json +48 -0
- package/src/get-page-view-stats.action.js +1 -65
- package/src/get-page-view-stats.task.js +64 -0
- package/src/get.action.js +1 -14
- package/src/get.task.js +14 -0
- package/src/index.js +13 -4
- package/src/list.action.js +1 -19
- package/src/list.task.js +20 -0
- package/src/resource-create.page.jsx +7 -5
- package/src/resource-detail.page.jsx +0 -2
- package/src/resource.helpers.js +83 -0
- package/src/resources.action-media.js +18 -0
- package/src/resources.attach-media-urls.js +1 -1
- package/src/resources.events.js +1 -0
- package/src/resources.spec.js +54 -55
- package/src/search.action.js +1 -19
- package/src/search.task.js +20 -0
- package/src/server.js +4 -0
- package/src/sv.translations.json +48 -0
- package/src/update-access.action.js +1 -20
- package/src/update-access.task.js +22 -0
- package/src/update-content.action.js +1 -39
- package/src/update-content.task.js +41 -0
- package/src/update-location.action.js +1 -18
- package/src/update-location.task.js +20 -0
- package/src/update-name.action.js +1 -18
- package/src/update-name.task.js +20 -0
- package/src/upload-named-version.action.js +1 -42
- package/src/upload-named-version.task.js +46 -0
- package/src/item-access.api.js +0 -34
- package/src/item-content.api.js +0 -35
- package/src/item-location.api.js +0 -34
- package/src/item-name.api.js +0 -34
- package/src/item-version.api.js +0 -48
- package/src/item.api.js +0 -58
- package/src/list.api.js +0 -43
- package/src/page-views-stats.api.js +0 -23
- package/src/page-views.api.js +0 -23
- package/src/search.api.js +0 -27
package/src/index.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
export { metadata as CreateResource } from './create.action.js'
|
|
2
|
+
export { metadata as GetResource } from './get.action.js'
|
|
3
|
+
export { metadata as ListResources } from './list.action.js'
|
|
4
|
+
export { metadata as SearchResources } from './search.action.js'
|
|
5
|
+
export { metadata as DeleteResource } from './delete.action.js'
|
|
6
|
+
export { metadata as UpdateResourceContent } from './update-content.action.js'
|
|
7
|
+
export { metadata as UpdateResourceLocation } from './update-location.action.js'
|
|
8
|
+
export { metadata as UpdateResourceName } from './update-name.action.js'
|
|
9
|
+
export { metadata as UpdateResourceAccess } from './update-access.action.js'
|
|
10
|
+
export { metadata as UploadNamedVersion } from './upload-named-version.action.js'
|
|
11
|
+
export { metadata as CreatePageView } from './create-page-view.action.js'
|
|
12
|
+
export { metadata as GetPageViewStats } from './get-page-view-stats.action.js'
|
|
5
13
|
export * from './AudioResource.jsx'
|
|
6
14
|
export * from './CreateDirectory.jsx'
|
|
7
15
|
export * from './CreateDocument.jsx'
|
|
@@ -29,3 +37,4 @@ export * from './UploadResources.jsx'
|
|
|
29
37
|
export * from './VideoResource.jsx'
|
|
30
38
|
export * from './useActivePath.jsx'
|
|
31
39
|
export * from './useForm.js'
|
|
40
|
+
export * from './resource.helpers.js'
|
package/src/list.action.js
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const id = 'resources/list'
|
|
4
|
-
export const access = 'workspace'
|
|
5
|
-
|
|
6
|
-
export async function run({ payload, req }) {
|
|
7
|
-
const query = { ...(payload?.query ?? {}) }
|
|
8
|
-
const workspaceId = payload?.workspaceId ?? req?.workspaceId
|
|
9
|
-
const user = payload?.user ?? req?.user
|
|
10
|
-
let location = payload?.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/list', access: 'workspace' }
|
package/src/list.task.js
ADDED
|
@@ -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/list' }
|
|
5
|
+
|
|
6
|
+
export async function run({ payload, req }) {
|
|
7
|
+
const query = { ...(payload?.query ?? {}) }
|
|
8
|
+
const workspaceId = payload?.workspaceId ?? req?.workspaceId
|
|
9
|
+
const user = payload?.user ?? req?.user
|
|
10
|
+
let location = payload?.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
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createDocument } from './resource.helpers.js'
|
|
3
|
+
import { useSdk } from '@ossy/sdk-react'
|
|
4
|
+
import { View, useLocale } from '@ossy/design-system'
|
|
4
5
|
import { useRouter } from '@ossy/router-react'
|
|
5
6
|
import { GenericResourceForm } from './GenericResourceForm.jsx'
|
|
6
7
|
|
|
@@ -15,9 +16,10 @@ export const metadata = {
|
|
|
15
16
|
/** Generic resource create page — slot lookup with template Fields fallback. */
|
|
16
17
|
export default function GenericResourceCreatePage () {
|
|
17
18
|
const router = useRouter()
|
|
19
|
+
const { t } = useLocale()
|
|
18
20
|
const templateId = decodeURIComponent(router.params.templateId || '')
|
|
19
21
|
const location = router.searchParams.location
|
|
20
|
-
const
|
|
22
|
+
const sdk = useSdk()
|
|
21
23
|
const [error, setError] = useState()
|
|
22
24
|
|
|
23
25
|
const onCancel = () => {
|
|
@@ -26,9 +28,9 @@ export default function GenericResourceCreatePage () {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
const onSubmit = ({ name, content, type }) => {
|
|
29
|
-
createDocument({ type, location, name, content })
|
|
31
|
+
createDocument(sdk, { type, location, name, content })
|
|
30
32
|
.then(() => onCancel())
|
|
31
|
-
.catch((err) => setError(err?.message || '
|
|
33
|
+
.catch((err) => setError(err?.message || t('resources.form.createFailed')))
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
return (
|
|
@@ -5,14 +5,12 @@ import { GenericResourceDetail } from './GenericResourceDetail.jsx'
|
|
|
5
5
|
|
|
6
6
|
export const metadata = {
|
|
7
7
|
id: '@resource',
|
|
8
|
-
title: 'Resource',
|
|
9
8
|
path: {
|
|
10
9
|
en: '/resources/item/:resourceId',
|
|
11
10
|
sv: '/resources/objekt/:resourceId',
|
|
12
11
|
},
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
/** Generic resource detail page — slot lookup with template fallback. */
|
|
16
14
|
export default function ResourceDetailPage () {
|
|
17
15
|
const router = useRouter()
|
|
18
16
|
const resourceId = router.params.resourceId
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { cacheKey, normalizeLocation } from '@ossy/sdk-react'
|
|
2
|
+
import { metadata as CreateResource } from './create.action.js'
|
|
3
|
+
import { metadata as DeleteResource } from './delete.action.js'
|
|
4
|
+
import { metadata as ListResources } from './list.action.js'
|
|
5
|
+
import { metadata as UpdateResourceAccess } from './update-access.action.js'
|
|
6
|
+
import { metadata as UpdateResourceContent } from './update-content.action.js'
|
|
7
|
+
import { metadata as UpdateResourceLocation } from './update-location.action.js'
|
|
8
|
+
import { metadata as UpdateResourceName } from './update-name.action.js'
|
|
9
|
+
|
|
10
|
+
function invalidateLocation(sdk, location) {
|
|
11
|
+
sdk.invalidate(cacheKey(ListResources, { location: normalizeLocation(location) }))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function createDocument(sdk, document) {
|
|
15
|
+
const resource = await sdk.invoke(CreateResource, {
|
|
16
|
+
type: document.type,
|
|
17
|
+
location: normalizeLocation(document.location),
|
|
18
|
+
name: document.name,
|
|
19
|
+
content: document.content,
|
|
20
|
+
})
|
|
21
|
+
invalidateLocation(sdk, resource.location)
|
|
22
|
+
return resource
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function createDirectory(sdk, { location, name }) {
|
|
26
|
+
const resource = await sdk.invoke(CreateResource, {
|
|
27
|
+
type: 'directory',
|
|
28
|
+
location: normalizeLocation(location),
|
|
29
|
+
name,
|
|
30
|
+
})
|
|
31
|
+
invalidateLocation(sdk, resource.location)
|
|
32
|
+
return resource
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function uploadFile(sdk, uploadLocation, file) {
|
|
36
|
+
const location = normalizeLocation(uploadLocation)
|
|
37
|
+
const resource = await sdk.invoke(CreateResource, {
|
|
38
|
+
type: file.type,
|
|
39
|
+
location,
|
|
40
|
+
name: file.name,
|
|
41
|
+
size: file.size,
|
|
42
|
+
})
|
|
43
|
+
if (resource.content?.uploadUrl) {
|
|
44
|
+
await fetch(resource.content.uploadUrl, { method: 'PUT', body: file })
|
|
45
|
+
}
|
|
46
|
+
invalidateLocation(sdk, location)
|
|
47
|
+
return resource
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function removeResource(sdk, id, location) {
|
|
51
|
+
await sdk.invoke(DeleteResource, { id })
|
|
52
|
+
if (location) invalidateLocation(sdk, location)
|
|
53
|
+
sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function updateResourceContent(sdk, id, content) {
|
|
57
|
+
const resource = await sdk.invoke(UpdateResourceContent, { id, content })
|
|
58
|
+
invalidateLocation(sdk, resource.location)
|
|
59
|
+
sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
|
|
60
|
+
return resource
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function moveResource(sdk, id, target, sourceLocation) {
|
|
64
|
+
const resource = await sdk.invoke(UpdateResourceLocation, { id, target })
|
|
65
|
+
invalidateLocation(sdk, resource.location)
|
|
66
|
+
if (sourceLocation) invalidateLocation(sdk, sourceLocation)
|
|
67
|
+
sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
|
|
68
|
+
return resource
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function renameResource(sdk, id, name) {
|
|
72
|
+
const resource = await sdk.invoke(UpdateResourceName, { id, name })
|
|
73
|
+
invalidateLocation(sdk, resource.location)
|
|
74
|
+
sdk.invalidate(cacheKey({ id: 'resources/get' }, { id }))
|
|
75
|
+
return resource
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function updateResourceAccess(sdk, params) {
|
|
79
|
+
const resource = await sdk.invoke(UpdateResourceAccess, params)
|
|
80
|
+
invalidateLocation(sdk, resource.location)
|
|
81
|
+
sdk.invalidate(cacheKey({ id: 'resources/get' }, { id: params.id }))
|
|
82
|
+
return resource
|
|
83
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { attachResourceMediaUrls } from './resources.attach-media-urls.js'
|
|
2
|
+
|
|
3
|
+
export function mediaContextFromRun({ payload, req }) {
|
|
4
|
+
return {
|
|
5
|
+
userId: payload?.userId ?? req?.userId,
|
|
6
|
+
workspaceId: payload?.workspaceId ?? req?.workspaceId,
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function withResourceMedia(result, context) {
|
|
11
|
+
if (Array.isArray(result)) {
|
|
12
|
+
return Promise.all(result.map(r => attachResourceMediaUrls(r, context)))
|
|
13
|
+
}
|
|
14
|
+
if (result && typeof result === 'object' && result.id) {
|
|
15
|
+
return attachResourceMediaUrls(result, context)
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StorageClient } from '@ossy/platform'
|
|
2
2
|
import { Aggregate } from '@ossy/event-store'
|
|
3
|
-
import { Workspace } from '@ossy/workspaces'
|
|
3
|
+
import { Workspace } from '@ossy/workspaces/server'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Adds `content.src` (and `content.sizes[*]` URLs) for file resources, respecting
|
package/src/resources.events.js
CHANGED
package/src/resources.spec.js
CHANGED
|
@@ -9,72 +9,70 @@ function workspaceHeaders(userToken, workspaceId) {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
describe('[/
|
|
12
|
+
describe('[resources/create]', () => {
|
|
13
13
|
|
|
14
|
-
TestUtil.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
headers: {
|
|
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('[/
|
|
23
|
-
TestUtil.
|
|
24
|
-
|
|
25
|
-
|
|
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('[/
|
|
31
|
-
TestUtil.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headers: {
|
|
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('[
|
|
39
|
-
TestUtil.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
headers: {
|
|
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('[
|
|
47
|
-
TestUtil.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
headers: {
|
|
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('[
|
|
55
|
-
TestUtil.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
headers: {
|
|
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('[/
|
|
63
|
-
TestUtil.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
headers: {
|
|
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.
|
|
74
|
-
|
|
75
|
-
method: 'POST',
|
|
72
|
+
const workspace = await TestUtil.InvokeAction({
|
|
73
|
+
actionId: 'workspaces/create',
|
|
76
74
|
headers: { 'Content-Type': 'application/json', Authorization: user.token },
|
|
77
|
-
|
|
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.
|
|
93
|
-
|
|
94
|
-
method: 'POST',
|
|
90
|
+
const createRes = await TestUtil.InvokeAction({
|
|
91
|
+
actionId: 'resources/create',
|
|
95
92
|
headers: workspaceHeaders(user.token, workspace.id),
|
|
96
|
-
|
|
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.
|
|
109
|
-
|
|
110
|
-
method: 'PUT',
|
|
105
|
+
const updateRes = await TestUtil.InvokeAction({
|
|
106
|
+
actionId: 'resources/update-content',
|
|
111
107
|
headers: workspaceHeaders(user.token, workspace.id),
|
|
112
|
-
|
|
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.
|
|
130
|
-
|
|
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(
|
|
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.
|
|
138
|
-
|
|
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)
|
package/src/search.action.js
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
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,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"resources/home.documentTitle": "Gratis resurser",
|
|
3
|
+
"resources.home.title": "Gratis resurser",
|
|
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
|
+
"resources.form.nameRequired": "Namn krävs",
|
|
6
|
+
"resources.form.untitled": "Namnlös",
|
|
7
|
+
"resources.form.selectType": "Välj en resurstyp för att fortsätta.",
|
|
8
|
+
"resources.form.createFailed": "Skapandet misslyckades",
|
|
9
|
+
"resources.form.createResource": "Skapa resurs",
|
|
10
|
+
"resources/create.label": "Skapa resurs",
|
|
11
|
+
"resources/create.description": "Skapa en ny resurs i workspace",
|
|
12
|
+
"resources/list.label": "Lista resurser",
|
|
13
|
+
"resources/list.description": "Lista resurser på en plats i workspace",
|
|
14
|
+
"resources/get.label": "Hämta resurs",
|
|
15
|
+
"resources/get.description": "Hämta en enskild resurs via id",
|
|
16
|
+
"resources/search.label": "Sök resurser",
|
|
17
|
+
"resources/search.description": "Sök resurser i en workspace",
|
|
18
|
+
"resources/delete.label": "Ta bort resurs",
|
|
19
|
+
"resources/delete.description": "Ta bort en resurs från workspace",
|
|
20
|
+
"resources/update-name.label": "Uppdatera resursnamn",
|
|
21
|
+
"resources/update-name.description": "Byt namn på en resurs i workspace",
|
|
22
|
+
"resources/update-content.label": "Uppdatera resursinnehåll",
|
|
23
|
+
"resources/update-content.description": "Uppdatera innehållet i en resurs",
|
|
24
|
+
"resources/update-location.label": "Uppdatera resursplats",
|
|
25
|
+
"resources/update-location.description": "Flytta en resurs till en ny plats",
|
|
26
|
+
"resources/update-access.label": "Uppdatera resursåtkomst",
|
|
27
|
+
"resources/update-access.description": "Ändra åtkomstinställningar för en resurs",
|
|
28
|
+
"resources/upload-named-version.label": "Ladda upp namngiven version",
|
|
29
|
+
"resources/upload-named-version.description": "Ladda upp en namngiven version av en resursfil",
|
|
30
|
+
"resources/create-page-view.label": "Skapa sidvisning",
|
|
31
|
+
"resources/create-page-view.description": "Registrera en sidvisningshändelse för analys",
|
|
32
|
+
"resources/get-page-view-stats.label": "Hämta sidvisningsstatistik",
|
|
33
|
+
"resources/get-page-view-stats.description": "Hämta aggregerad statistik för sidvisningar",
|
|
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
|
+
"@resource.documentTitle": "Resurs",
|
|
39
|
+
"resources.upload.title": "Ladda upp",
|
|
40
|
+
"resources.upload.location": "Plats: {location}",
|
|
41
|
+
"resources.createDirectory.title": "Skapa mapp",
|
|
42
|
+
"resources.createDocument.title": "Skapa dokument",
|
|
43
|
+
"resources.createDirectory.description": "Skapa en ny mapp för att organisera dina resurser. Den nya mappen skapas på platsen: {location}",
|
|
44
|
+
"resources.createDirectory.namePlaceholder": "Mappnamn",
|
|
45
|
+
"resources.createDirectory.submit": "Skapa mapp",
|
|
46
|
+
"resources.createDirectory.errorEmpty": "Mappnamn får inte vara tomt",
|
|
47
|
+
"resources.createDirectory.errorOssyPrefix": "Mappnamn får inte börja med @ossy"
|
|
48
|
+
}
|
|
@@ -1,20 +1 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
+
}
|