export const API_CLIENT_AUTHORIZATION_REMOVE_DIALOG = 'apiClientAuthorizationRemoveDialog';
export const API_CLIENT_AUTHORIZATION_ATTRIBUTES_DIALOG = 'apiClientAuthorizationAttributesDialog';
+export const API_CLIENT_AUTHORIZATION_HELP_DIALOG = 'apiClientAuthorizationHelpDialog';
export const loadApiClientAuthorizationsPanel = () =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
} catch (e) {
return;
}
+ };
+
+export const openApiClientAuthorizationsHelpDialog = () =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const apiHost = getState().properties.apiHost;
+ const user = getState().auth.user;
+ const email = user ? user.email : '';
+ const apiToken = getState().auth.apiToken;
+ dispatch(dialogActions.OPEN_DIALOG({ id: API_CLIENT_AUTHORIZATION_HELP_DIALOG, data: { apiHost, apiToken, email } }));
};
\ No newline at end of file
import { API_CLIENT_AUTHORIZATION_ATTRIBUTES_DIALOG } from '~/store/api-client-authorizations/api-client-authorizations-actions';
import { ArvadosTheme } from '~/common/custom-theme';
import { ApiClientAuthorization } from '~/models/api-client-authorization';
+import { formatDate } from '~/common/formatters';
type CssRules = 'root';
<Grid item xs={7}>{data.apiClientAuthorization.uuid}</Grid>
<Grid item xs={5}>Owner uuid</Grid>
<Grid item xs={7}>{data.apiClientAuthorization.ownerUuid}</Grid>
+ <Grid item xs={5}>API Client ID</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.apiClientId}</Grid>
+ <Grid item xs={5}>API Token</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.apiToken}</Grid>
+ <Grid item xs={5}>Created by IP address</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.createdByIpAddress || '(none)'}</Grid>
+ <Grid item xs={5}>Default owner</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.defaultOwnerUuid || '(none)'}</Grid>
+ <Grid item xs={5}>Expires at</Grid>
+ <Grid item xs={7}>{formatDate(data.apiClientAuthorization.expiresAt) || '(none)'}</Grid>
+ <Grid item xs={5}>Last used at</Grid>
+ <Grid item xs={7}>{formatDate(data.apiClientAuthorization.lastUsedAt) || '(none)'}</Grid>
+ <Grid item xs={5}>Last used by IP address</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.lastUsedByIpAddress || '(none)'}</Grid>
+ <Grid item xs={5}>Scopes</Grid>
+ <Grid item xs={7}>{JSON.stringify(data.apiClientAuthorization.scopes || '(none)')}</Grid>
+ <Grid item xs={5}>User ID</Grid>
+ <Grid item xs={7}>{data.apiClientAuthorization.userId || '(none)'}</Grid>
+ <Grid item xs={5}>Created at</Grid>
+ <Grid item xs={7}>{formatDate(data.apiClientAuthorization.createdAt) || '(none)'}</Grid>
+ <Grid item xs={5}>Updated at</Grid>
+ <Grid item xs={7}>{formatDate(data.apiClientAuthorization.updatedAt) || '(none)'}</Grid>
</Grid>}
</DialogContent>
<DialogActions>
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from "@material-ui/core";
+import { WithDialogProps } from "~/store/dialog/with-dialog";
+import { withDialog } from '~/store/dialog/with-dialog';
+import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
+import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { compose } from "redux";
+import { API_CLIENT_AUTHORIZATION_HELP_DIALOG } from '~/store/api-client-authorizations/api-client-authorizations-actions';
+
+type CssRules = 'codeSnippet';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ codeSnippet: {
+ borderRadius: theme.spacing.unit * 0.5,
+ border: `1px solid ${theme.palette.grey["400"]}`,
+ '& pre': {
+ fontSize: '0.815rem'
+ }
+ }
+});
+
+interface HelpApiClientAuthorizationDataProps {
+ apiHost: string;
+ apiToken: string;
+ email: string;
+}
+
+export const HelpApiClientAuthorizationDialog = compose(
+ withDialog(API_CLIENT_AUTHORIZATION_HELP_DIALOG),
+ withStyles(styles))(
+ (props: WithDialogProps<HelpApiClientAuthorizationDataProps> & WithStyles<CssRules>) =>
+ <Dialog open={props.open}
+ onClose={props.closeDialog}
+ fullWidth
+ maxWidth='md'>
+ <DialogTitle>HELP:</DialogTitle>
+ <DialogContent>
+ <DefaultCodeSnippet
+ className={props.classes.codeSnippet}
+ lines={[snippetText(props.data)]} />
+ {/* // lines={snippetText2(props.data)} /> */}
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ onClick={props.closeDialog}>
+ Close
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
+
+const snippetText = (data: HelpApiClientAuthorizationDataProps) => `### Pasting the following lines at a shell prompt will allow Arvados SDKs
+### to authenticate to your account, ${data.email}
+
+read ARVADOS_API_TOKEN <<EOF
+${data.apiToken}
+EOF
+export ARVADOS_API_TOKEN ARVADOS_API_HOST=${data.apiHost}
+unset ARVADOS_API_HOST_INSECURE`;
const pathname = router.location ? router.location.pathname : '';
return !Routes.matchWorkflowRoute(pathname) && !Routes.matchVirtualMachineRoute(pathname) &&
!Routes.matchRepositoriesRoute(pathname) && !Routes.matchSshKeysRoute(pathname) &&
- !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname);
+ !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname) &&
+ !Routes.matchApiClientAuthorizationsRoute(pathname);
};
export const MainContentBar = connect((state: RootState) => ({
Table, TableHead, TableRow, TableCell, TableBody, Tooltip, IconButton
} from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
-import { MoreOptionsIcon } from '~/components/icon/icon';
+import { MoreOptionsIcon, HelpIcon } from '~/components/icon/icon';
import { ApiClientAuthorization } from '~/models/api-client-authorization';
+import { formatDate } from '~/common/formatters';
-type CssRules = 'root' | 'tableRow';
+type CssRules = 'root' | 'tableRow' | 'helpIconGrid' | 'tableGrid';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
root: {
width: '100%',
overflow: 'auto'
},
+ helpIconGrid: {
+ textAlign: 'right'
+ },
+ tableGrid: {
+ marginTop: theme.spacing.unit
+ },
tableRow: {
'& td, th': {
whiteSpace: 'nowrap'
export interface ApiClientAuthorizationPanelRootActionProps {
openRowOptions: (event: React.MouseEvent<HTMLElement>, keepService: ApiClientAuthorization) => void;
+ openHelpDialog: () => void;
}
export interface ApiClientAuthorizationPanelRootDataProps {
& ApiClientAuthorizationPanelRootDataProps & WithStyles<CssRules>;
export const ApiClientAuthorizationPanelRoot = withStyles(styles)(
- ({ classes, hasApiClientAuthorizations, apiClientAuthorizations, openRowOptions }: ApiClientAuthorizationPanelRootProps) =>
+ ({ classes, hasApiClientAuthorizations, apiClientAuthorizations, openRowOptions, openHelpDialog }: ApiClientAuthorizationPanelRootProps) =>
<Card className={classes.root}>
<CardContent>
- {hasApiClientAuthorizations && <Grid container direction="row">
+ {hasApiClientAuthorizations && <Grid container direction="row" justify="flex-end">
+ <Grid item xs={12} className={classes.helpIconGrid}>
+ <Tooltip title="Api token - help">
+ <IconButton onClick={openHelpDialog}>
+ <HelpIcon />
+ </IconButton>
+ </Tooltip>
+ </Grid>
<Grid item xs={12}>
<Table>
<TableHead>
<TableCell>{apiClientAuthorizatio.apiToken}</TableCell>
<TableCell>{apiClientAuthorizatio.createdByIpAddress || '(none)'}</TableCell>
<TableCell>{apiClientAuthorizatio.defaultOwnerUuid || '(none)'}</TableCell>
- <TableCell>{apiClientAuthorizatio.expiresAt || '(none)'}</TableCell>
- <TableCell>{apiClientAuthorizatio.lastUsedAt || '(none)'}</TableCell>
+ <TableCell>{formatDate(apiClientAuthorizatio.expiresAt) || '(none)'}</TableCell>
+ <TableCell>{formatDate(apiClientAuthorizatio.lastUsedAt) || '(none)'}</TableCell>
<TableCell>{apiClientAuthorizatio.lastUsedByIpAddress || '(none)'}</TableCell>
<TableCell>{JSON.stringify(apiClientAuthorizatio.scopes)}</TableCell>
<TableCell>{apiClientAuthorizatio.userId}</TableCell>
ApiClientAuthorizationPanelRootActionProps
} from '~/views/api-client-authorization-panel/api-client-authorization-panel-root';
import { openApiClientAuthorizationContextMenu } from '~/store/context-menu/context-menu-actions';
+import { openApiClientAuthorizationsHelpDialog } from '~/store/api-client-authorizations/api-client-authorizations-actions';
const mapStateToProps = (state: RootState): ApiClientAuthorizationPanelRootDataProps => {
return {
const mapDispatchToProps = (dispatch: Dispatch): ApiClientAuthorizationPanelRootActionProps => ({
openRowOptions: (event, apiClientAuthorization) => {
dispatch<any>(openApiClientAuthorizationContextMenu(event, apiClientAuthorization));
+ },
+ openHelpDialog: () => {
+ dispatch<any>(openApiClientAuthorizationsHelpDialog());
}
});
import { AttributesKeepServiceDialog } from '~/views-components/keep-services-dialog/attributes-dialog';
import { AttributesSshKeyDialog } from '~/views-components/ssh-keys-dialog/attributes-dialog';
import { VirtualMachineAttributesDialog } from '~/views-components/virtual-machines-dialog/attributes-dialog';
+import { HelpApiClientAuthorizationDialog } from '~/views-components/api-client-authorizations-dialog/help-dialog';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<CurrentTokenDialog />
<FileRemoveDialog />
<FilesUploadCollectionDialog />
+ <HelpApiClientAuthorizationDialog />
<MoveCollectionDialog />
<MoveProcessDialog />
<MoveProjectDialog />