Skip to content

Commit b2e358b

Browse files
Add read-only dashboard UI
1 parent 3ae701b commit b2e358b

File tree

66 files changed

+2220
-115
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2220
-115
lines changed

src/platform/packages/shared/content-management/content_editor/src/components/editor_flyout_content.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const getI18nTexts = ({ entityName }: { entityName: string }) => ({
3838
entityName,
3939
},
4040
}),
41+
// TODO: Remove and replace with normal X button in header
4142
cancelButtonLabel: i18n.translate('contentManagement.contentEditor.cancelButtonLabel', {
4243
defaultMessage: 'Cancel',
4344
}),
@@ -159,6 +160,12 @@ export const ContentEditorFlyoutContent: FC<Props> = ({
159160
form={{ ...form, isSubmitted }}
160161
isReadonly={isReadonly}
161162
readonlyReason={
163+
/*
164+
TODO: Replace this with a warning callout:
165+
managed: You don’t have permissions to edit this dashboard. Contact your admin to change your role.
166+
role no-edit: You don’t have permissions to edit this dashboard. Contact your admin to change your role.
167+
Readonly: You don’t have permissions to edit this dashboard. Contact [Creator name] to change it.
168+
*/
162169
readonlyReason ||
163170
i18n.translate('contentManagement.contentEditor.metadataForm.readOnlyToolTip', {
164171
defaultMessage: 'To edit these details, contact your administrator for access.',

src/platform/packages/shared/content-management/content_editor/src/components/metadata_form.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const MetadataForm: FC<React.PropsWithChildren<Props>> = ({
6262
<EuiForm isInvalid={isSubmitted && !isValid} error={getErrors()} data-test-subj="metadataForm">
6363
<ContentEditorFlyoutWarningsCallOut warningMessages={getWarnings()} />
6464
{isReadonly && <EuiCallOut size="s" title={readonlyReason} iconType="info" />}
65+
{/* TODO: Remove this icon */}
6566
<EuiFormRow
6667
label={i18n.translate('contentManagement.contentEditor.metadataForm.nameInputLabel', {
6768
defaultMessage: 'Name',

src/platform/packages/shared/shared-ux/modal/tabbed/src/tabbed_modal.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import React, {
1717
type ComponentProps,
1818
type FC,
1919
type ReactElement,
20+
type ReactNode,
2021
} from 'react';
2122
import {
2223
EuiButton,
@@ -65,6 +66,7 @@ export interface ITabbedModalInner extends Pick<ComponentProps<typeof EuiModal>,
6566
modalWidth?: number;
6667
modalTitle?: string;
6768
anchorElement?: HTMLElement;
69+
aboveTabsContent?: ReactNode;
6870
'data-test-subj'?: string;
6971
}
7072

@@ -73,6 +75,7 @@ const TabbedModalInner: FC<ITabbedModalInner> = ({
7375
modalTitle,
7476
modalWidth,
7577
anchorElement,
78+
aboveTabsContent: AboveTabsContent,
7679
...props
7780
}) => {
7881
const { tabs, state, dispatch } =
@@ -177,6 +180,7 @@ const TabbedModalInner: FC<ITabbedModalInner> = ({
177180
<EuiModalHeaderTitle id={tabbedModalHeadingHTMLId}>{modalTitle}</EuiModalHeaderTitle>
178181
</EuiModalHeader>
179182
<EuiModalBody>
183+
{AboveTabsContent}
180184
<Fragment>
181185
<Fragment>{renderTabs()}</Fragment>
182186
<EuiSpacer size="m" />

src/platform/plugins/shared/content_management/README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,30 @@
22

33
The content management plugin provides functionality to manage content in Kibana.
44

5+
## API Methods
6+
7+
The Content Management plugin provides the following methods for managing content:
8+
9+
### Core CRUD Operations
10+
11+
- `get()` - Retrieve a single content item
12+
- `bulkGet()` - Retrieve multiple content items
13+
- `create()` - Create a new content item
14+
- `update()` - Update an existing content item
15+
- `delete()` - Delete a content item
16+
- `search()` - Search for content items
17+
- `mSearch()` - Multi-type search across content
18+
19+
### Access Control
20+
21+
- `changeAccessMode()` - Change the access mode for content items
522

623
## Testing
724

825
Many parts of the Content Management service are implemented *in-memory*, hence it
926
is possible to test big chunks of the Content Management plugin using Jest
1027
tests.
1128

12-
1329
### Elasticsearch Integration tests
1430

1531
Some functionality of the Content Management plugin can be tested using *Kibana

src/platform/plugins/shared/content_management/common/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ export type {
2929
MSearchQuery,
3030
MSearchResult,
3131
MSearchOut,
32+
ChangeAccessModeIn,
33+
ChangeAccessModeResult,
3234
} from './rpc';
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import { schema } from '@kbn/config-schema';
11+
import type { Version } from '@kbn/object-versioning';
12+
import type { ProcedureSchemas } from './types';
13+
import { versionSchema } from './constants';
14+
15+
export const changeAccessModeSchemas: ProcedureSchemas = {
16+
in: schema.object(
17+
{
18+
version: schema.maybe(versionSchema),
19+
objects: schema.arrayOf(
20+
schema.object(
21+
{
22+
contentTypeId: schema.string(),
23+
id: schema.string(),
24+
},
25+
{ unknowns: 'forbid' }
26+
),
27+
{ minSize: 1 }
28+
),
29+
options: schema.object(
30+
{
31+
accessMode: schema.oneOf([schema.literal('read_only'), schema.literal('default')]),
32+
},
33+
{ unknowns: 'forbid' }
34+
),
35+
},
36+
{ unknowns: 'forbid' }
37+
),
38+
out: schema.maybe(
39+
schema.object({
40+
objects: schema.arrayOf(
41+
schema.object({
42+
id: schema.string(),
43+
type: schema.string(),
44+
error: schema.maybe(
45+
schema.object({
46+
error: schema.string(),
47+
message: schema.string(),
48+
statusCode: schema.number(),
49+
metadata: schema.maybe(schema.recordOf(schema.string(), schema.any())),
50+
})
51+
),
52+
})
53+
),
54+
})
55+
),
56+
};
57+
58+
export interface ChangeAccessModeIn {
59+
version?: Version;
60+
objects: Array<{
61+
contentTypeId: string;
62+
id: string;
63+
}>;
64+
options: {
65+
accessMode: AccessMode;
66+
};
67+
}
68+
69+
export interface ChangeAccessModeResult {
70+
objects: Array<
71+
{
72+
type: string;
73+
id: string;
74+
} & { error?: { error: string; message: string; statusCode: number } }
75+
>;
76+
}
77+
78+
export type AccessMode = 'default' | 'read_only';
79+
80+
export interface AccessControl {
81+
owner?: string;
82+
accessMode?: AccessMode;
83+
}

src/platform/plugins/shared/content_management/common/rpc/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const procedureNames = [
1818
'delete',
1919
'search',
2020
'mSearch',
21+
'changeAccessMode',
2122
] as const;
2223

2324
export type ProcedureName = (typeof procedureNames)[number];

src/platform/plugins/shared/content_management/common/rpc/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export type { SearchIn, SearchQuery, SearchResult } from './search';
1919
export type { MSearchIn, MSearchQuery, MSearchOut, MSearchResult } from './msearch';
2020
export type { ProcedureSchemas } from './types';
2121
export type { ProcedureName } from './constants';
22+
export type { ChangeAccessModeIn, ChangeAccessModeResult } from './change_access_mode';

src/platform/plugins/shared/content_management/common/rpc/rpc.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { updateSchemas } from './update';
1616
import { deleteSchemas } from './delete';
1717
import { searchSchemas } from './search';
1818
import { mSearchSchemas } from './msearch';
19+
import { changeAccessModeSchemas } from './change_access_mode';
1920

2021
export const schemas = {
2122
get: getSchemas,
@@ -25,6 +26,7 @@ export const schemas = {
2526
delete: deleteSchemas,
2627
search: searchSchemas,
2728
mSearch: mSearchSchemas,
29+
changeAccessMode: changeAccessModeSchemas,
2830
} satisfies {
2931
[key in ProcedureName]: ProcedureSchemas;
3032
};

src/platform/plugins/shared/content_management/public/content_client/content_client.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ import { lastValueFrom } from 'rxjs';
1111
import { takeWhile, toArray } from 'rxjs';
1212
import { createCrudClientMock } from '../crud_client/crud_client.mock';
1313
import { ContentClient } from './content_client';
14-
import type { GetIn, CreateIn, UpdateIn, DeleteIn, SearchIn, MSearchIn } from '../../common';
14+
import type {
15+
GetIn,
16+
CreateIn,
17+
UpdateIn,
18+
DeleteIn,
19+
SearchIn,
20+
MSearchIn,
21+
ChangeAccessModeIn,
22+
} from '../../common';
1523
import { ContentTypeRegistry } from '../registry';
1624

1725
const setup = () => {
@@ -198,3 +206,18 @@ describe('#mSearch', () => {
198206
});
199207
});
200208
});
209+
210+
describe('#changeAccessMode', () => {
211+
it('calls rpcClient.changeAccessMode with input and returns output', async () => {
212+
const { crudClient, contentClient } = setup();
213+
const input: ChangeAccessModeIn = {
214+
objects: [{ contentTypeId: 'testType', id: 'test-id' }],
215+
options: { accessMode: 'read_only' },
216+
};
217+
const output = { objects: [{ type: 'testType', id: 'test-id' }] };
218+
// @ts-ignore
219+
crudClient.changeAccessMode.mockResolvedValueOnce(output);
220+
expect(await contentClient.changeAccessMode(input)).toEqual(output);
221+
expect(crudClient.changeAccessMode).toBeCalledWith(input);
222+
});
223+
});

0 commit comments

Comments
 (0)