Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions workspaces/lightspeed/.changeset/strange-peaches-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-lightspeed': patch
---

Added a missing permission screen for the Notebooks tab. When a user lacks the `lightspeed.notebooks.use` permission, the Notebooks tab now displays a "Missing permission" message with a "Go back" button instead of the notebook list.
5 changes: 4 additions & 1 deletion workspaces/lightspeed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@
"resolutions": {
"@types/react": "^18",
"@types/react-dom": "^18",
"refractor@npm:3.6.0/prismjs": "^1.30.0"
"refractor@npm:3.6.0/prismjs": "^1.30.0",
"@backstage/backend-app-api": "1.4.1",
"@backstage/backend-plugin-api": "1.6.2",
"@backstage/frontend-plugin-api": "0.15.1"
},
"prettier": "@spotify/prettier-config",
"lint-staged": {
Expand Down
1 change: 1 addition & 0 deletions workspaces/lightspeed/packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage-community/plugin-rbac": "^1.51.0",
"@backstage/app-defaults": "^1.7.4",
"@backstage/catalog-model": "^1.7.6",
"@backstage/cli": "^0.35.3",
Expand Down
2 changes: 2 additions & 0 deletions workspaces/lightspeed/packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
LightspeedPage,
LightspeedDrawerProvider,
} from '@red-hat-developer-hub/backstage-plugin-lightspeed';
import { RbacPage } from '@backstage-community/plugin-rbac';

