77import React , { Component } from 'react' ;
88import PropTypes from 'prop-types' ;
99import { Route , Switch , Redirect } from 'react-router-dom' ;
10+ import chrome from 'ui/chrome' ;
11+ import { fatalError } from 'ui/notify' ;
12+ import { i18n } from '@kbn/i18n' ;
13+ import { injectI18n , FormattedMessage } from '@kbn/i18n/react' ;
14+
15+ import {
16+ EuiEmptyPrompt ,
17+ EuiFlexGroup ,
18+ EuiFlexItem ,
19+ EuiLoadingSpinner ,
20+ EuiPageContent ,
21+ EuiTitle ,
22+ } from '@elastic/eui' ;
1023
11- import routing from './services/routing' ;
1224import { BASE_PATH } from '../../common/constants' ;
25+ import { SectionUnauthorized , SectionError } from './components' ;
26+ import routing from './services/routing' ;
27+ import { isAvailable , isActive , getReason } from './services/license' ;
28+ import { loadPermissions } from './services/api' ;
1329
1430import {
1531 CrossClusterReplicationHome ,
@@ -19,47 +35,181 @@ import {
1935 FollowerIndexEdit ,
2036} from './sections' ;
2137
22- export class App extends Component {
23- static contextTypes = {
24- router : PropTypes . shape ( {
25- history : PropTypes . shape ( {
26- push : PropTypes . func . isRequired ,
27- createHref : PropTypes . func . isRequired
38+ export const App = injectI18n (
39+ class extends Component {
40+ static contextTypes = {
41+ router : PropTypes . shape ( {
42+ history : PropTypes . shape ( {
43+ push : PropTypes . func . isRequired ,
44+ createHref : PropTypes . func . isRequired
45+ } ) . isRequired
2846 } ) . isRequired
29- } ) . isRequired
30- }
47+ }
3148
32- constructor ( ...args ) {
33- super ( ...args ) ;
34- this . registerRouter ( ) ;
35- }
49+ constructor ( ...args ) {
50+ super ( ...args ) ;
51+ this . registerRouter ( ) ;
3652
37- componentWillMount ( ) {
38- routing . userHasLeftApp = false ;
39- }
53+ this . state = {
54+ isFetchingPermissions : false ,
55+ fetchPermissionError : undefined ,
56+ hasPermission : false ,
57+ missingPermissions : [ ] ,
58+ } ;
59+ }
4060
41- componentWillUnmount ( ) {
42- routing . userHasLeftApp = true ;
43- }
61+ componentWillMount ( ) {
62+ routing . userHasLeftApp = false ;
63+ }
4464
45- registerRouter ( ) {
46- const { router } = this . context ;
47- routing . reactRouter = router ;
48- }
65+ componentDidMount ( ) {
66+ this . checkPermissions ( ) ;
67+ }
4968
50- render ( ) {
51- return (
52- < div >
53- < Switch >
54- < Redirect exact from = { `${ BASE_PATH } ` } to = { `${ BASE_PATH } /follower_indices` } />
55- < Route exact path = { `${ BASE_PATH } /auto_follow_patterns/add` } component = { AutoFollowPatternAdd } />
56- < Route exact path = { `${ BASE_PATH } /auto_follow_patterns/edit/:id` } component = { AutoFollowPatternEdit } />
57- < Route exact path = { `${ BASE_PATH } /follower_indices/add` } component = { FollowerIndexAdd } />
58- < Route exact path = { `${ BASE_PATH } /follower_indices/edit/:id` } component = { FollowerIndexEdit } />
59- < Route exact path = { `${ BASE_PATH } /:section` } component = { CrossClusterReplicationHome } />
60- </ Switch >
61- </ div >
62- ) ;
63- }
64- }
69+ componentWillUnmount ( ) {
70+ routing . userHasLeftApp = true ;
71+ }
72+
73+ async checkPermissions ( ) {
74+ this . setState ( {
75+ isFetchingPermissions : true ,
76+ } ) ;
6577
78+ try {
79+ const { hasPermission, missingPermissions } = await loadPermissions ( ) ;
80+
81+ this . setState ( {
82+ isFetchingPermissions : false ,
83+ hasPermission,
84+ missingPermissions,
85+ } ) ;
86+ } catch ( error ) {
87+ // Expect an error in the shape provided by Angular's $http service.
88+ if ( error && error . data ) {
89+ return this . setState ( {
90+ isFetchingPermissions : false ,
91+ fetchPermissionError : error ,
92+ } ) ;
93+ }
94+
95+ // This error isn't an HTTP error, so let the fatal error screen tell the user something
96+ // unexpected happened.
97+ fatalError ( error , i18n . translate ( 'xpack.crossClusterReplication.app.checkPermissionsFatalErrorTitle' , {
98+ defaultMessage : 'Cross Cluster Replication app' ,
99+ } ) ) ;
100+ }
101+ }
102+
103+ registerRouter ( ) {
104+ const { router } = this . context ;
105+ routing . reactRouter = router ;
106+ }
107+
108+ render ( ) {
109+ const {
110+ isFetchingPermissions,
111+ fetchPermissionError,
112+ hasPermission,
113+ missingPermissions,
114+ } = this . state ;
115+
116+ if ( ! isAvailable ( ) || ! isActive ( ) ) {
117+ return (
118+ < SectionUnauthorized
119+ title = { (
120+ < FormattedMessage
121+ id = "xpack.crossClusterReplication.app.licenseErrorTitle"
122+ defaultMessage = "License error"
123+ />
124+ ) }
125+ >
126+ { getReason ( ) }
127+ { ' ' }
128+ < a href = { chrome . addBasePath ( '/app/kibana#/management/elasticsearch/license_management/home' ) } >
129+ < FormattedMessage
130+ id = "xpack.crossClusterReplication.app.licenseErrorLinkText"
131+ defaultMessage = "Manage your license."
132+ />
133+ </ a >
134+ </ SectionUnauthorized >
135+ ) ;
136+ }
137+
138+ if ( isFetchingPermissions ) {
139+ return (
140+ < EuiPageContent horizontalPosition = "center" >
141+ < EuiFlexGroup >
142+ < EuiFlexItem >
143+ < EuiLoadingSpinner size = "xl" />
144+ </ EuiFlexItem >
145+
146+ < EuiFlexItem >
147+ < EuiTitle >
148+ < h2 >
149+ < FormattedMessage
150+ id = "xpack.crossClusterReplication.app.permissionCheckTitle"
151+ defaultMessage = "Checking permissions..."
152+ />
153+ </ h2 >
154+ </ EuiTitle >
155+ </ EuiFlexItem >
156+ </ EuiFlexGroup >
157+ </ EuiPageContent >
158+ ) ;
159+ }
160+
161+ if ( fetchPermissionError ) {
162+ return (
163+ < SectionError
164+ title = { (
165+ < FormattedMessage
166+ id = "xpack.crossClusterReplication.app.permissionCheckErrorTitle"
167+ defaultMessage = "Error checking permissions"
168+ />
169+ ) }
170+ error = { fetchPermissionError }
171+ />
172+ ) ;
173+ }
174+
175+ if ( ! hasPermission ) {
176+ return (
177+ < EuiPageContent horizontalPosition = "center" >
178+ < EuiEmptyPrompt
179+ iconType = "securityApp"
180+ iconColor = { null }
181+ title = {
182+ < h2 >
183+ < FormattedMessage
184+ id = "xpack.crossClusterReplication.app.deniedPermissionTitle"
185+ defaultMessage = "Permission denied"
186+ />
187+ </ h2 > }
188+ body = {
189+ < p >
190+ < FormattedMessage
191+ id = "xpack.crossClusterReplication.app.deniedPermissionDescription"
192+ defaultMessage = "You do not have required permissions ({permissions}) for Cross Cluster Replication."
193+ values = { { permissions : missingPermissions . join ( ', ' ) } }
194+ />
195+ </ p > }
196+ />
197+ </ EuiPageContent >
198+ ) ;
199+ }
200+
201+ return (
202+ < div >
203+ < Switch >
204+ < Redirect exact from = { `${ BASE_PATH } ` } to = { `${ BASE_PATH } /follower_indices` } />
205+ < Route exact path = { `${ BASE_PATH } /auto_follow_patterns/add` } component = { AutoFollowPatternAdd } />
206+ < Route exact path = { `${ BASE_PATH } /auto_follow_patterns/edit/:id` } component = { AutoFollowPatternEdit } />
207+ < Route exact path = { `${ BASE_PATH } /follower_indices/add` } component = { FollowerIndexAdd } />
208+ < Route exact path = { `${ BASE_PATH } /follower_indices/edit/:id` } component = { FollowerIndexEdit } />
209+ < Route exact path = { `${ BASE_PATH } /:section` } component = { CrossClusterReplicationHome } />
210+ </ Switch >
211+ </ div >
212+ ) ;
213+ }
214+ }
215+ ) ;
0 commit comments