import React, { PureComponent } from 'react'; import ReactDOMServer from 'react-dom/server'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; import { ApiKey, NewApiKey, OrgRole } from 'app/types'; import { getNavModel } from 'app/core/selectors/navModel'; import { getApiKeys, getApiKeysCount } from './state/selectors'; import { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey } from './state/actions'; import Page from 'app/core/components/Page/Page'; import { SlideDown } from 'app/core/components/Animations/SlideDown'; import ApiKeysAddedModal from './ApiKeysAddedModal'; import config from 'app/core/config'; import appEvents from 'app/core/app_events'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { DeleteButton, EventsWithValidation, FormLabel, Input, ValidationEvents } from '@grafana/ui'; import { NavModel } from '@grafana/data'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { store } from 'app/store/store'; import kbn from 'app/core/utils/kbn'; // Utils import { dateTime, isDateTime } from '@grafana/data'; import { getTimeZone } from 'app/features/profile/state/selectors'; const timeRangeValidationEvents: ValidationEvents = { [EventsWithValidation.onBlur]: [ { rule: value => { if (!value) { return true; } try { kbn.interval_to_seconds(value); return true; } catch { return false; } }, errorMessage: 'Not a valid duration', }, ], }; export interface Props { navModel: NavModel; apiKeys: ApiKey[]; searchQuery: string; hasFetched: boolean; loadApiKeys: typeof loadApiKeys; deleteApiKey: typeof deleteApiKey; setSearchQuery: typeof setSearchQuery; addApiKey: typeof addApiKey; apiKeysCount: number; } export interface State { isAdding: boolean; newApiKey: NewApiKey; } enum ApiKeyStateProps { Name = 'name', Role = 'role', SecondsToLive = 'secondsToLive', } const initialApiKeyState = { name: '', role: OrgRole.Viewer, secondsToLive: '', }; const tooltipText = 'The api key life duration. For example 1d if your key is going to last for one day. All the supported units are: s,m,h,d,w,M,y'; export class ApiKeysPage extends PureComponent { constructor(props: Props) { super(props); this.state = { isAdding: false, newApiKey: initialApiKeyState }; } componentDidMount() { this.fetchApiKeys(); } async fetchApiKeys() { await this.props.loadApiKeys(); } onDeleteApiKey(key: ApiKey) { this.props.deleteApiKey(key.id); } onSearchQueryChange = (value: string) => { this.props.setSearchQuery(value); }; onToggleAdding = () => { this.setState({ isAdding: !this.state.isAdding }); }; onAddApiKey = async (evt: any) => { evt.preventDefault(); const openModal = (apiKey: string) => { const rootPath = window.location.origin + config.appSubUrl; const modalTemplate = ReactDOMServer.renderToString(); appEvents.emit('show-modal', { templateHtml: modalTemplate, }); }; // make sure that secondsToLive is number or null const secondsToLive = this.state.newApiKey['secondsToLive']; this.state.newApiKey['secondsToLive'] = secondsToLive ? kbn.interval_to_seconds(secondsToLive) : null; this.props.addApiKey(this.state.newApiKey, openModal); this.setState((prevState: State) => { return { ...prevState, newApiKey: initialApiKeyState, isAdding: false, }; }); }; onApiKeyStateUpdate = (evt: any, prop: string) => { const value = evt.currentTarget.value; this.setState((prevState: State) => { const newApiKey: any = { ...prevState.newApiKey, }; newApiKey[prop] = value; return { ...prevState, newApiKey: newApiKey, }; }); }; renderEmptyList() { const { isAdding } = this.state; return ( <> {!isAdding && ( )} {this.renderAddApiKeyForm()} ); } formatDate(date: any, format?: string) { if (!date) { return 'No expiration date'; } date = isDateTime(date) ? date : dateTime(date); format = format || 'YYYY-MM-DD HH:mm:ss'; const timezone = getTimeZone(store.getState().user); return timezone === 'utc' ? date.utc().format(format) : date.format(format); } renderAddApiKeyForm() { const { newApiKey, isAdding } = this.state; return (
Add API Key
Key name this.onApiKeyStateUpdate(evt, ApiKeyStateProps.Name)} />
Role
Time to live this.onApiKeyStateUpdate(evt, ApiKeyStateProps.SecondsToLive)} />
); } renderApiKeyList() { const { isAdding } = this.state; const { apiKeys, searchQuery } = this.props; return ( <>
{this.renderAddApiKeyForm()}

Existing Keys

{apiKeys.length > 0 ? ( {apiKeys.map(key => { return ( ); })} ) : null}
Name Role Expires
{key.name} {key.role} {this.formatDate(key.expiration)} this.onDeleteApiKey(key)} />
); } render() { const { hasFetched, navModel, apiKeysCount } = this.props; return ( {hasFetched && (apiKeysCount > 0 ? this.renderApiKeyList() : this.renderEmptyList())} ); } } function mapStateToProps(state: any) { return { navModel: getNavModel(state.navIndex, 'apikeys'), apiKeys: getApiKeys(state.apiKeys), searchQuery: state.apiKeys.searchQuery, apiKeysCount: getApiKeysCount(state.apiKeys), hasFetched: state.apiKeys.hasFetched, }; } const mapDispatchToProps = { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey, }; export default hot(module)( connect( mapStateToProps, mapDispatchToProps )(ApiKeysPage) );