mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[8.8][Backport][Enterprise Search] Move Search Applications to Applications module (#156725)
## Summary Backport of #155805
This commit is contained in:
parent
e02d94af2d
commit
c6a65ad0f0
93 changed files with 364 additions and 104 deletions
|
@ -136,6 +136,7 @@ export const applicationUsageSchema = {
|
|||
enterpriseSearch: commonSchema,
|
||||
enterpriseSearchContent: commonSchema,
|
||||
enterpriseSearchAnalytics: commonSchema,
|
||||
enterpriseSearchApplications: commonSchema,
|
||||
elasticsearch: commonSchema,
|
||||
appSearch: commonSchema,
|
||||
workplaceSearch: commonSchema,
|
||||
|
|
|
@ -2229,6 +2229,137 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"enterpriseSearchApplications": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "Always `main`"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen over the last 90 days"
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application being tracked"
|
||||
}
|
||||
},
|
||||
"viewId": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The application view being tracked"
|
||||
}
|
||||
},
|
||||
"clicks_total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the application sub view since we started counting them"
|
||||
}
|
||||
},
|
||||
"clicks_7_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"clicks_30_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"clicks_90_days": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "General number of clicks in the active application sub view over the last 90 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_total": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application sub view is active and on-screen since we started counting them."
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_7_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 7 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_30_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 30 days"
|
||||
}
|
||||
},
|
||||
"minutes_on_screen_90_days": {
|
||||
"type": "float",
|
||||
"_meta": {
|
||||
"description": "Minutes the application is active and on-screen active application sub view over the last 90 days"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"elasticsearch": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
|
|
|
@ -118,10 +118,17 @@ export const SEARCH_EXPERIENCES_PLUGIN = {
|
|||
WORKPLACE_SEARCH_TUTORIAL_URL: 'https://docs.elastic.co/search-ui/tutorials/workplace-search',
|
||||
};
|
||||
|
||||
export const ENGINES_PLUGIN = {
|
||||
NAV_TITLE: i18n.translate('xpack.enterpriseSearch.applications.navTitle', {
|
||||
export const APPLICATIONS_PLUGIN = {
|
||||
ID: 'enterpriseSearchApplications',
|
||||
LOGO: 'logoEnterpriseSearch',
|
||||
NAME: i18n.translate('xpack.enterpriseSearch.applications.productName', {
|
||||
defaultMessage: 'Applications',
|
||||
}),
|
||||
NAV_TITLE: i18n.translate('xpack.enterpriseSearch.applications.navTitle', {
|
||||
defaultMessage: 'Search Applications',
|
||||
}),
|
||||
SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/',
|
||||
URL: '/app/enterprise_search/applications',
|
||||
};
|
||||
|
||||
export const LICENSED_SUPPORT_URL = 'https://support.elastic.co';
|
||||
|
|
|
@ -15,7 +15,7 @@ import { shallow } from 'enzyme';
|
|||
|
||||
import { AnalyticsCollection } from '../../../../../common/types/analytics';
|
||||
|
||||
import { LicensingCallout } from '../../../enterprise_search_content/components/shared/licensing_callout/licensing_callout';
|
||||
import { LicensingCallout } from '../../../shared/licensing_callout/licensing_callout';
|
||||
|
||||
import { AnalyticsCollectionTable } from './analytics_collection_table';
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ import { EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../shared/licensing';
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../../enterprise_search_content/components/shared/licensing_callout/licensing_callout';
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../shared/licensing';
|
||||
} from '../../../shared/licensing_callout/licensing_callout';
|
||||
import { AddAnalyticsCollection } from '../add_analytics_collections/add_analytics_collection';
|
||||
|
||||
import { EnterpriseSearchAnalyticsPageTemplate } from '../layout/page_template';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { kea, MakeLogicType } from 'kea';
|
||||
|
||||
import { Status } from '../../../../../../common/types/api';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../enterprise_search_content/api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
|
||||
interface EngineAPIActions {
|
||||
apiError: typeof GenerateEngineApiKeyLogic.actions.apiError;
|
|
@ -8,7 +8,7 @@
|
|||
import { LogicMounter } from '../../../../../__mocks__/kea_logic';
|
||||
|
||||
import { Status } from '../../../../../../../common/types/api';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../../enterprise_search_content/api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
|
||||
import { GenerateApiKeyModalLogic } from './generate_engine_api_key_modal.logic';
|
||||
|
|
@ -9,7 +9,7 @@ import { kea, MakeLogicType } from 'kea';
|
|||
|
||||
import { Status } from '../../../../../../../common/types/api';
|
||||
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../../enterprise_search_content/api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
|
||||
interface GenerateApiKeyModalActions {
|
||||
setKeyName(keyName: string): { keyName: string };
|
|
@ -31,7 +31,7 @@ import {
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
import { GenerateEngineApiKeyLogic } from '../../../../../enterprise_search_content/api/generate_engine_api_key/generate_engine_api_key_logic';
|
||||
|
||||
import { GenerateApiKeyModalLogic } from './generate_engine_api_key_modal.logic';
|
||||
|
|
@ -39,7 +39,7 @@ describe('EngineError', () => {
|
|||
expect(wrapper.find(ErrorStatePrompt)).toHaveLength(0);
|
||||
|
||||
const notFound = wrapper.find(NotFoundPrompt);
|
||||
expect(notFound.prop('backToLink')).toEqual('/engines');
|
||||
expect(notFound.prop('backToLink')).toEqual('/search_applications');
|
||||
expect(notFound.prop('backToContent')).toEqual('Back to Search Applications');
|
||||
|
||||
const telemetry = wrapper.find(SendEnterpriseSearchTelemetry);
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../../common/constants';
|
||||
import { HttpError } from '../../../../../common/types/api';
|
||||
|
||||
import { ErrorStatePrompt } from '../../../shared/error_state';
|
||||
|
@ -34,7 +34,7 @@ export const EngineError: React.FC<{ error?: HttpError; notFound?: boolean }> =
|
|||
}
|
||||
)}
|
||||
backToLink={ENGINES_PATH}
|
||||
productSupportUrl={ENTERPRISE_SEARCH_CONTENT_PLUGIN.SUPPORT_URL}
|
||||
productSupportUrl={APPLICATIONS_PLUGIN.SUPPORT_URL}
|
||||
/>
|
||||
</>
|
||||
);
|
|
@ -22,8 +22,10 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
|
||||
import { EnterpriseSearchEngineIndex } from '../../../../../common/types/engines';
|
||||
|
||||
import { SEARCH_INDEX_PATH } from '../../../enterprise_search_content/routes';
|
||||
import { CANCEL_BUTTON_LABEL } from '../../../shared/constants';
|
||||
import { indexHealthToHealthColor } from '../../../shared/constants/health_colors';
|
||||
import { generateEncodedPath } from '../../../shared/encode_path_params';
|
||||
|
@ -31,8 +33,6 @@ import { KibanaLogic } from '../../../shared/kibana';
|
|||
import { EuiLinkTo } from '../../../shared/react_router_helpers';
|
||||
import { TelemetryLogic } from '../../../shared/telemetry/telemetry_logic';
|
||||
|
||||
import { SEARCH_INDEX_PATH } from '../../routes';
|
||||
|
||||
import { EngineIndicesLogic } from './engine_indices_logic';
|
||||
|
||||
export const EngineIndices: React.FC = () => {
|
||||
|
@ -87,7 +87,10 @@ export const EngineIndices: React.FC = () => {
|
|||
) : (
|
||||
<EuiLinkTo
|
||||
data-test-subj="engine-index-link"
|
||||
to={generateEncodedPath(SEARCH_INDEX_PATH, { indexName: name })}
|
||||
to={`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/${generateEncodedPath(SEARCH_INDEX_PATH, {
|
||||
indexName: name,
|
||||
})}`}
|
||||
shouldNotCreateHref
|
||||
>
|
||||
{name}
|
||||
</EuiLinkTo>
|
|
@ -36,13 +36,14 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import { FieldIcon } from '@kbn/react-field';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
|
||||
import { SchemaField } from '../../../../../common/types/engines';
|
||||
|
||||
import { SEARCH_INDEX_TAB_PATH } from '../../../enterprise_search_content/routes';
|
||||
import { docLinks } from '../../../shared/doc_links';
|
||||
import { generateEncodedPath } from '../../../shared/encode_path_params';
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
import { EuiLinkTo } from '../../../shared/react_router_helpers';
|
||||
import { SEARCH_INDEX_TAB_PATH } from '../../routes';
|
||||
|
||||
import { EngineIndicesLogic } from './engine_indices_logic';
|
||||
|
||||
|
@ -63,10 +64,10 @@ const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaFiel
|
|||
),
|
||||
render: (name: string) => (
|
||||
<EuiLinkTo
|
||||
to={generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
|
||||
to={`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
|
||||
indexName: name,
|
||||
tabId: 'index_mappings',
|
||||
})}
|
||||
})}`}
|
||||
>
|
||||
{name}
|
||||
</EuiLinkTo>
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
.searchApplicationHeaderBackgroundColor {
|
||||
background-color: $euiColorLightestShade;
|
||||
|
||||
}
|
|
@ -13,10 +13,9 @@ import { mount, shallow } from 'enzyme';
|
|||
|
||||
import { Status } from '../../../../../common/types/api';
|
||||
|
||||
import { LicensingCallout } from '../../../shared/licensing_callout/licensing_callout';
|
||||
import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template';
|
||||
|
||||
import { LicensingCallout } from '../shared/licensing_callout/licensing_callout';
|
||||
|
||||
import { EmptyEnginesPrompt } from './components/empty_engines_prompt';
|
||||
import { EnginesListTable } from './components/tables/engines_table';
|
||||
import { EnginesList, CreateEngineButton } from './engines_list';
|
|
@ -31,11 +31,14 @@ import { docLinks } from '../../../shared/doc_links';
|
|||
|
||||
import { KibanaLogic } from '../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../shared/licensing';
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../../shared/licensing_callout/licensing_callout';
|
||||
|
||||
import { ENGINES_PATH, ENGINE_CREATION_PATH } from '../../routes';
|
||||
import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_template';
|
||||
|
||||
import { LicensingCallout, LICENSING_FEATURE } from '../shared/licensing_callout/licensing_callout';
|
||||
|
||||
import { EmptyEnginesPrompt } from './components/empty_engines_prompt';
|
||||
import { EnginesListTable } from './components/tables/engines_table';
|
||||
import { CreateEngineFlyout } from './create_engine_flyout';
|
||||
|
@ -161,11 +164,7 @@ export const EnginesList: React.FC<ListProps> = ({ createEngineFlyoutOpen }) =>
|
|||
<EngineListIndicesFlyout />
|
||||
{createEngineFlyoutOpen && <CreateEngineFlyout onClose={() => navigateToUrl(ENGINES_PATH)} />}
|
||||
<EnterpriseSearchEnginesPageTemplate
|
||||
pageChrome={[
|
||||
i18n.translate('xpack.enterpriseSearch.content.searchApplications.breadcrumb', {
|
||||
defaultMessage: 'Search Applications',
|
||||
}),
|
||||
]}
|
||||
pageChrome={[]}
|
||||
pageHeader={{
|
||||
description: (
|
||||
<FormattedMessage
|
|
@ -25,14 +25,15 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
|
||||
import { Status } from '../../../../../common/types/api';
|
||||
|
||||
import { EnterpriseSearchEngineIndex } from '../../../../../common/types/engines';
|
||||
|
||||
import { SEARCH_INDEX_PATH } from '../../../enterprise_search_content/routes';
|
||||
import { healthColorsMap } from '../../../shared/constants/health_colors';
|
||||
import { generateEncodedPath } from '../../../shared/encode_path_params';
|
||||
import { EuiLinkTo } from '../../../shared/react_router_helpers';
|
||||
import { SEARCH_INDEX_PATH } from '../../routes';
|
||||
|
||||
import { EngineError } from '../engine/engine_error';
|
||||
|
||||
|
@ -66,7 +67,10 @@ export const EngineListIndicesFlyout: React.FC = () => {
|
|||
<EuiLinkTo
|
||||
data-test-subj="engine-index-link"
|
||||
data-telemetry-id="entSearchContent-engines-list-viewIndex"
|
||||
to={generateEncodedPath(SEARCH_INDEX_PATH, { indexName })}
|
||||
to={`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/${generateEncodedPath(SEARCH_INDEX_PATH, {
|
||||
indexName,
|
||||
})}`}
|
||||
shouldNotCreateHref
|
||||
>
|
||||
{indexName}
|
||||
</EuiLinkTo>
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
|
||||
import { SetEnterpriseSearchEnginesChrome } from '../../../shared/kibana_chrome';
|
||||
import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout';
|
||||
import { useEnterpriseSearchEngineNav } from '../../../shared/layout';
|
||||
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
|
||||
|
||||
export type EnterpriseSearchEnginesPageTemplateProps = PageTemplateProps & {
|
||||
engineName?: string;
|
||||
};
|
||||
|
||||
export const EnterpriseSearchEnginesPageTemplate: React.FC<
|
||||
EnterpriseSearchEnginesPageTemplateProps
|
||||
> = ({ children, pageChrome, pageViewTelemetry, engineName, ...pageTemplateProps }) => {
|
||||
const navItems = useEnterpriseSearchEngineNav(engineName, pageTemplateProps.isEmptyState);
|
||||
return (
|
||||
<EnterpriseSearchPageTemplateWrapper
|
||||
{...pageTemplateProps}
|
||||
solutionNav={{
|
||||
items: navItems,
|
||||
name: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME,
|
||||
}}
|
||||
restrictWidth
|
||||
setPageChrome={pageChrome && <SetEnterpriseSearchEnginesChrome trail={pageChrome} />}
|
||||
>
|
||||
{pageViewTelemetry && (
|
||||
<SendEnterpriseSearchTelemetry action="viewed" metric={pageViewTelemetry} />
|
||||
)}
|
||||
{children}
|
||||
</EnterpriseSearchPageTemplateWrapper>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../common/constants';
|
||||
import { PageTemplateProps } from '../../shared/layout';
|
||||
import { NotFoundPrompt } from '../../shared/not_found';
|
||||
import { SendEnterpriseSearchTelemetry } from '../../shared/telemetry';
|
||||
|
||||
import { EnterpriseSearchEnginesPageTemplate } from './layout/engines_page_template';
|
||||
|
||||
export const NotFound: React.FC<PageTemplateProps> = ({ pageChrome = [] }) => {
|
||||
return (
|
||||
<EnterpriseSearchEnginesPageTemplate pageChrome={[...pageChrome, '404']} customPageSections>
|
||||
<SendEnterpriseSearchTelemetry action="error" metric="not_found" />
|
||||
<NotFoundPrompt productSupportUrl={APPLICATIONS_PLUGIN.SUPPORT_URL} />
|
||||
</EnterpriseSearchEnginesPageTemplate>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Redirect, Switch } from 'react-router-dom';
|
||||
|
||||
import { Route } from '@kbn/shared-ux-router';
|
||||
|
||||
import { EnginesRouter } from './components/engines/engines_router';
|
||||
import { NotFound } from './components/not_found';
|
||||
import { ROOT_PATH, ENGINES_PATH } from './routes';
|
||||
|
||||
export const Applications = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Redirect exact from={ROOT_PATH} to={ENGINES_PATH} />
|
||||
<Route path={ENGINES_PATH}>
|
||||
<EnginesRouter />
|
||||
</Route>
|
||||
<Route>
|
||||
<NotFound />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const ROOT_PATH = '/';
|
||||
|
||||
export const ENGINES_PATH = `${ROOT_PATH}search_applications`;
|
||||
|
||||
export enum EngineViewTabs {
|
||||
PREVIEW = 'preview',
|
||||
CONTENT = 'content',
|
||||
CONNECT = 'connect',
|
||||
}
|
||||
export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`;
|
||||
export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`;
|
||||
export const ENGINE_TAB_PATH = `${ENGINE_PATH}/:tabId`;
|
||||
export const SEARCH_APPLICATION_CONNECT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONNECT}/:connectTabId`;
|
||||
export enum SearchApplicationConnectTabs {
|
||||
API = 'api',
|
||||
}
|
||||
export const SEARCH_APPLICATION_CONTENT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONTENT}/:contentTabId`;
|
||||
export enum SearchApplicationContentTabs {
|
||||
INDICES = 'indices',
|
||||
SCHEMA = 'schema',
|
||||
}
|
|
@ -17,14 +17,14 @@ import { Status } from '../../../../../../common/types/api';
|
|||
import { docLinks } from '../../../../shared/doc_links';
|
||||
import { KibanaLogic } from '../../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../../shared/licensing';
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../../../shared/licensing_callout/licensing_callout';
|
||||
import { AddConnectorApiLogic } from '../../../api/connector/add_connector_api_logic';
|
||||
|
||||
import { FetchCloudHealthApiLogic } from '../../../api/stats/fetch_cloud_health_api_logic';
|
||||
import { NATIVE_CONNECTORS } from '../../search_index/connector/constants';
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../shared/licensing_callout/licensing_callout';
|
||||
import { NewSearchIndexLogic } from '../new_search_index_logic';
|
||||
import { NewSearchIndexTemplate } from '../new_search_index_template';
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ import { Status } from '../../../../../../common/types/api';
|
|||
import { docLinks } from '../../../../shared/doc_links';
|
||||
import { KibanaLogic } from '../../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../../shared/licensing';
|
||||
import { CreateCrawlerIndexApiLogic } from '../../../api/crawler/create_crawler_index_api_logic';
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../shared/licensing_callout/licensing_callout';
|
||||
} from '../../../../shared/licensing_callout/licensing_callout';
|
||||
import { CreateCrawlerIndexApiLogic } from '../../../api/crawler/create_crawler_index_api_logic';
|
||||
import { NewSearchIndexTemplate } from '../new_search_index_template';
|
||||
|
||||
import { MethodCrawlerLogic } from './method_crawler_logic';
|
||||
|
|
|
@ -11,11 +11,11 @@ import { EuiContextMenuItem, EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants';
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../../../../common/constants';
|
||||
import { ENGINE_CREATION_PATH } from '../../../../../applications/routes';
|
||||
import { ESINDEX_QUERY_PARAMETER } from '../../../../../shared/constants';
|
||||
import { generateEncodedPath } from '../../../../../shared/encode_path_params';
|
||||
import { KibanaLogic } from '../../../../../shared/kibana';
|
||||
import { ENGINE_CREATION_PATH } from '../../../../routes';
|
||||
|
||||
export interface CreateEngineMenuItemProps {
|
||||
indexName?: string;
|
||||
|
@ -29,9 +29,9 @@ export const CreateEngineMenuItem: React.FC<CreateEngineMenuItemProps> = ({
|
|||
isHiddenIndex,
|
||||
}) => {
|
||||
const searchApplicationCreationPath = !indexName
|
||||
? `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}`
|
||||
? `${APPLICATIONS_PLUGIN.URL}${ENGINE_CREATION_PATH}`
|
||||
: generateEncodedPath(
|
||||
`${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`,
|
||||
`${APPLICATIONS_PLUGIN.URL}${ENGINE_CREATION_PATH}?:indexKey=:indexName`,
|
||||
{
|
||||
indexKey: ESINDEX_QUERY_PARAMETER,
|
||||
indexName,
|
||||
|
|
|
@ -20,11 +20,10 @@ import {
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../../../common/constants';
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../../../../common/constants';
|
||||
import { ENGINES_PATH } from '../../../../../applications/routes';
|
||||
import { KibanaLogic } from '../../../../../shared/kibana';
|
||||
|
||||
import { ENGINES_PATH } from '../../../../routes';
|
||||
|
||||
import { CreateEngineMenuItem } from './create_engine_menu_item';
|
||||
import { SearchEnginesPopoverLogic } from './search_engines_popover_logic';
|
||||
|
||||
|
@ -66,12 +65,9 @@ export const SearchEnginesPopover: React.FC<SearchEnginesPopoverProps> = ({
|
|||
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-searchEngines-viewEngines`}
|
||||
icon="eye"
|
||||
onClick={() => {
|
||||
KibanaLogic.values.navigateToUrl(
|
||||
ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + ENGINES_PATH,
|
||||
{
|
||||
shouldNotCreateHref: true,
|
||||
}
|
||||
);
|
||||
KibanaLogic.values.navigateToUrl(APPLICATIONS_PLUGIN.URL + ENGINES_PATH, {
|
||||
shouldNotCreateHref: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<EuiText>
|
||||
|
|
|
@ -14,7 +14,7 @@ import { shallow } from 'enzyme';
|
|||
|
||||
import { EuiCallOut, EuiButton } from '@elastic/eui';
|
||||
|
||||
import { LicensingCallout } from '../../../shared/licensing_callout/licensing_callout';
|
||||
import { LicensingCallout } from '../../../../../shared/licensing_callout/licensing_callout';
|
||||
|
||||
import { CopyAndCustomizePipelinePanel } from './customize_pipeline_item';
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ import { Status } from '../../../../../../../common/types/api';
|
|||
|
||||
import { KibanaLogic } from '../../../../../shared/kibana';
|
||||
import { LicensingLogic } from '../../../../../shared/licensing';
|
||||
import { CreateCustomPipelineApiLogic } from '../../../../api/index/create_custom_pipeline_api_logic';
|
||||
|
||||
import {
|
||||
LicensingCallout,
|
||||
LICENSING_FEATURE,
|
||||
} from '../../../shared/licensing_callout/licensing_callout';
|
||||
} from '../../../../../shared/licensing_callout/licensing_callout';
|
||||
import { CreateCustomPipelineApiLogic } from '../../../../api/index/create_custom_pipeline_api_logic';
|
||||
|
||||
import { IndexViewLogic } from '../../index_view_logic';
|
||||
|
||||
import { PipelinesLogic } from '../pipelines_logic';
|
||||
|
|
|
@ -19,18 +19,11 @@ import { HttpLogic } from '../shared/http';
|
|||
import { KibanaLogic } from '../shared/kibana';
|
||||
import { VersionMismatchPage } from '../shared/version_mismatch';
|
||||
|
||||
import { EnginesRouter } from './components/engines/engines_router';
|
||||
import { ErrorConnecting } from './components/error_connecting';
|
||||
import { NotFound } from './components/not_found';
|
||||
import { SearchIndicesRouter } from './components/search_indices';
|
||||
import { Settings } from './components/settings';
|
||||
import {
|
||||
SETUP_GUIDE_PATH,
|
||||
ROOT_PATH,
|
||||
SEARCH_INDICES_PATH,
|
||||
SETTINGS_PATH,
|
||||
ENGINES_PATH,
|
||||
} from './routes';
|
||||
import { SETUP_GUIDE_PATH, ROOT_PATH, SEARCH_INDICES_PATH, SETTINGS_PATH } from './routes';
|
||||
|
||||
export const EnterpriseSearchContent: React.FC<InitialAppData> = (props) => {
|
||||
const { config } = useValues(KibanaLogic);
|
||||
|
@ -83,9 +76,6 @@ export const EnterpriseSearchContentConfigured: React.FC<Required<InitialAppData
|
|||
<Route path={SETTINGS_PATH}>
|
||||
<Settings />
|
||||
</Route>
|
||||
<Route path={ENGINES_PATH}>
|
||||
<EnginesRouter />
|
||||
</Route>
|
||||
<Route>
|
||||
<NotFound />
|
||||
</Route>
|
||||
|
|
|
@ -25,25 +25,5 @@ export const SEARCH_INDEX_TAB_DETAIL_PATH = `${SEARCH_INDEX_TAB_PATH}/:detailId`
|
|||
export const SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/domain_management/:domainId`;
|
||||
export const OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/crawler/domains/:domainId`;
|
||||
|
||||
export const ENGINES_PATH = `${ROOT_PATH}engines`;
|
||||
|
||||
export enum EngineViewTabs {
|
||||
PREVIEW = 'preview',
|
||||
CONTENT = 'content',
|
||||
CONNECT = 'connect',
|
||||
}
|
||||
export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`;
|
||||
export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`;
|
||||
export const ENGINE_TAB_PATH = `${ENGINE_PATH}/:tabId`;
|
||||
export const SEARCH_APPLICATION_CONNECT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONNECT}/:connectTabId`;
|
||||
export enum SearchApplicationConnectTabs {
|
||||
API = 'api',
|
||||
}
|
||||
export const SEARCH_APPLICATION_CONTENT_PATH = `${ENGINE_PATH}/${EngineViewTabs.CONTENT}/:contentTabId`;
|
||||
export enum SearchApplicationContentTabs {
|
||||
INDICES = 'indices',
|
||||
SCHEMA = 'schema',
|
||||
}
|
||||
|
||||
export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models';
|
||||
export const ML_NOTIFICATIONS_PATH = '/app/ml/notifications';
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useEffect } from 'react';
|
|||
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import { ENGINES_PLUGIN } from '../../../../common/constants';
|
||||
import { APPLICATIONS_PLUGIN } from '../../../../common/constants';
|
||||
|
||||
import { KibanaLogic } from '../kibana';
|
||||
|
||||
|
@ -179,7 +179,7 @@ export const SetEnterpriseSearchEnginesChrome: React.FC<SetChromeProps> = ({ tra
|
|||
const docTitle = appSearchTitle(title);
|
||||
|
||||
const breadcrumbs = useEnterpriseSearchEnginesBreadcrumbs(
|
||||
useGenerateBreadcrumbs([ENGINES_PLUGIN.NAV_TITLE, ...trail])
|
||||
useGenerateBreadcrumbs([APPLICATIONS_PLUGIN.NAV_TITLE, ...trail])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -77,9 +77,9 @@ describe('useEnterpriseSearchContentNav', () => {
|
|||
name: 'Applications',
|
||||
items: [
|
||||
{
|
||||
href: '/app/enterprise_search/applications',
|
||||
id: 'searchApplications',
|
||||
name: 'Search Applications',
|
||||
href: '/app/enterprise_search/content/engines',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/analytics',
|
||||
|
@ -228,9 +228,9 @@ describe('useEnterpriseSearchEngineNav', () => {
|
|||
name: 'Applications',
|
||||
items: [
|
||||
{
|
||||
href: '/app/enterprise_search/applications',
|
||||
id: 'searchApplications',
|
||||
name: 'Search Applications',
|
||||
href: '/app/enterprise_search/content/engines',
|
||||
},
|
||||
{
|
||||
href: '/app/enterprise_search/analytics',
|
||||
|
@ -281,21 +281,21 @@ describe('useEnterpriseSearchEngineNav', () => {
|
|||
// @ts-ignore
|
||||
const engineItem: EuiSideNavItemType<unknown> = enginesItem!.items[0];
|
||||
expect(engineItem).toEqual({
|
||||
href: `/app/enterprise_search/content/engines/${engineName}`,
|
||||
href: `/app/enterprise_search/applications/search_applications/${engineName}`,
|
||||
id: 'engineId',
|
||||
items: [
|
||||
{
|
||||
href: `/app/enterprise_search/content/engines/${engineName}/preview`,
|
||||
href: `/app/enterprise_search/applications/search_applications/${engineName}/preview`,
|
||||
id: 'enterpriseSearchEnginePreview',
|
||||
name: 'Search Preview',
|
||||
},
|
||||
{
|
||||
href: `/app/enterprise_search/content/engines/${engineName}/content`,
|
||||
href: `/app/enterprise_search/applications/search_applications/${engineName}/content`,
|
||||
id: 'enterpriseSearchApplicationsContent',
|
||||
name: 'Content',
|
||||
},
|
||||
{
|
||||
href: `/app/enterprise_search/content/engines/${engineName}/connect`,
|
||||
href: `/app/enterprise_search/applications/search_applications/${engineName}/connect`,
|
||||
id: 'enterpriseSearchApplicationConnect',
|
||||
name: 'Connect',
|
||||
},
|
||||
|
@ -327,7 +327,7 @@ describe('useEnterpriseSearchEngineNav', () => {
|
|||
// @ts-ignore
|
||||
const engineItem: EuiSideNavItemType<unknown> = enginesItem!.items[0];
|
||||
expect(engineItem).toEqual({
|
||||
href: `/app/enterprise_search/content/engines/${engineName}`,
|
||||
href: `/app/enterprise_search/applications/search_applications/${engineName}`,
|
||||
id: 'engineId',
|
||||
name: engineName,
|
||||
});
|
||||
|
@ -369,7 +369,7 @@ describe('useEnterpriseSearchAnalyticsNav', () => {
|
|||
name: 'Applications',
|
||||
items: [
|
||||
{
|
||||
href: '/app/enterprise_search/content/engines',
|
||||
href: '/app/enterprise_search/applications',
|
||||
id: 'searchApplications',
|
||||
name: 'Search Applications',
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import {
|
||||
ANALYTICS_PLUGIN,
|
||||
APPLICATIONS_PLUGIN,
|
||||
APP_SEARCH_PLUGIN,
|
||||
ELASTICSEARCH_PLUGIN,
|
||||
ENTERPRISE_SEARCH_CONTENT_PLUGIN,
|
||||
|
@ -19,12 +20,8 @@ import {
|
|||
SEARCH_EXPERIENCES_PLUGIN,
|
||||
WORKPLACE_SEARCH_PLUGIN,
|
||||
} from '../../../../common/constants';
|
||||
import {
|
||||
ENGINES_PATH,
|
||||
SEARCH_INDICES_PATH,
|
||||
SETTINGS_PATH,
|
||||
EngineViewTabs,
|
||||
} from '../../enterprise_search_content/routes';
|
||||
import { ENGINES_PATH, EngineViewTabs } from '../../applications/routes';
|
||||
import { SEARCH_INDICES_PATH, SETTINGS_PATH } from '../../enterprise_search_content/routes';
|
||||
import { KibanaLogic } from '../kibana';
|
||||
|
||||
import { generateNavLink } from './nav_link_helpers';
|
||||
|
@ -109,7 +106,7 @@ export const useEnterpriseSearchNav = () => {
|
|||
}),
|
||||
...generateNavLink({
|
||||
shouldNotCreateHref: true,
|
||||
to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + ENGINES_PATH,
|
||||
to: APPLICATIONS_PLUGIN.URL,
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
@ -180,7 +177,7 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?:
|
|||
const enginesItem = applicationsItem.items?.find((item) => item.id === 'searchApplications');
|
||||
if (!enginesItem || enginesItem.id !== 'searchApplications') return navItems;
|
||||
|
||||
const enginePath = `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}${ENGINES_PATH}/${engineName}`;
|
||||
const enginePath = `${APPLICATIONS_PLUGIN.URL}${ENGINES_PATH}/${engineName}`;
|
||||
|
||||
enginesItem.items = !isEmptyState
|
||||
? [
|
||||
|
|
|
@ -10,7 +10,7 @@ import React from 'react';
|
|||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { docLinks } from '../../../../shared/doc_links/doc_links';
|
||||
import { docLinks } from '../doc_links/doc_links';
|
||||
|
||||
export enum LICENSING_FEATURE {
|
||||
NATIVE_CONNECTOR = 'nativeConnector',
|
|
@ -26,6 +26,7 @@ import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/p
|
|||
|
||||
import {
|
||||
ANALYTICS_PLUGIN,
|
||||
APPLICATIONS_PLUGIN,
|
||||
APP_SEARCH_PLUGIN,
|
||||
ELASTICSEARCH_PLUGIN,
|
||||
ENTERPRISE_SEARCH_CONTENT_PLUGIN,
|
||||
|
@ -117,6 +118,29 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
},
|
||||
});
|
||||
|
||||
core.application.register({
|
||||
appRoute: APPLICATIONS_PLUGIN.URL,
|
||||
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
|
||||
euiIconType: APPLICATIONS_PLUGIN.LOGO,
|
||||
id: APPLICATIONS_PLUGIN.ID,
|
||||
navLinkStatus: AppNavLinkStatus.default,
|
||||
searchable: true,
|
||||
title: APPLICATIONS_PLUGIN.NAV_TITLE,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const kibanaDeps = await this.getKibanaDeps(core, params, cloud);
|
||||
const { chrome, http } = kibanaDeps.core;
|
||||
chrome.docTitle.change(ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME);
|
||||
|
||||
await this.getInitialData(http);
|
||||
const pluginData = this.getPluginData();
|
||||
|
||||
const { renderApp } = await import('./applications');
|
||||
const { Applications } = await import('./applications/applications');
|
||||
|
||||
return renderApp(Applications, kibanaDeps, pluginData);
|
||||
},
|
||||
});
|
||||
|
||||
core.application.register({
|
||||
id: ANALYTICS_PLUGIN.ID,
|
||||
title: ANALYTICS_PLUGIN.NAME,
|
||||
|
|
|
@ -184,6 +184,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
enterpriseSearch: showEnterpriseSearch,
|
||||
enterpriseSearchContent: showEnterpriseSearch,
|
||||
enterpriseSearchAnalytics: showEnterpriseSearch,
|
||||
enterpriseSearchApplications: showEnterpriseSearch,
|
||||
elasticsearch: showEnterpriseSearch,
|
||||
appSearch: hasAppSearchAccess && config.canDeployEntSearch,
|
||||
workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch,
|
||||
|
@ -193,6 +194,7 @@ export class EnterpriseSearchPlugin implements Plugin {
|
|||
enterpriseSearch: showEnterpriseSearch,
|
||||
enterpriseSearchContent: showEnterpriseSearch,
|
||||
enterpriseSearchAnalytics: showEnterpriseSearch,
|
||||
enterpriseSearchApplications: showEnterpriseSearch,
|
||||
elasticsearch: showEnterpriseSearch,
|
||||
appSearch: hasAppSearchAccess && config.canDeployEntSearch,
|
||||
workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch,
|
||||
|
|
|
@ -66,6 +66,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
@ -93,6 +94,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
|
|
@ -53,6 +53,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'appSearch',
|
||||
'workplaceSearch'
|
||||
)
|
||||
|
@ -69,6 +70,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
'guidedOnboardingFeature'
|
||||
|
|
|
@ -30,6 +30,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'elasticsearch',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
|
|
|
@ -22,6 +22,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
|
|||
'enterpriseSearch',
|
||||
'enterpriseSearchContent',
|
||||
'enterpriseSearchAnalytics',
|
||||
'enterpriseSearchApplications',
|
||||
'appSearch',
|
||||
'workplaceSearch',
|
||||
];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue