import React, { PureComponent } from 'react'; import { connect, ConnectedProps } from 'react-redux'; // Utils import { rangeUtil } from '@grafana/data'; import { InlineField, InlineSwitch, VerticalGroup } from '@grafana/ui'; import appEvents from 'app/core/app_events'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import Page from 'app/core/components/Page/Page'; import config from 'app/core/config'; import { contextSrv } from 'app/core/core'; import { getNavModel } from 'app/core/selectors/navModel'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { AccessControlAction, ApiKey, NewApiKey, StoreState } from 'app/types'; import { ShowModalReactEvent } from 'app/types/events'; import { ApiKeysActionBar } from './ApiKeysActionBar'; import { ApiKeysAddedModal } from './ApiKeysAddedModal'; import { ApiKeysController } from './ApiKeysController'; import { ApiKeysForm } from './ApiKeysForm'; import { ApiKeysTable } from './ApiKeysTable'; import { addApiKey, deleteApiKey, loadApiKeys, toggleIncludeExpired } from './state/actions'; import { setSearchQuery } from './state/reducers'; import { getApiKeys, getApiKeysCount, getIncludeExpired, getIncludeExpiredDisabled } from './state/selectors'; function mapStateToProps(state: StoreState) { const canCreate = contextSrv.hasAccess(AccessControlAction.ActionAPIKeysCreate, true); return { navModel: getNavModel(state.navIndex, 'apikeys'), apiKeys: getApiKeys(state.apiKeys), searchQuery: state.apiKeys.searchQuery, apiKeysCount: getApiKeysCount(state.apiKeys), hasFetched: state.apiKeys.hasFetched, timeZone: getTimeZone(state.user), includeExpired: getIncludeExpired(state.apiKeys), includeExpiredDisabled: getIncludeExpiredDisabled(state.apiKeys), canCreate: canCreate, }; } const mapDispatchToProps = { loadApiKeys, deleteApiKey, setSearchQuery, toggleIncludeExpired, addApiKey, }; const connector = connect(mapStateToProps, mapDispatchToProps); interface OwnProps {} export type Props = OwnProps & ConnectedProps; interface State { isAdding: boolean; } export class ApiKeysPageUnconnected extends PureComponent { constructor(props: Props) { super(props); } componentDidMount() { this.fetchApiKeys(); } async fetchApiKeys() { await this.props.loadApiKeys(); } onDeleteApiKey = (key: ApiKey) => { this.props.deleteApiKey(key.id!); }; onSearchQueryChange = (value: string) => { this.props.setSearchQuery(value); }; onIncludeExpiredChange = (event: React.SyntheticEvent) => { this.props.toggleIncludeExpired(); }; onAddApiKey = (newApiKey: NewApiKey) => { const openModal = (apiKey: string) => { const rootPath = window.location.origin + config.appSubUrl; appEvents.publish( new ShowModalReactEvent({ props: { apiKey, rootPath, }, component: ApiKeysAddedModal, }) ); }; const secondsToLive = newApiKey.secondsToLive; try { const secondsToLiveAsNumber = secondsToLive ? rangeUtil.intervalToSeconds(secondsToLive) : null; const apiKey: ApiKey = { ...newApiKey, secondsToLive: secondsToLiveAsNumber, }; this.props.addApiKey(apiKey, openModal); this.setState((prevState: State) => { return { ...prevState, isAdding: false, }; }); } catch (err) { console.error(err); } }; render() { const { hasFetched, navModel, apiKeysCount, apiKeys, searchQuery, timeZone, includeExpired, includeExpiredDisabled, canCreate, } = this.props; if (!hasFetched) { return ( {} ); } return ( {({ isAdding, toggleIsAdding }) => { const showCTA = !isAdding && apiKeysCount === 0; const showTable = apiKeysCount > 0; return ( <> {/* TODO: enable when API keys to service accounts migration is ready {config.featureToggles.serviceAccounts && ( Service accounts give you more control. API keys will be automatically migrated into tokens inside respective service accounts. The current API keys will still work, but will be called tokens and you will find them in the detail view of a respective service account. )} */} {showCTA ? ( ) : null} {showTable ? ( ) : null} {showTable ? ( ) : null} ); }} ); } } const ApiKeysPage = connector(ApiKeysPageUnconnected); export default ApiKeysPage;