Skip to content

Commit

Permalink
[Enterprise Search] Adds app logic file to Workplace Search (elastic#…
Browse files Browse the repository at this point in the history
…76009) (elastic#76040)

* Add new Workplace Search initial data properties

* Add app logic

* Refactor index to match App Search

Adds the easier-to-read ComponentConfigured and ComponentUnconfigured FCs with a ternary in the root compoenent

* Remove ‘Logic’ from interface names

* Extract initial data from WS into interface

This allows for breaking apart the app-specific data and also having an interface to extend in the app_logic file

* Destructuring FTW
  • Loading branch information
scottybollinger authored Aug 26, 2020
1 parent deb3559 commit ee66836
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export const DEFAULT_INITIAL_APP_DATA = {
},
},
workplaceSearch: {
canCreateInvitations: true,
isFederatedAuth: false,
organization: {
name: 'ACME Donuts',
defaultOrgName: 'My Organization',
Expand Down
7 changes: 2 additions & 5 deletions x-pack/plugins/enterprise_search/common/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@
*/

import { IAccount as IAppSearchAccount } from './app_search';
import { IAccount as IWorkplaceSearchAccount, IOrganization } from './workplace_search';
import { IWorkplaceSearchInitialData } from './workplace_search';

export interface IInitialAppData {
readOnlyMode?: boolean;
ilmEnabled?: boolean;
configuredLimits?: IConfiguredLimits;
appSearch?: IAppSearchAccount;
workplaceSearch?: {
organization: IOrganization;
fpAccount: IWorkplaceSearchAccount;
};
workplaceSearch?: IWorkplaceSearchInitialData;
}

export interface IConfiguredLimits {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ export interface IOrganization {
name: string;
defaultOrgName: string;
}

export interface IWorkplaceSearchInitialData {
canCreateInvitations: boolean;
isFederatedAuth: boolean;
organization: IOrganization;
fpAccount: IAccount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { resetContext } from 'kea';

import { DEFAULT_INITIAL_APP_DATA } from '../../../common/__mocks__';
import { AppLogic } from './app_logic';

describe('AppLogic', () => {
beforeEach(() => {
resetContext({});
AppLogic.mount();
});

const DEFAULT_VALUES = {
hasInitialized: false,
};

it('has expected default values', () => {
expect(AppLogic.values).toEqual(DEFAULT_VALUES);
});

describe('initializeAppData()', () => {
it('sets values based on passed props', () => {
AppLogic.actions.initializeAppData(DEFAULT_INITIAL_APP_DATA);

expect(AppLogic.values).toEqual({
hasInitialized: true,
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { kea } from 'kea';

import { IInitialAppData } from '../../../common/types';
import { IWorkplaceSearchInitialData } from '../../../common/types/workplace_search';
import { IKeaLogic } from '../shared/types';

export interface IAppValues extends IWorkplaceSearchInitialData {
hasInitialized: boolean;
}
export interface IAppActions {
initializeAppData(props: IInitialAppData): void;
}

export const AppLogic = kea({
actions: (): IAppActions => ({
initializeAppData: ({ workplaceSearch }) => workplaceSearch,
}),
reducers: () => ({
hasInitialized: [
false,
{
initializeAppData: () => true,
},
],
}),
}) as IKeaLogic<IAppValues, IAppActions>;
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,76 @@ import '../__mocks__/kea.mock';
import React, { useContext } from 'react';
import { Redirect } from 'react-router-dom';
import { shallow } from 'enzyme';
import { useValues } from 'kea';
import { useValues, useActions } from 'kea';

import { Overview } from './views/overview';
import { SetupGuide } from './views/setup_guide';
import { ErrorState } from './views/error_state';
import { Overview } from './views/overview';

import { WorkplaceSearch } from './';
import { WorkplaceSearch, WorkplaceSearchUnconfigured, WorkplaceSearchConfigured } from './';

describe('Workplace Search', () => {
it('redirects to Setup Guide when enterpriseSearchUrl is not set', () => {
(useContext as jest.Mock).mockImplementationOnce(() => ({
config: { host: '' },
}));
describe('WorkplaceSearch', () => {
it('renders WorkplaceSearchUnconfigured when config.host is not set', () => {
(useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } }));
const wrapper = shallow(<WorkplaceSearch />);

expect(wrapper.find(Redirect)).toHaveLength(1);
expect(wrapper.find(Overview)).toHaveLength(0);
expect(wrapper.find(WorkplaceSearchUnconfigured)).toHaveLength(1);
});

it('renders the Overview when enterpriseSearchUrl is set', () => {
(useContext as jest.Mock).mockImplementationOnce(() => ({
config: { host: 'https://foo.bar' },
}));
it('renders WorkplaceSearchConfigured when config.host set', () => {
(useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'some.url' } }));
const wrapper = shallow(<WorkplaceSearch />);

expect(wrapper.find(WorkplaceSearchConfigured)).toHaveLength(1);
});
});

describe('WorkplaceSearchUnconfigured', () => {
it('renders the Setup Guide and redirects to the Setup Guide', () => {
const wrapper = shallow(<WorkplaceSearchUnconfigured />);

expect(wrapper.find(SetupGuide)).toHaveLength(1);
expect(wrapper.find(Redirect)).toHaveLength(1);
});
});

describe('WorkplaceSearchConfigured', () => {
beforeEach(() => {
// Mock resets
(useValues as jest.Mock).mockImplementation(() => ({}));
(useActions as jest.Mock).mockImplementation(() => ({ initializeAppData: () => {} }));
});

it('renders with layout', () => {
const wrapper = shallow(<WorkplaceSearchConfigured />);

expect(wrapper.find(Overview)).toHaveLength(1);
expect(wrapper.find(Redirect)).toHaveLength(0);
});

it('renders ErrorState when the app cannot connect to Enterprise Search', () => {
(useValues as jest.Mock).mockImplementationOnce(() => ({ errorConnecting: true }));
const wrapper = shallow(<WorkplaceSearch />);
it('initializes app data with passed props', () => {
const initializeAppData = jest.fn();
(useActions as jest.Mock).mockImplementation(() => ({ initializeAppData }));

shallow(<WorkplaceSearchConfigured readOnlyMode={true} />);

expect(initializeAppData).toHaveBeenCalledWith({ readOnlyMode: true });
});

it('does not re-initialize app data', () => {
const initializeAppData = jest.fn();
(useActions as jest.Mock).mockImplementation(() => ({ initializeAppData }));
(useValues as jest.Mock).mockImplementation(() => ({ hasInitialized: true }));

shallow(<WorkplaceSearchConfigured />);

expect(initializeAppData).not.toHaveBeenCalled();
});

it('renders ErrorState', () => {
(useValues as jest.Mock).mockImplementation(() => ({ errorConnecting: true }));

const wrapper = shallow(<WorkplaceSearchConfigured />);

expect(wrapper.find(ErrorState).exists()).toBe(true);
expect(wrapper.find(Overview)).toHaveLength(0);
expect(wrapper.find(ErrorState)).toHaveLength(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useContext } from 'react';
import React, { useContext, useEffect } from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import { useValues } from 'kea';
import { useActions, useValues } from 'kea';

import { IInitialAppData } from '../../../common/types';
import { KibanaContext, IKibanaContext } from '../index';
import { HttpLogic, IHttpLogicValues } from '../shared/http';
import { AppLogic, IAppActions, IAppValues } from './app_logic';
import { Layout } from '../shared/layout';
import { WorkplaceSearchNav } from './components/layout/nav';

Expand All @@ -20,21 +21,19 @@ import { SetupGuide } from './views/setup_guide';
import { ErrorState } from './views/error_state';
import { Overview } from './views/overview';

export const WorkplaceSearch: React.FC<IInitialAppData> = () => {
export const WorkplaceSearch: React.FC<IInitialAppData> = (props) => {
const { config } = useContext(KibanaContext) as IKibanaContext;
return !config.host ? <WorkplaceSearchUnconfigured /> : <WorkplaceSearchConfigured {...props} />;
};

export const WorkplaceSearchConfigured: React.FC<IInitialAppData> = (props) => {
const { hasInitialized } = useValues(AppLogic) as IAppValues;
const { initializeAppData } = useActions(AppLogic) as IAppActions;
const { errorConnecting } = useValues(HttpLogic) as IHttpLogicValues;

if (!config.host)
return (
<Switch>
<Route exact path={SETUP_GUIDE_PATH}>
<SetupGuide />
</Route>
<Route>
<Redirect to={SETUP_GUIDE_PATH} />
</Route>
</Switch>
);
useEffect(() => {
if (!hasInitialized) initializeAppData(props);
}, [hasInitialized]);

return (
<Switch>
Expand All @@ -61,3 +60,14 @@ export const WorkplaceSearch: React.FC<IInitialAppData> = () => {
</Switch>
);
};

export const WorkplaceSearchUnconfigured: React.FC = () => (
<Switch>
<Route exact path={SETUP_GUIDE_PATH}>
<SetupGuide />
</Route>
<Route>
<Redirect to={SETUP_GUIDE_PATH} />
</Route>
</Switch>
);
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ describe('callEnterpriseSearchConfigAPI', () => {
onboarding_complete: true,
},
workplace_search: {
can_create_invitations: true,
is_federated_auth: false,
organization: {
name: 'ACME Donuts',
default_org_name: 'My Organization',
Expand Down Expand Up @@ -136,6 +138,8 @@ describe('callEnterpriseSearchConfigAPI', () => {
},
},
workplaceSearch: {
canCreateInvitations: false,
isFederatedAuth: false,
organization: {
name: undefined,
defaultOrgName: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export const callEnterpriseSearchConfigAPI = async ({
},
},
workplaceSearch: {
canCreateInvitations: !!data?.settings?.workplace_search?.can_create_invitations,
isFederatedAuth: !!data?.settings?.workplace_search?.is_federated_auth,
organization: {
name: data?.settings?.workplace_search?.organization?.name,
defaultOrgName: data?.settings?.workplace_search?.organization?.default_org_name,
Expand Down

0 comments on commit ee66836

Please sign in to comment.