From e3d579e410fe6a1f93bad62ca9edf52300e73d47 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 26 Sep 2018 13:45:04 +0200 Subject: [PATCH] Add "search box" and a "add new" box to the new API Keys page #13411 --- public/app/features/api-keys/ApiKeysPage.tsx | 147 ++++++++++++++++-- public/app/features/api-keys/state/actions.ts | 23 ++- .../app/features/api-keys/state/reducers.ts | 7 +- .../app/features/api-keys/state/selectors.ts | 9 ++ public/app/types/apiKeys.ts | 6 + public/app/types/index.ts | 3 +- 6 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 public/app/features/api-keys/state/selectors.ts diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index e0b4da28c40..5ad292c7ba3 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -1,12 +1,13 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; -import { NavModel, ApiKey } from '../../types'; +import { NavModel, ApiKey, NewApiKey, OrgRole } from 'app/types'; import { getNavModel } from 'app/core/selectors/navModel'; +import { getApiKeys } from './state/selectors'; +import { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey } from './state/actions'; // import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; -import { loadApiKeys, deleteApiKey } from './state/actions'; -import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import SlideDown from 'app/core/components/Animations/SlideDown'; export interface Props { navModel: NavModel; @@ -14,12 +15,31 @@ export interface Props { searchQuery: string; loadApiKeys: typeof loadApiKeys; deleteApiKey: typeof deleteApiKey; - // loadTeams: typeof loadTeams; - // deleteTeam: typeof deleteTeam; - // setSearchQuery: typeof setSearchQuery; + setSearchQuery: typeof setSearchQuery; + addApiKey: typeof addApiKey; } +export interface State { + isAdding: boolean; + newApiKey: NewApiKey; +} + +enum ApiKeyStateProps { + Name = 'name', + Role = 'role', +} + +const initialApiKeyState = { + name: '', + role: OrgRole.Viewer, +}; + export class ApiKeysPage extends PureComponent { + constructor(props) { + super(props); + this.state = { isAdding: false, newApiKey: initialApiKeyState }; + } + componentDidMount() { this.fetchApiKeys(); } @@ -28,19 +48,120 @@ export class ApiKeysPage extends PureComponent { await this.props.loadApiKeys(); } - deleteApiKey(id: number) { + onDeleteApiKey(id: number) { return () => { this.props.deleteApiKey(id); }; } + onSearchQueryChange = evt => { + this.props.setSearchQuery(evt.target.value); + }; + + onToggleAdding = () => { + this.setState({ isAdding: !this.state.isAdding }); + }; + + onAddApiKey = async evt => { + evt.preventDefault(); + this.props.addApiKey(this.state.newApiKey); + this.setState((prevState: State) => { + return { + ...prevState, + newApiKey: initialApiKeyState, + }; + }); + }; + + onApiKeyStateUpdate = (evt, prop: string) => { + const value = evt.currentTarget.value; + this.setState((prevState: State) => { + const newApiKey = { + ...prevState.newApiKey, + }; + newApiKey[prop] = value; + + return { + ...prevState, + newApiKey: newApiKey, + }; + }); + }; + render() { - const { navModel, apiKeys } = this.props; + const { newApiKey, isAdding } = this.state; + const { navModel, apiKeys, searchQuery } = this.props; return (
+
+
+ +
+ +
+ + {/* +
+ + +
+ +
Add API Key
+
+
+
+ Key name + this.onApiKeyStateUpdate(evt, ApiKeyStateProps.Name)} + /> +
+
+ Role + + + +
+
+ +
+
+
+
+
+

Existing Keys

@@ -59,7 +180,7 @@ export class ApiKeysPage extends PureComponent { @@ -78,7 +199,8 @@ export class ApiKeysPage extends PureComponent { function mapStateToProps(state) { return { navModel: getNavModel(state.navIndex, 'apikeys'), - apiKeys: state.apiKeys.keys, + apiKeys: getApiKeys(state.apiKeys), + searchQuery: state.apiKeys.searchQuery, // searchQuery: getSearchQuery(state.teams), }; } @@ -86,9 +208,8 @@ function mapStateToProps(state) { const mapDispatchToProps = { loadApiKeys, deleteApiKey, - // loadTeams, - // deleteTeam, - // setSearchQuery, + setSearchQuery, + addApiKey, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(ApiKeysPage)); diff --git a/public/app/features/api-keys/state/actions.ts b/public/app/features/api-keys/state/actions.ts index 494b562b3c9..934852e1b19 100644 --- a/public/app/features/api-keys/state/actions.ts +++ b/public/app/features/api-keys/state/actions.ts @@ -1,10 +1,10 @@ import { ThunkAction } from 'redux-thunk'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { StoreState, ApiKey } from 'app/types'; -import { updateNavIndex, UpdateNavIndexAction } from 'app/core/actions'; export enum ActionTypes { LoadApiKeys = 'LOAD_API_KEYS', + SetApiKeysSearchQuery = 'SET_API_KEYS_SEARCH_QUERY', } export interface LoadApiKeysAction { @@ -12,15 +12,27 @@ export interface LoadApiKeysAction { payload: ApiKey[]; } -export type Action = LoadApiKeysAction; +export interface SetSearchQueryAction { + type: ActionTypes.SetApiKeysSearchQuery; + payload: string; +} -type ThunkResult = ThunkAction; +export type Action = LoadApiKeysAction | SetSearchQueryAction; + +type ThunkResult = ThunkAction; const apiKeysLoaded = (apiKeys: ApiKey[]): LoadApiKeysAction => ({ type: ActionTypes.LoadApiKeys, payload: apiKeys, }); +export function addApiKey(apiKey: ApiKey): ThunkResult { + return async dispatch => { + await getBackendSrv().post('/api/auth/keys', apiKey); + dispatch(loadApiKeys()); + }; +} + export function loadApiKeys(): ThunkResult { return async dispatch => { const response = await getBackendSrv().get('/api/auth/keys'); @@ -35,3 +47,8 @@ export function deleteApiKey(id: number): ThunkResult { .then(dispatch(loadApiKeys())); }; } + +export const setSearchQuery = (searchQuery: string): SetSearchQueryAction => ({ + type: ActionTypes.SetApiKeysSearchQuery, + payload: searchQuery, +}); diff --git a/public/app/features/api-keys/state/reducers.ts b/public/app/features/api-keys/state/reducers.ts index 6d45ccbfa03..a21aa55dbf7 100644 --- a/public/app/features/api-keys/state/reducers.ts +++ b/public/app/features/api-keys/state/reducers.ts @@ -1,12 +1,17 @@ import { ApiKeysState } from 'app/types'; import { Action, ActionTypes } from './actions'; -export const initialApiKeysState: ApiKeysState = { keys: [] }; +export const initialApiKeysState: ApiKeysState = { + keys: [], + searchQuery: '', +}; export const apiKeysReducer = (state = initialApiKeysState, action: Action): ApiKeysState => { switch (action.type) { case ActionTypes.LoadApiKeys: return { ...state, keys: action.payload }; + case ActionTypes.SetApiKeysSearchQuery: + return { ...state, searchQuery: action.payload }; } return state; }; diff --git a/public/app/features/api-keys/state/selectors.ts b/public/app/features/api-keys/state/selectors.ts new file mode 100644 index 00000000000..8065c252e85 --- /dev/null +++ b/public/app/features/api-keys/state/selectors.ts @@ -0,0 +1,9 @@ +import { ApiKeysState } from 'app/types'; + +export const getApiKeys = (state: ApiKeysState) => { + const regex = RegExp(state.searchQuery, 'i'); + + return state.keys.filter(key => { + return regex.test(key.name) || regex.test(key.role); + }); +}; diff --git a/public/app/types/apiKeys.ts b/public/app/types/apiKeys.ts index 56d3e930504..6288f5165ad 100644 --- a/public/app/types/apiKeys.ts +++ b/public/app/types/apiKeys.ts @@ -6,6 +6,12 @@ export interface ApiKey { role: OrgRole; } +export interface NewApiKey { + name: string; + role: OrgRole; +} + export interface ApiKeysState { keys: ApiKey[]; + searchQuery: string; } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index bd219282f52..42460ecb9c6 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -7,7 +7,7 @@ import { DashboardState } from './dashboard'; import { DashboardAcl, OrgRole, PermissionLevel } from './acl'; import { DataSource } from './datasources'; import { PluginMeta } from './plugins'; -import { ApiKey, ApiKeysState } from './apiKeys'; +import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { User } from './user'; export { @@ -37,6 +37,7 @@ export { PluginMeta, ApiKey, ApiKeysState, + NewApiKey, User, };
{key.name} {key.role} - +