const githubProvider = {
id: 'github-auth-provider',
Expand Down Expand Up @@ -129,6 +130,7 @@ const routes = (
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
<Route path="/lightspeed" element={<LightspeedPage />} />
<Route path="/rbac" element={<RbacPage />} />
</FlatRoutes>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
LightspeedDrawerStateExposer,
LightspeedFAB,
} from '@red-hat-developer-hub/backstage-plugin-lightspeed';
import { Administration } from '@backstage-community/plugin-rbac';
import { ApplicationDrawer } from './ApplicationDrawer';
import LogoFull from './LogoFull';
import LogoIcon from './LogoIcon';
Expand Down Expand Up @@ -127,6 +128,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => {
</SidebarGroup>
<SidebarSpace />
<SidebarDivider />
<Administration />
<SidebarGroup
label="Settings"
icon={<UserSettingsSignInAvatar />}
Expand Down
1 change: 1 addition & 0 deletions workspaces/lightspeed/packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
},
"dependencies": {
"@backstage-community/plugin-rbac-backend": "5.2.6",
"@backstage/backend-defaults": "^0.15.1",
"@backstage/config": "^1.3.6",
"@backstage/plugin-app-backend": "^0.5.10",
Expand Down
8 changes: 2 additions & 6 deletions workspaces/lightspeed/packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,8 @@ backend.add(
// See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors
backend.add(import('@backstage/plugin-catalog-backend-module-logs'));

// permission plugin
backend.add(import('@backstage/plugin-permission-backend'));
// See https://backstage.io/docs/permissions/getting-started for how to create your own permission policy
backend.add(
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
// RBAC backend (registers as "permission" and provides /rbac; do not add plugin-permission-backend separately)
backend.add(import('@backstage-community/plugin-rbac-backend'));

// search plugin
backend.add(import('@backstage/plugin-search-backend/alpha'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ readonly "conversation.rename.confirm.action": string;
readonly "conversation.rename.placeholder": string;
readonly "permission.required.title": string;
readonly "permission.required.description": string;
readonly "permission.notebooks.title": string;
readonly "permission.notebooks.description": string;
readonly "permission.notebooks.goBack": string;
readonly "footer.accuracy.label": string;
readonly "common.cancel": string;
readonly "common.close": string;
Expand Down
9 changes: 5 additions & 4 deletions workspaces/lightspeed/plugins/lightspeed/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { createApiRef } from '@backstage/core-plugin-api';
import { createApiRef, type ApiRef } from '@backstage/core-plugin-api';

import {
Attachment,
Expand Down Expand Up @@ -57,6 +57,7 @@ export type LightspeedAPI = {
* Lightspeed API interface
*/

export const lightspeedApiRef = createApiRef<LightspeedAPI>({
id: 'plugin.lightspeed.service',
});
export const lightspeedApiRef: ApiRef<LightspeedAPI> =
createApiRef<LightspeedAPI>({
id: 'plugin.lightspeed.service',
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { createApiRef } from '@backstage/core-plugin-api';
import { createApiRef, type ApiRef } from '@backstage/core-plugin-api';

import { NotebookSession } from '../types';

Expand All @@ -32,6 +32,8 @@ export type NotebooksAPI = {
* @public
* AI Notebooks API interface
*/
export const notebooksApiRef = createApiRef<NotebooksAPI>({
id: 'plugin.lightspeed.notebooks.service',
});
export const notebooksApiRef: ApiRef<NotebooksAPI> = createApiRef<NotebooksAPI>(
{
id: 'plugin.lightspeed.notebooks.service',
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {
useIsMobile,
useLastOpenedConversation,
useLightspeedDeletePermission,
useLightspeedNotebooksPermission,
useNotebookSessions,
usePinnedChatsSettings,
useSortSettings,
Expand All @@ -98,13 +99,14 @@ import {
import Attachment from './Attachment';
import { useFileAttachmentContext } from './AttachmentContext';
import { DeleteModal } from './DeleteModal';
import { DeleteNotebookModal } from './DeleteNotebookModal';
import FilePreview from './FilePreview';
import { LightspeedChatBox } from './LightspeedChatBox';
import { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader';
import { NotebooksTab } from './NotebooksTab';
import { DeleteNotebookModal } from './notebooks/DeleteNotebookModal';
import { NotebookPermissionRequired } from './notebooks/NotebookPermissionRequired';
import { NotebooksTab } from './notebooks/NotebooksTab';
import { RenameNotebookModal } from './notebooks/RenameNotebookModal';
import { RenameConversationModal } from './RenameConversationModal';
import { RenameNotebookModal } from './RenameNotebookModal';

const useStyles = makeStyles(theme => ({
body: {
Expand Down Expand Up @@ -463,6 +465,8 @@ export const LightspeedChat = ({

const { allowed: hasDeleteAccess } = useLightspeedDeletePermission();
const { allowed: hasUpdateAccess } = useLightspeedUpdatePermission();
const { allowed: hasNotebooksAccess, loading: notebooksPermissionLoading } =
useLightspeedNotebooksPermission();
const samplePrompts = useWelcomePrompts();
useEffect(() => {
if (!user || !isReady) return;
Expand Down Expand Up @@ -1218,19 +1222,26 @@ export const LightspeedChat = ({
}
/>
)}
{showNotebooksPanel && (
<NotebooksTab
notebooks={notebooks}
hasNotebooks={hasNotebooks}
classes={classes}
openNotebookMenuId={openNotebookMenuId}
setOpenNotebookMenuId={setOpenNotebookMenuId}
onRename={setRenameNotebookId}
onDelete={setDeleteNotebookId}
t={t}
getDocumentsCount={getDocumentsCount}
/>
)}
{showNotebooksPanel &&
!notebooksPermissionLoading &&
hasNotebooksAccess && (
<NotebooksTab
notebooks={notebooks}
hasNotebooks={hasNotebooks}
classes={classes}
openNotebookMenuId={openNotebookMenuId}
setOpenNotebookMenuId={setOpenNotebookMenuId}
onRename={setRenameNotebookId}
onDelete={setDeleteNotebookId}
t={t}
getDocumentsCount={getDocumentsCount}
/>
)}
{showNotebooksPanel &&
!notebooksPermissionLoading &&
!hasNotebooksAccess && (
<NotebookPermissionRequired onGoBack={() => setActiveTab(0)} />
)}
</Chatbot>
<Attachment />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jest.mock('../../hooks/useConversations', () => ({
}),
}));

jest.mock('../../hooks/useNotebookSessions', () => ({
jest.mock('../../hooks/notebooks/useNotebookSessions', () => ({
useNotebookSessions: jest.fn().mockReturnValue({
data: [],
refetch: jest.fn(),
Expand Down Expand Up @@ -180,7 +180,7 @@ describe('LightspeedChat', () => {
const mockSetCurrentConversationId = jest.fn();

beforeEach(() => {
mockUsePermission.mockReturnValue({ loading: true, allowed: true });
mockUsePermission.mockReturnValue({ loading: false, allowed: true });
mockUseConversations.mockReturnValue({
data: [],
isRefetching: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import DialogTitle from '@mui/material/DialogTitle';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';

import { useDeleteNotebook } from '../hooks/useDeleteNotebook';
import { useTranslation } from '../hooks/useTranslation';
import { useDeleteNotebook } from '../../hooks/notebooks/useDeleteNotebook';
import { useTranslation } from '../../hooks/useTranslation';

const useStyles = makeStyles(theme => ({
dialogPaper: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
import { EllipsisVIcon } from '@patternfly/react-icons';
import { CatalogIcon } from '@patternfly/react-icons/dist/esm/icons';

import { lightspeedTranslationRef } from '../translations/ref';
import { NotebookSession } from '../types';
import { formatUpdatedLabel } from '../utils/notebooks-utils';
import { lightspeedTranslationRef } from '../../translations/ref';
import { NotebookSession } from '../../types';
import { formatUpdatedLabel } from '../../utils/notebooks-utils';

type NotebookCardProps = {
notebook: NotebookSession;
Expand Down Expand Up @@ -70,7 +70,7 @@
onOpenChange={isOpen =>
setOpenNotebookMenuId(isOpen ? notebook.session_id : null)
}
toggle={toggleRef => (

Check warning on line 73 in workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookCard.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move this component definition out of the parent component and pass data as props.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh-plugins&issues=AZ05K2OMvIUCM9Fwzuls&open=AZ05K2OMvIUCM9Fwzuls&pullRequest=2633
<MenuToggle
ref={toggleRef}
variant="plain"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Button, Grid, Typography } from '@material-ui/core';
import { createStyles, makeStyles } from '@material-ui/core/styles';

import { useTranslation } from '../../hooks/useTranslation';
import permissionRequired from '../../images/permission-required.svg';

const useStyles = makeStyles(theme =>
createStyles({
root: {
padding: theme.spacing(4),
height: '100%',
maxWidth: 1592,
margin: 'auto',
},
title: {
fontSize: '2.5rem',
fontWeight: 400,
marginBottom: theme.spacing(2),
},
description: {
fontSize: '1rem',
color: theme.palette.text.secondary,
marginBottom: theme.spacing(3),
lineHeight: 1.5,
},
goBackButton: {
borderColor: theme.palette.primary.main,
color: theme.palette.primary.main,
borderRadius: 999,
textTransform: 'none',
paddingLeft: theme.spacing(3),
paddingRight: theme.spacing(3),
},
illustration: {
width: '100%',
maxWidth: 600,
height: 'auto',
},
}),
);

interface NotebookPermissionRequiredProps {
onGoBack: () => void;
}

export const NotebookPermissionRequired = ({
onGoBack,
}: NotebookPermissionRequiredProps) => {
const classes = useStyles();
const { t } = useTranslation();

return (
<div className={classes.root}>
<Grid
container
spacing={4}
alignItems="center"
justifyContent="center"
style={{ height: '100%' }}
>
<Grid item xs={12} md={6}>
<Typography variant="h4" className={classes.title}>
{t('permission.notebooks.title')}
</Typography>
<Typography className={classes.description}>
{t('permission.notebooks.description')}
</Typography>
<Button
variant="outlined"
className={classes.goBackButton}
onClick={onGoBack}
>
{t('permission.notebooks.goBack')}
</Button>
</Grid>
<Grid item xs={12} md={6} style={{ textAlign: 'right' }}>
<img
src={permissionRequired as any}
alt={t('icon.permissionRequired.alt')}
className={classes.illustration}
/>
</Grid>
</Grid>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import { PlusCircleIcon } from '@patternfly/react-icons';
import { CatalogIcon } from '@patternfly/react-icons/dist/esm/icons';

import { lightspeedTranslationRef } from '../translations/ref';
import { NotebookSession } from '../types';
import { lightspeedTranslationRef } from '../../translations/ref';
import { NotebookSession } from '../../types';
import { NotebookCard } from './NotebookCard';

type NotebooksTabProps = {
Expand Down Expand Up @@ -63,7 +63,7 @@
</Button>
)}
</div>
{!hasNotebooks ? (

Check warning on line 66 in workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebooksTab.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh-plugins&issues=AZ05K2CxvIUCM9Fwzulr&open=AZ05K2CxvIUCM9Fwzulr&pullRequest=2633
<div className={classes.notebooksEmptyState}>
<CatalogIcon className={classes.notebooksIcon} />
<Typography variant="h6" className={classes.notebooksHeadingEmpty}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import DialogTitle from '@mui/material/DialogTitle';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';

import { useRenameNotebook } from '../hooks/useRenameNotebook';
import { useTranslation } from '../hooks/useTranslation';
import { useRenameNotebook } from '../../hooks/notebooks/useRenameNotebook';
import { useTranslation } from '../../hooks/useTranslation';

const useStyles = makeStyles(theme => ({
dialogPaper: {
Expand Down
7 changes: 4 additions & 3 deletions workspaces/lightspeed/plugins/lightspeed/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ export * from './useConversationMessages';
export * from './useConversations';
export * from './useCreateCoversationMessage';
export * from './useDeleteConversation';
export * from './useDeleteNotebook';
export * from './notebooks/useDeleteNotebook';
export * from './useIsMobile';
export * from './useLastOpenedConversation';
export * from './useLightspeedDeletePermission';
export * from './notebooks/useLightspeedNotebooksPermission';
export * from './useLightspeedViewPermission';
export * from './useDisplayModeSettings';
export * from './useNotebookSessions';
export * from './notebooks/useNotebookSessions';
export * from './usePinnedChatsSettings';
export * from './useRenameNotebook';
export * from './notebooks/useRenameNotebook';
export * from './useSortSettings';
export * from './useTranslation';
Loading
Loading