From 0a72e8d8b897b1adbd8be30995c08e26b2d2232b Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Mon, 30 Nov 2020 13:01:59 -0600 Subject: [PATCH 01/15] Initial copy/paste of component tree Only does linting changes and: - lodash imports - Replace unescaped apostrophes with ' - Fix ternary function call to if block: if (isAdding) { actions.onSchemaSetFormErrors(errors); } else { actions.onSchemaSetError({ flashMessages: { error: errors } }); } --- .../components/schema/schema.tsx | 156 +++++++- .../schema/schema_change_errors.tsx | 39 +- .../components/schema/schema_fields_table.tsx | 63 +++ .../components/schema/schema_logic.ts | 358 ++++++++++++++++++ 4 files changed, 612 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index 55f1e1e03b2db..abef98f2235d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -4,6 +4,158 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect } from 'react'; -export const Schema: React.FC = () => <>Schema Placeholder; +import { useActions, useValues } from 'kea'; + +import routes from 'workplace_search/routes'; +import { getReindexJobRoute } from 'workplace_search/utils/routePaths'; + +import { + EuiButton, + EuiButtonEmpty, + EuiEmptyPrompt, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiPanel, +} from '@elastic/eui'; + +import { AppLogic } from 'workplace_search/App/AppLogic'; +import { Loading, ViewContentHeader } from 'workplace_search/components'; + +import FlashMessages from 'shared/components/FlashMessages'; +import IndexingStatus from 'shared/components/IndexingStatus'; +import { SchemaAddFieldModal } from 'shared/components/Schema'; + +import { SchemaFieldsTable } from './SchemaFieldsTable'; +import { SchemaLogic } from './SchemaLogic'; + +export const Schema: React.FC = () => { + const { + initializeSchema, + onIndexingComplete, + addNewField, + updateFields, + openAddFieldModal, + closeAddFieldModal, + setFilterValue, + } = useActions(SchemaLogic); + + const { + sourceId, + activeSchema, + filterValue, + showAddFieldModal, + addFieldFormErrors, + mostRecentIndexJob, + formUnchanged, + flashMessages, + dataLoading, + } = useValues(SchemaLogic); + + const { isOrganization } = useValues(AppLogic); + + useEffect(() => { + initializeSchema(); + }, []); + + if (dataLoading) return ; + + const hasSchemaFields = Object.keys(activeSchema).length > 0; + const { isActive, hasErrors, percentageComplete } = mostRecentIndexJob; + + const addFieldButton = ( + + Add Field + + ); + const getStatusPath = isOrganization + ? routes.statusFritoPieOrganizationContentSourceReindexJobPath + : routes.statusFritoPieAccountContentSourceReindexJobPath; + + return ( + <> + +
+ {!!flashMessages && } + {(isActive || hasErrors) && ( + + )} + {hasSchemaFields ? ( + <> + + + + setFilterValue(e.target.value)} + /> + + + + {addFieldButton} + + {percentageComplete < 100 ? ( + + Updating schema... + + ) : ( + + Save Schema + + )} + + + + + + + + ) : ( + + Content source does not have a schema} + body={ +

+ A schema is created for you once you index some documents. Click below to create + schema fields in advance. +

+ } + actions={addFieldButton} + /> +
+ )} +
+ {showAddFieldModal && ( + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx index dd772b86a00e2..929ab2d79fd14 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx @@ -4,6 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; -export const SchemaChangeErrors: React.FC = () => <>Schema Errors Placeholder; +import { useActions, useValues } from 'kea'; + +import { EuiSpacer } from '@elastic/eui'; + +import SchemaErrorsAccordion from 'shared/components/Schema/SchemaErrorsAccordion'; +import { ViewContentHeader } from 'workplace_search/components'; +import { SchemaLogic } from './SchemaLogic'; + +export const SchemaChangeErrors: React.FC = () => { + const { activeReindexJobId, sourceId } = useParams() as { + activeReindexJobId: string; + sourceId: string; + }; + const { initializeSchemaFieldErrors } = useActions(SchemaLogic); + + const { fieldCoercionErrors, serverSchema } = useValues(SchemaLogic); + + useEffect(() => { + initializeSchemaFieldErrors(activeReindexJobId, sourceId); + }, []); + + return ( +
+ + +
+ +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx new file mode 100644 index 0000000000000..dc2f223ef56c4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx @@ -0,0 +1,63 @@ +/* + * 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 React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiTable, + EuiTableBody, + EuiTableHeader, + EuiTableHeaderCell, + EuiTableRow, + EuiTableRowCell, +} from '@elastic/eui'; + +import { SchemaExistingField } from 'shared/components/Schema'; +import { SchemaLogic } from './SchemaLogic'; + +export const SchemaFieldsTable: React.FC = () => { + const { updateExistingFieldType } = useActions(SchemaLogic); + + const { filteredSchemaFields, filterValue } = useValues(SchemaLogic); + + return Object.keys(filteredSchemaFields).length > 0 ? ( + + + Field Name + Data Type + + + {Object.keys(filteredSchemaFields).map((fieldName) => ( + + + + + {fieldName} + + + + + + + + ))} + + + ) : ( +

No results found for '{filterValue}'.

+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts new file mode 100644 index 0000000000000..8f85e2604490c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -0,0 +1,358 @@ +/* + * 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 { cloneDeep, isEqual } from 'lodash'; +import { kea, MakeLogicType } from 'kea'; + +import http from 'shared/http'; +import routes from 'workplace_search/routes'; + +import { TEXT } from 'shared/constants/fieldTypes'; +import { ADD, UPDATE } from 'shared/constants/operations'; +import { IIndexJob, IFlashMessagesProps, TOperation } from 'shared/types'; + +import { AppLogic } from 'workplace_search/App/AppLogic'; +import { SourceLogic } from 'workplace_search/ContentSources/SourceLogic'; +import { IObject, OptionValue } from 'workplace_search/types'; + +interface SchemaActions { + setFlashMessages(flashMessages: IFlashMessagesProps): { flashMessages: IFlashMessagesProps }; + onInitializeSchema(schemaProps: SchemaInitialData): SchemaInitialData; + onInitializeSchemaFieldErrors( + fieldCoercionErrorsProps: SchemaChangeErrorsProps + ): SchemaChangeErrorsProps; + onSchemaSetSuccess( + schemaProps: SchemaSetProps & SchemaResponseProps + ): SchemaSetProps & SchemaResponseProps; + onSchemaSetError(errorProps: SchemaSetProps): SchemaSetProps; + onSchemaSetFormErrors(errors: string[]): string[]; + updateNewFieldType(newFieldType: string): string; + onFieldUpdate({ + schema, + formUnchanged, + }: { + schema: IObject; + formUnchanged: boolean; + }): { schema: IObject; formUnchanged: boolean }; + onIndexingComplete(numDocumentsWithErrors: number): number; + resetMostRecentIndexJob(emptyReindexJob: IIndexJob): IIndexJob; + showFieldSuccess(successMessage: string): string; + setFieldName(rawFieldName: string): string; + setFilterValue(filterValue: string): string; + addNewField(fieldName: string, newFieldType: string): { fieldName: string; newFieldType: string }; + updateFields(): void; + openAddFieldModal(): void; + closeAddFieldModal(): void; + resetSchemaState(): void; + initializeSchema(): void; + initializeSchemaFieldErrors( + activeReindexJobId: string, + sourceId: string + ): { activeReindexJobId: string; sourceId: string }; + updateExistingFieldType( + fieldName: string, + newFieldType: string + ): { fieldName: string; newFieldType: string }; + setServerField( + updatedSchema: IObject, + operation: TOperation + ): { updatedSchema: IObject; operation: TOperation }; +} + +interface SchemaValues { + sourceId: string; + activeSchema: IObject; + serverSchema: IObject; + filterValue: string; + filteredSchemaFields: IObject; + dataTypeOptions: OptionValue[]; + showAddFieldModal: boolean; + addFieldFormErrors: string[] | null; + mostRecentIndexJob: IIndexJob; + fieldCoercionErrors: FieldCoercionErrors; + flashMessages: IFlashMessagesProps; + newFieldType: string; + rawFieldName: string; + formUnchanged: boolean; + dataLoading: boolean; +} + +interface SchemaResponseProps { + schema: IObject; + mostRecentIndexJob: IIndexJob; +} + +export interface SchemaInitialData extends SchemaResponseProps { + sourceId: string; +} + +interface SchemaSetProps { + flashMessages: IFlashMessagesProps; +} + +interface FieldCoercionError { + external_id: string; + error: string; +} + +export interface FieldCoercionErrors { + [key: string]: FieldCoercionError[]; +} + +interface SchemaChangeErrorsProps { + fieldCoercionErrors: FieldCoercionErrors; +} + +const dataTypeOptions = [ + { value: 'text', text: 'Text' }, + { value: 'date', text: 'Date' }, + { value: 'number', text: 'Number' }, + { value: 'geolocation', text: 'Geo Location' }, +]; + +const FIELD_ERRORS_ERROR = 'Oops, we were not able to find any errors for this Schema'; + +export const SchemaLogic = kea>({ + actions: { + setFlashMessages: (flashMessages: IFlashMessagesProps) => ({ flashMessages }), + onInitializeSchema: (schemaProps: SchemaInitialData) => schemaProps, + onInitializeSchemaFieldErrors: (fieldCoercionErrorsProps: SchemaChangeErrorsProps) => + fieldCoercionErrorsProps, + onSchemaSetSuccess: (schemaProps: SchemaSetProps & SchemaResponseProps) => schemaProps, + onSchemaSetError: (errorProps: SchemaSetProps) => errorProps, + onSchemaSetFormErrors: (errors: string[]) => errors, + updateNewFieldType: (newFieldType: string) => newFieldType, + onFieldUpdate: ({ schema, formUnchanged }: { schema: IObject; formUnchanged: boolean }) => ({ + schema, + formUnchanged, + }), + onIndexingComplete: (numDocumentsWithErrors: number) => numDocumentsWithErrors, + resetMostRecentIndexJob: (emptyReindexJob: IIndexJob) => emptyReindexJob, + showFieldSuccess: (successMessage: string) => successMessage, + setFieldName: (rawFieldName: string) => rawFieldName, + setFilterValue: (filterValue: string) => filterValue, + openAddFieldModal: () => true, + closeAddFieldModal: () => true, + resetSchemaState: () => true, + initializeSchema: () => true, + initializeSchemaFieldErrors: (activeReindexJobId: string, sourceId: string) => ({ + activeReindexJobId, + sourceId, + }), + addNewField: (fieldName: string, newFieldType: string) => ({ fieldName, newFieldType }), + updateExistingFieldType: (fieldName: string, newFieldType: string) => ({ + fieldName, + newFieldType, + }), + updateFields: () => true, + setServerField: (updatedSchema: IObject, operation: TOperation) => ({ + updatedSchema, + operation, + }), + }, + reducers: { + dataTypeOptions: [dataTypeOptions], + sourceId: [ + '', + { + onInitializeSchema: (_, { sourceId }) => sourceId, + }, + ], + activeSchema: [ + {}, + { + onInitializeSchema: (_, { schema }) => schema, + onSchemaSetSuccess: (_, { schema }) => schema, + onFieldUpdate: (_, { schema }) => schema, + }, + ], + serverSchema: [ + {}, + { + onInitializeSchema: (_, { schema }) => schema, + onSchemaSetSuccess: (_, { schema }) => schema, + }, + ], + mostRecentIndexJob: [ + {} as IIndexJob, + { + onInitializeSchema: (_, { mostRecentIndexJob }) => mostRecentIndexJob, + resetMostRecentIndexJob: (_, emptyReindexJob) => emptyReindexJob, + onSchemaSetSuccess: (_, { mostRecentIndexJob }) => mostRecentIndexJob, + onIndexingComplete: (state, numDocumentsWithErrors) => ({ + ...state, + numDocumentsWithErrors, + percentageComplete: 100, + hasErrors: numDocumentsWithErrors > 0, + isActive: false, + }), + }, + ], + flashMessages: [ + {}, + { + setFlashMessages: (_, { flashMessages }) => flashMessages, + resetMostRecentIndexJob: () => ({}), + resetSchemaState: () => ({}), + onSchemaSetSuccess: (_, { flashMessages }) => flashMessages, + onSchemaSetError: (_, { flashMessages }) => flashMessages, + }, + ], + newFieldType: [ + TEXT, + { + updateNewFieldType: (_, newFieldType) => newFieldType, + onSchemaSetSuccess: () => TEXT, + }, + ], + addFieldFormErrors: [ + null, + { + onSchemaSetSuccess: () => null, + closeAddFieldModal: () => null, + onSchemaSetFormErrors: (_, addFieldFormErrors) => addFieldFormErrors, + }, + ], + filterValue: [ + '', + { + setFilterValue: (_, filterValue) => filterValue, + }, + ], + formUnchanged: [ + true, + { + onSchemaSetSuccess: () => true, + onFieldUpdate: (_, { formUnchanged }) => formUnchanged, + }, + ], + showAddFieldModal: [ + false, + { + onSchemaSetSuccess: () => false, + onSchemaSetError: () => false, + openAddFieldModal: () => true, + closeAddFieldModal: () => false, + }, + ], + dataLoading: [ + true, + { + onSchemaSetSuccess: () => false, + onInitializeSchema: () => false, + resetSchemaState: () => true, + }, + ], + rawFieldName: [ + '', + { + setFieldName: (_, rawFieldName) => rawFieldName, + onSchemaSetSuccess: () => '', + }, + ], + fieldCoercionErrors: [ + {}, + { + onInitializeSchemaFieldErrors: (_, { fieldCoercionErrors }) => fieldCoercionErrors, + }, + ], + }, + selectors: ({ selectors }) => ({ + filteredSchemaFields: [ + () => [selectors.activeSchema, selectors.filterValue], + (activeSchema, filterValue) => { + const filteredSchema = {}; + Object.keys(activeSchema) + .filter((x) => x.includes(filterValue)) + .forEach((k) => (filteredSchema[k] = activeSchema[k])); + return filteredSchema; + }, + ], + }), + listeners: ({ actions, values }) => ({ + initializeSchema: () => { + const { isOrganization } = AppLogic.values; + const { + contentSource: { id: sourceId }, + } = SourceLogic.values; + const route = isOrganization + ? routes.fritoPieOrganizationContentSourceSchemasPath(sourceId) + : routes.fritoPieAccountContentSourceSchemasPath(sourceId); + + return http(route).then(({ data }) => actions.onInitializeSchema({ sourceId, ...data })); + }, + initializeSchemaFieldErrors: async ({ activeReindexJobId, sourceId }) => { + const { isOrganization } = AppLogic.values; + + const route = isOrganization + ? routes.fritoPieOrganizationContentSourceReindexJobPath(sourceId, activeReindexJobId) + : routes.fritoPieAccountContentSourceReindexJobPath(sourceId, activeReindexJobId); + + try { + await actions.initializeSchema(); + http(route).then(({ data: { fieldCoercionErrors } }) => + actions.onInitializeSchemaFieldErrors({ fieldCoercionErrors }) + ); + } catch (error) { + actions.setFlashMessages({ error: [FIELD_ERRORS_ERROR] }); + } + }, + addNewField: ({ fieldName, newFieldType }) => { + const schema = cloneDeep(values.activeSchema); + schema[fieldName] = newFieldType; + actions.setServerField(schema, ADD); + }, + updateExistingFieldType: ({ fieldName, newFieldType }) => { + const schema = cloneDeep(values.activeSchema); + schema[fieldName] = newFieldType; + actions.onFieldUpdate({ schema, formUnchanged: isEqual(values.serverSchema, schema) }); + }, + updateFields: () => actions.setServerField(values.activeSchema, UPDATE), + setServerField: ({ updatedSchema, operation }) => { + const { isOrganization } = AppLogic.values; + const isAdding = operation === ADD; + const { sourceId } = values; + const successMessage = isAdding ? 'New field added.' : 'Schema updated.'; + const route = isOrganization + ? routes.fritoPieOrganizationContentSourceSchemasPath(sourceId) + : routes.fritoPieAccountContentSourceSchemasPath(sourceId); + + const emptyReindexJob = { + percentageComplete: 100, + numDocumentsWithErrors: 0, + activeReindexJobId: 0, + isActive: false, + }; + + actions.resetMostRecentIndexJob(emptyReindexJob); + + http + .post(route, updatedSchema) + .then(({ data }) => { + window.scrollTo(0, 0); + + actions.onSchemaSetSuccess({ + ...data, + flashMessages: { success: [successMessage] }, + }); + }) + .catch( + ({ + response: { + data: { errors }, + }, + }) => { + window.scrollTo(0, 0); + if (isAdding) { + actions.onSchemaSetFormErrors(errors); + } else { + actions.onSchemaSetError({ flashMessages: { error: errors } }); + } + } + ); + }, + }), +}); From 6370b52eefc10811e7af3e452473632bcc21cf96 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 11:42:08 -0600 Subject: [PATCH 02/15] Remove local flash messages from component --- .../views/content_sources/components/schema/schema.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index abef98f2235d0..7dcdcf0c12422 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -25,7 +25,6 @@ import { import { AppLogic } from 'workplace_search/App/AppLogic'; import { Loading, ViewContentHeader } from 'workplace_search/components'; -import FlashMessages from 'shared/components/FlashMessages'; import IndexingStatus from 'shared/components/IndexingStatus'; import { SchemaAddFieldModal } from 'shared/components/Schema'; @@ -51,7 +50,6 @@ export const Schema: React.FC = () => { addFieldFormErrors, mostRecentIndexJob, formUnchanged, - flashMessages, dataLoading, } = useValues(SchemaLogic); @@ -82,7 +80,6 @@ export const Schema: React.FC = () => { description="Add new fields or change the types of existing ones" />
- {!!flashMessages && } {(isActive || hasErrors) && ( Date: Wed, 2 Dec 2020 11:57:03 -0600 Subject: [PATCH 03/15] Update paths - Adds getReindexJobRoute method to routes - Repalces legacy Rails routes helper with hard-coded paths --- .../applications/workplace_search/routes.ts | 6 ++++++ .../content_sources/components/schema/schema.tsx | 16 +++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts index 14c288de5a0c8..868d76f7d09c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts @@ -126,3 +126,9 @@ export const getGroupSourcePrioritizationPath = (groupId: string): string => `${GROUPS_PATH}/${groupId}/source_prioritization`; export const getSourcesPath = (path: string, isOrganization: boolean): string => isOrganization ? path : `${PERSONAL_PATH}${path}`; +export const getReindexJobRoute = ( + sourceId: string, + activeReindexJobId: string, + isOrganization: boolean +) => + getSourcesPath(generatePath(REINDEX_JOB_PATH, { sourceId, activeReindexJobId }), isOrganization); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index 7dcdcf0c12422..78a351cc8e69e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -8,9 +8,6 @@ import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; -import routes from 'workplace_search/routes'; -import { getReindexJobRoute } from 'workplace_search/utils/routePaths'; - import { EuiButton, EuiButtonEmpty, @@ -24,6 +21,7 @@ import { import { AppLogic } from 'workplace_search/App/AppLogic'; import { Loading, ViewContentHeader } from 'workplace_search/components'; +import { getReindexJobRoute } from '../../../../routes'; import IndexingStatus from 'shared/components/IndexingStatus'; import { SchemaAddFieldModal } from 'shared/components/Schema'; @@ -62,16 +60,16 @@ export const Schema: React.FC = () => { if (dataLoading) return ; const hasSchemaFields = Object.keys(activeSchema).length > 0; - const { isActive, hasErrors, percentageComplete } = mostRecentIndexJob; + const { isActive, hasErrors, percentageComplete, activeReindexJobId } = mostRecentIndexJob; const addFieldButton = ( Add Field ); - const getStatusPath = isOrganization - ? routes.statusFritoPieOrganizationContentSourceReindexJobPath - : routes.statusFritoPieAccountContentSourceReindexJobPath; + const statusPath = isOrganization + ? `/api/workplace_search/org/sources/${sourceId}/reindex_job/${activeReindexJobId}` + : `/api/workplace_search/account/sources/${sourceId}/reindex_job/${activeReindexJobId}`; return ( <> @@ -85,10 +83,10 @@ export const Schema: React.FC = () => { itemId={sourceId} viewLinkPath={getReindexJobRoute( sourceId, - mostRecentIndexJob.activeReindexJobId, + mostRecentIndexJob.activeReindexJobId.toString(), isOrganization )} - getStatusPath={getStatusPath} + statusPath={statusPath} onComplete={onIndexingComplete} {...mostRecentIndexJob} /> From 3f19dd56db151abb282cdca4cdf1f24f175d001a Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 12:04:27 -0600 Subject: [PATCH 04/15] Add types and constants --- .../public/applications/shared/constants/operations.ts | 9 +++++++++ .../public/applications/shared/types.ts | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/constants/operations.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/constants/operations.ts b/x-pack/plugins/enterprise_search/public/applications/shared/constants/operations.ts new file mode 100644 index 0000000000000..96043bb4046ed --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/constants/operations.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const ADD = 'add'; +export const UPDATE = 'update'; +export const REMOVE = 'remove'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 38a6187d290b5..4d1ea5e579166 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ADD, UPDATE } from './constants/operations'; + export type SchemaTypes = 'text' | 'number' | 'geolocation' | 'date'; export interface Schema { @@ -27,8 +29,16 @@ export interface SchemaConflicts { [key: string]: SchemaConflictFieldTypes; } + export interface IIndexingStatus { percentageComplete: number; numDocumentsWithErrors: number; activeReindexJobId: number; } + +export interface IndexJob extends IIndexingStatus { + isActive?: boolean; + hasErrors?: boolean; +} + +export type TOperation = typeof ADD | typeof UPDATE; From 2ba975d614d16d232dc2d92c7f3f71aafaf91bd6 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 12:04:39 -0600 Subject: [PATCH 05/15] Update paths --- .../components/schema/schema.tsx | 14 ++++--- .../schema/schema_change_errors.tsx | 6 +-- .../components/schema/schema_fields_table.tsx | 4 +- .../components/schema/schema_logic.ts | 42 +++++++++---------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index 78a351cc8e69e..f370c5e543311 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -19,15 +19,17 @@ import { EuiPanel, } from '@elastic/eui'; -import { AppLogic } from 'workplace_search/App/AppLogic'; -import { Loading, ViewContentHeader } from 'workplace_search/components'; import { getReindexJobRoute } from '../../../../routes'; +import { AppLogic } from '../../../../app_logic'; -import IndexingStatus from 'shared/components/IndexingStatus'; -import { SchemaAddFieldModal } from 'shared/components/Schema'; +import { Loading } from '../../../../../shared/loading'; +import { ViewContentHeader } from '../../../../components/shared/view_content_header'; -import { SchemaFieldsTable } from './SchemaFieldsTable'; -import { SchemaLogic } from './SchemaLogic'; +import { SchemaAddFieldModal } from '../../../../../shared/schema/schema_add_field_modal'; +import { IndexingStatus } from '../../../../../shared/indexing_status'; + +import { SchemaFieldsTable } from './schema_fields_table'; +import { SchemaLogic } from './schema_logic'; export const Schema: React.FC = () => { const { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx index 929ab2d79fd14..e96380c221a0a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx @@ -11,9 +11,9 @@ import { useActions, useValues } from 'kea'; import { EuiSpacer } from '@elastic/eui'; -import SchemaErrorsAccordion from 'shared/components/Schema/SchemaErrorsAccordion'; -import { ViewContentHeader } from 'workplace_search/components'; -import { SchemaLogic } from './SchemaLogic'; +import { SchemaErrorsAccordion } from '../../../../../shared/schema/schema_errors_accordion'; +import { ViewContentHeader } from '../../../../components/shared/view_content_header'; +import { SchemaLogic } from './schema_logic'; export const SchemaChangeErrors: React.FC = () => { const { activeReindexJobId, sourceId } = useParams() as { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx index dc2f223ef56c4..ae241d9425e9d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx @@ -19,8 +19,8 @@ import { EuiTableRowCell, } from '@elastic/eui'; -import { SchemaExistingField } from 'shared/components/Schema'; -import { SchemaLogic } from './SchemaLogic'; +import { SchemaExistingField } from '../../../../../shared/schema/schema_existing_field'; +import { SchemaLogic } from './schema_logic'; export const SchemaFieldsTable: React.FC = () => { const { updateExistingFieldType } = useActions(SchemaLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 8f85e2604490c..e94d1b81f668c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -10,13 +10,13 @@ import { kea, MakeLogicType } from 'kea'; import http from 'shared/http'; import routes from 'workplace_search/routes'; -import { TEXT } from 'shared/constants/fieldTypes'; -import { ADD, UPDATE } from 'shared/constants/operations'; -import { IIndexJob, IFlashMessagesProps, TOperation } from 'shared/types'; +import { TEXT } from '../../../../../shared/constants/field_types'; +import { ADD, UPDATE } from '../../../../../shared/constants/operations'; +import { IndexJob, IFlashMessagesProps, TOperation } from '../../../../../shared/types'; +import { OptionValue } from '../../../../types'; -import { AppLogic } from 'workplace_search/App/AppLogic'; -import { SourceLogic } from 'workplace_search/ContentSources/SourceLogic'; -import { IObject, OptionValue } from 'workplace_search/types'; +import { AppLogic } from '../../../../app_logic'; +import { SourceLogic } from '../../source_logic'; interface SchemaActions { setFlashMessages(flashMessages: IFlashMessagesProps): { flashMessages: IFlashMessagesProps }; @@ -34,11 +34,11 @@ interface SchemaActions { schema, formUnchanged, }: { - schema: IObject; + schema: object; formUnchanged: boolean; - }): { schema: IObject; formUnchanged: boolean }; + }): { schema: object; formUnchanged: boolean }; onIndexingComplete(numDocumentsWithErrors: number): number; - resetMostRecentIndexJob(emptyReindexJob: IIndexJob): IIndexJob; + resetMostRecentIndexJob(emptyReindexJob: IndexJob): IndexJob; showFieldSuccess(successMessage: string): string; setFieldName(rawFieldName: string): string; setFilterValue(filterValue: string): string; @@ -57,21 +57,21 @@ interface SchemaActions { newFieldType: string ): { fieldName: string; newFieldType: string }; setServerField( - updatedSchema: IObject, + updatedSchema: object, operation: TOperation - ): { updatedSchema: IObject; operation: TOperation }; + ): { updatedSchema: object; operation: TOperation }; } interface SchemaValues { sourceId: string; - activeSchema: IObject; - serverSchema: IObject; + activeSchema: object; + serverSchema: object; filterValue: string; - filteredSchemaFields: IObject; + filteredSchemaFields: object; dataTypeOptions: OptionValue[]; showAddFieldModal: boolean; addFieldFormErrors: string[] | null; - mostRecentIndexJob: IIndexJob; + mostRecentIndexJob: IndexJob; fieldCoercionErrors: FieldCoercionErrors; flashMessages: IFlashMessagesProps; newFieldType: string; @@ -81,8 +81,8 @@ interface SchemaValues { } interface SchemaResponseProps { - schema: IObject; - mostRecentIndexJob: IIndexJob; + schema: object; + mostRecentIndexJob: IndexJob; } export interface SchemaInitialData extends SchemaResponseProps { @@ -125,12 +125,12 @@ export const SchemaLogic = kea>({ onSchemaSetError: (errorProps: SchemaSetProps) => errorProps, onSchemaSetFormErrors: (errors: string[]) => errors, updateNewFieldType: (newFieldType: string) => newFieldType, - onFieldUpdate: ({ schema, formUnchanged }: { schema: IObject; formUnchanged: boolean }) => ({ + onFieldUpdate: ({ schema, formUnchanged }: { schema: object; formUnchanged: boolean }) => ({ schema, formUnchanged, }), onIndexingComplete: (numDocumentsWithErrors: number) => numDocumentsWithErrors, - resetMostRecentIndexJob: (emptyReindexJob: IIndexJob) => emptyReindexJob, + resetMostRecentIndexJob: (emptyReindexJob: IndexJob) => emptyReindexJob, showFieldSuccess: (successMessage: string) => successMessage, setFieldName: (rawFieldName: string) => rawFieldName, setFilterValue: (filterValue: string) => filterValue, @@ -148,7 +148,7 @@ export const SchemaLogic = kea>({ newFieldType, }), updateFields: () => true, - setServerField: (updatedSchema: IObject, operation: TOperation) => ({ + setServerField: (updatedSchema: object, operation: TOperation) => ({ updatedSchema, operation, }), @@ -177,7 +177,7 @@ export const SchemaLogic = kea>({ }, ], mostRecentIndexJob: [ - {} as IIndexJob, + {} as IndexJob, { onInitializeSchema: (_, { mostRecentIndexJob }) => mostRecentIndexJob, resetMostRecentIndexJob: (_, emptyReindexJob) => emptyReindexJob, From 2f8bba10af7ca34f3a28decf1b1dd3afae7bc36f Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 13:22:32 -0600 Subject: [PATCH 06/15] Replace local flash message logic with gobal --- .../components/schema/schema_logic.ts | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index e94d1b81f668c..610f1965748c2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -12,22 +12,24 @@ import routes from 'workplace_search/routes'; import { TEXT } from '../../../../../shared/constants/field_types'; import { ADD, UPDATE } from '../../../../../shared/constants/operations'; -import { IndexJob, IFlashMessagesProps, TOperation } from '../../../../../shared/types'; +import { IndexJob, TOperation } from '../../../../../shared/types'; import { OptionValue } from '../../../../types'; +import { + flashAPIErrors, + setSuccessMessage, + FlashMessagesLogic, +} from '../../../../../shared/flash_messages'; + import { AppLogic } from '../../../../app_logic'; import { SourceLogic } from '../../source_logic'; interface SchemaActions { - setFlashMessages(flashMessages: IFlashMessagesProps): { flashMessages: IFlashMessagesProps }; onInitializeSchema(schemaProps: SchemaInitialData): SchemaInitialData; onInitializeSchemaFieldErrors( fieldCoercionErrorsProps: SchemaChangeErrorsProps ): SchemaChangeErrorsProps; - onSchemaSetSuccess( - schemaProps: SchemaSetProps & SchemaResponseProps - ): SchemaSetProps & SchemaResponseProps; - onSchemaSetError(errorProps: SchemaSetProps): SchemaSetProps; + onSchemaSetSuccess(schemaProps: SchemaResponseProps): SchemaResponseProps; onSchemaSetFormErrors(errors: string[]): string[]; updateNewFieldType(newFieldType: string): string; onFieldUpdate({ @@ -73,7 +75,6 @@ interface SchemaValues { addFieldFormErrors: string[] | null; mostRecentIndexJob: IndexJob; fieldCoercionErrors: FieldCoercionErrors; - flashMessages: IFlashMessagesProps; newFieldType: string; rawFieldName: string; formUnchanged: boolean; @@ -89,10 +90,6 @@ export interface SchemaInitialData extends SchemaResponseProps { sourceId: string; } -interface SchemaSetProps { - flashMessages: IFlashMessagesProps; -} - interface FieldCoercionError { external_id: string; error: string; @@ -117,12 +114,10 @@ const FIELD_ERRORS_ERROR = 'Oops, we were not able to find any errors for this S export const SchemaLogic = kea>({ actions: { - setFlashMessages: (flashMessages: IFlashMessagesProps) => ({ flashMessages }), onInitializeSchema: (schemaProps: SchemaInitialData) => schemaProps, onInitializeSchemaFieldErrors: (fieldCoercionErrorsProps: SchemaChangeErrorsProps) => fieldCoercionErrorsProps, - onSchemaSetSuccess: (schemaProps: SchemaSetProps & SchemaResponseProps) => schemaProps, - onSchemaSetError: (errorProps: SchemaSetProps) => errorProps, + onSchemaSetSuccess: (schemaProps: SchemaResponseProps) => schemaProps, onSchemaSetFormErrors: (errors: string[]) => errors, updateNewFieldType: (newFieldType: string) => newFieldType, onFieldUpdate: ({ schema, formUnchanged }: { schema: object; formUnchanged: boolean }) => ({ @@ -191,16 +186,6 @@ export const SchemaLogic = kea>({ }), }, ], - flashMessages: [ - {}, - { - setFlashMessages: (_, { flashMessages }) => flashMessages, - resetMostRecentIndexJob: () => ({}), - resetSchemaState: () => ({}), - onSchemaSetSuccess: (_, { flashMessages }) => flashMessages, - onSchemaSetError: (_, { flashMessages }) => flashMessages, - }, - ], newFieldType: [ TEXT, { @@ -233,7 +218,6 @@ export const SchemaLogic = kea>({ false, { onSchemaSetSuccess: () => false, - onSchemaSetError: () => false, openAddFieldModal: () => true, closeAddFieldModal: () => false, }, @@ -296,8 +280,8 @@ export const SchemaLogic = kea>({ http(route).then(({ data: { fieldCoercionErrors } }) => actions.onInitializeSchemaFieldErrors({ fieldCoercionErrors }) ); - } catch (error) { - actions.setFlashMessages({ error: [FIELD_ERRORS_ERROR] }); + } catch (e) { + flashAPIErrors({ ...e, message: FIELD_ERRORS_ERROR }); } }, addNewField: ({ fieldName, newFieldType }) => { @@ -334,10 +318,8 @@ export const SchemaLogic = kea>({ .then(({ data }) => { window.scrollTo(0, 0); - actions.onSchemaSetSuccess({ - ...data, - flashMessages: { success: [successMessage] }, - }); + actions.onSchemaSetSuccess(data); + setSuccessMessage(successMessage); }) .catch( ({ @@ -349,10 +331,16 @@ export const SchemaLogic = kea>({ if (isAdding) { actions.onSchemaSetFormErrors(errors); } else { - actions.onSchemaSetError({ flashMessages: { error: errors } }); + flashAPIErrors(errors); } } ); }, + resetMostRecentIndexJob: () => { + FlashMessagesLogic.actions.clearFlashMessages(); + }, + resetSchemaState: () => { + FlashMessagesLogic.actions.clearFlashMessages(); + }, }), }); From c0649aeac50dd3149ea21952ab5bb5ba59afba8b Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 13:45:29 -0600 Subject: [PATCH 07/15] Update with newly added types Added here: https://github.com/elastic/kibana/pull/84822 --- .../components/schema/schema_logic.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 610f1965748c2..60e092f81efa9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -12,7 +12,7 @@ import routes from 'workplace_search/routes'; import { TEXT } from '../../../../../shared/constants/field_types'; import { ADD, UPDATE } from '../../../../../shared/constants/operations'; -import { IndexJob, TOperation } from '../../../../../shared/types'; +import { IndexJob, TOperation, Schema, SchemaTypes } from '../../../../../shared/types'; import { OptionValue } from '../../../../types'; import { @@ -31,20 +31,23 @@ interface SchemaActions { ): SchemaChangeErrorsProps; onSchemaSetSuccess(schemaProps: SchemaResponseProps): SchemaResponseProps; onSchemaSetFormErrors(errors: string[]): string[]; - updateNewFieldType(newFieldType: string): string; + updateNewFieldType(newFieldType: SchemaTypes): SchemaTypes; onFieldUpdate({ schema, formUnchanged, }: { - schema: object; + schema: Schema; formUnchanged: boolean; - }): { schema: object; formUnchanged: boolean }; + }): { schema: Schema; formUnchanged: boolean }; onIndexingComplete(numDocumentsWithErrors: number): number; resetMostRecentIndexJob(emptyReindexJob: IndexJob): IndexJob; showFieldSuccess(successMessage: string): string; setFieldName(rawFieldName: string): string; setFilterValue(filterValue: string): string; - addNewField(fieldName: string, newFieldType: string): { fieldName: string; newFieldType: string }; + addNewField( + fieldName: string, + newFieldType: SchemaTypes + ): { fieldName: string; newFieldType: SchemaTypes }; updateFields(): void; openAddFieldModal(): void; closeAddFieldModal(): void; @@ -56,20 +59,20 @@ interface SchemaActions { ): { activeReindexJobId: string; sourceId: string }; updateExistingFieldType( fieldName: string, - newFieldType: string - ): { fieldName: string; newFieldType: string }; + newFieldType: SchemaTypes + ): { fieldName: string; newFieldType: SchemaTypes }; setServerField( - updatedSchema: object, + updatedSchema: Schema, operation: TOperation - ): { updatedSchema: object; operation: TOperation }; + ): { updatedSchema: Schema; operation: TOperation }; } interface SchemaValues { sourceId: string; - activeSchema: object; - serverSchema: object; + activeSchema: Schema; + serverSchema: Schema; filterValue: string; - filteredSchemaFields: object; + filteredSchemaFields: Schema; dataTypeOptions: OptionValue[]; showAddFieldModal: boolean; addFieldFormErrors: string[] | null; @@ -82,7 +85,7 @@ interface SchemaValues { } interface SchemaResponseProps { - schema: object; + schema: Schema; mostRecentIndexJob: IndexJob; } @@ -120,7 +123,7 @@ export const SchemaLogic = kea>({ onSchemaSetSuccess: (schemaProps: SchemaResponseProps) => schemaProps, onSchemaSetFormErrors: (errors: string[]) => errors, updateNewFieldType: (newFieldType: string) => newFieldType, - onFieldUpdate: ({ schema, formUnchanged }: { schema: object; formUnchanged: boolean }) => ({ + onFieldUpdate: ({ schema, formUnchanged }: { schema: Schema; formUnchanged: boolean }) => ({ schema, formUnchanged, }), @@ -137,13 +140,13 @@ export const SchemaLogic = kea>({ activeReindexJobId, sourceId, }), - addNewField: (fieldName: string, newFieldType: string) => ({ fieldName, newFieldType }), + addNewField: (fieldName: string, newFieldType: SchemaTypes) => ({ fieldName, newFieldType }), updateExistingFieldType: (fieldName: string, newFieldType: string) => ({ fieldName, newFieldType, }), updateFields: () => true, - setServerField: (updatedSchema: object, operation: TOperation) => ({ + setServerField: (updatedSchema: Schema, operation: TOperation) => ({ updatedSchema, operation, }), @@ -248,7 +251,7 @@ export const SchemaLogic = kea>({ filteredSchemaFields: [ () => [selectors.activeSchema, selectors.filterValue], (activeSchema, filterValue) => { - const filteredSchema = {}; + const filteredSchema = {} as Schema; Object.keys(activeSchema) .filter((x) => x.includes(filterValue)) .forEach((k) => (filteredSchema[k] = activeSchema[k])); From cf8098838826586c5693849857f0ecb61f778d22 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 13:54:48 -0600 Subject: [PATCH 08/15] Update server routes --- .../components/schema/schema_logic.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 60e092f81efa9..6a9fa45fe9bad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -8,7 +8,6 @@ import { cloneDeep, isEqual } from 'lodash'; import { kea, MakeLogicType } from 'kea'; import http from 'shared/http'; -import routes from 'workplace_search/routes'; import { TEXT } from '../../../../../shared/constants/field_types'; import { ADD, UPDATE } from '../../../../../shared/constants/operations'; @@ -265,9 +264,10 @@ export const SchemaLogic = kea>({ const { contentSource: { id: sourceId }, } = SourceLogic.values; + const route = isOrganization - ? routes.fritoPieOrganizationContentSourceSchemasPath(sourceId) - : routes.fritoPieAccountContentSourceSchemasPath(sourceId); + ? `/api/workplace_search/org/sources/${sourceId}/schemas` + : `/api/workplace_search/account/sources/${sourceId}/schemas`; return http(route).then(({ data }) => actions.onInitializeSchema({ sourceId, ...data })); }, @@ -275,8 +275,8 @@ export const SchemaLogic = kea>({ const { isOrganization } = AppLogic.values; const route = isOrganization - ? routes.fritoPieOrganizationContentSourceReindexJobPath(sourceId, activeReindexJobId) - : routes.fritoPieAccountContentSourceReindexJobPath(sourceId, activeReindexJobId); + ? `/api/workplace_search/org/sources/${sourceId}/reindex_job/${activeReindexJobId}` + : `/api/workplace_search/account/sources/${sourceId}/reindex_job/${activeReindexJobId}`; try { await actions.initializeSchema(); @@ -304,8 +304,8 @@ export const SchemaLogic = kea>({ const { sourceId } = values; const successMessage = isAdding ? 'New field added.' : 'Schema updated.'; const route = isOrganization - ? routes.fritoPieOrganizationContentSourceSchemasPath(sourceId) - : routes.fritoPieAccountContentSourceSchemasPath(sourceId); + ? `/api/workplace_search/org/sources/${sourceId}/schemas` + : `/api/workplace_search/account/sources/${sourceId}/schemas`; const emptyReindexJob = { percentageComplete: 100, From 8d5279f74ee31eb2cc42b9608eafffe2e636b9dd Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 14:09:33 -0600 Subject: [PATCH 09/15] Replace Rails http with kibana http --- .../components/schema/schema_logic.ts | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 6a9fa45fe9bad..c8042810d160f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -7,7 +7,7 @@ import { cloneDeep, isEqual } from 'lodash'; import { kea, MakeLogicType } from 'kea'; -import http from 'shared/http'; +import { HttpLogic } from '../../../../../shared/http'; import { TEXT } from '../../../../../shared/constants/field_types'; import { ADD, UPDATE } from '../../../../../shared/constants/operations'; @@ -259,8 +259,9 @@ export const SchemaLogic = kea>({ ], }), listeners: ({ actions, values }) => ({ - initializeSchema: () => { + initializeSchema: async () => { const { isOrganization } = AppLogic.values; + const { http } = HttpLogic.values; const { contentSource: { id: sourceId }, } = SourceLogic.values; @@ -269,20 +270,26 @@ export const SchemaLogic = kea>({ ? `/api/workplace_search/org/sources/${sourceId}/schemas` : `/api/workplace_search/account/sources/${sourceId}/schemas`; - return http(route).then(({ data }) => actions.onInitializeSchema({ sourceId, ...data })); + try { + const response = await http.get(route); + actions.onInitializeSchema({ sourceId, ...response }); + } catch (e) { + flashAPIErrors(e); + } }, initializeSchemaFieldErrors: async ({ activeReindexJobId, sourceId }) => { const { isOrganization } = AppLogic.values; - + const { http } = HttpLogic.values; const route = isOrganization ? `/api/workplace_search/org/sources/${sourceId}/reindex_job/${activeReindexJobId}` : `/api/workplace_search/account/sources/${sourceId}/reindex_job/${activeReindexJobId}`; try { await actions.initializeSchema(); - http(route).then(({ data: { fieldCoercionErrors } }) => - actions.onInitializeSchemaFieldErrors({ fieldCoercionErrors }) - ); + const response = await http.get(route); + actions.onInitializeSchemaFieldErrors({ + fieldCoercionErrors: response.fieldCoercionErrors, + }); } catch (e) { flashAPIErrors({ ...e, message: FIELD_ERRORS_ERROR }); } @@ -298,8 +305,9 @@ export const SchemaLogic = kea>({ actions.onFieldUpdate({ schema, formUnchanged: isEqual(values.serverSchema, schema) }); }, updateFields: () => actions.setServerField(values.activeSchema, UPDATE), - setServerField: ({ updatedSchema, operation }) => { + setServerField: async ({ updatedSchema, operation }) => { const { isOrganization } = AppLogic.values; + const { http } = HttpLogic.values; const isAdding = operation === ADD; const { sourceId } = values; const successMessage = isAdding ? 'New field added.' : 'Schema updated.'; @@ -316,28 +324,20 @@ export const SchemaLogic = kea>({ actions.resetMostRecentIndexJob(emptyReindexJob); - http - .post(route, updatedSchema) - .then(({ data }) => { - window.scrollTo(0, 0); - - actions.onSchemaSetSuccess(data); - setSuccessMessage(successMessage); - }) - .catch( - ({ - response: { - data: { errors }, - }, - }) => { - window.scrollTo(0, 0); - if (isAdding) { - actions.onSchemaSetFormErrors(errors); - } else { - flashAPIErrors(errors); - } - } - ); + try { + const response = await http.post(route, { + body: JSON.stringify({ ...updatedSchema }), + }); + actions.onSchemaSetSuccess(response); + setSuccessMessage(successMessage); + } catch (e) { + window.scrollTo(0, 0); + if (isAdding) { + actions.onSchemaSetFormErrors(e?.message); + } else { + flashAPIErrors(e); + } + } }, resetMostRecentIndexJob: () => { FlashMessagesLogic.actions.clearFlashMessages(); From 9364af374bb5ef1267321d447e0065aaeb90adb9 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 16:35:22 -0600 Subject: [PATCH 10/15] Set percentage to 0 when updating Without this, the IndexingStatus never shows. --- .../views/content_sources/components/schema/schema_logic.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index c8042810d160f..2541d54c18f44 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -186,6 +186,10 @@ export const SchemaLogic = kea>({ hasErrors: numDocumentsWithErrors > 0, isActive: false, }), + updateFields: (state) => ({ + ...state, + percentageComplete: 0, + }), }, ], newFieldType: [ From 1b05ab1e01b40bc64a8ddf6abd47bcdaa9f7817f Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 16:36:06 -0600 Subject: [PATCH 11/15] Fix route paths --- .../views/content_sources/components/schema/schema.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index f370c5e543311..4ed8ddb84c739 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -70,8 +70,8 @@ export const Schema: React.FC = () => { ); const statusPath = isOrganization - ? `/api/workplace_search/org/sources/${sourceId}/reindex_job/${activeReindexJobId}` - : `/api/workplace_search/account/sources/${sourceId}/reindex_job/${activeReindexJobId}`; + ? `/api/workplace_search/org/sources/${sourceId}/reindex_job/${activeReindexJobId}/status` + : `/api/workplace_search/account/sources/${sourceId}/reindex_job/${activeReindexJobId}/status`; return ( <> From 67fb7b1d745c1bc78705421d0b9cebd517efb998 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 16:37:25 -0600 Subject: [PATCH 12/15] Fix server route validation The empty object was breaking the UI since `schema.object({})` is actually an empty object. This is more explicit and correct. --- .../server/routes/workplace_search/sources.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index 9beac109be510..04db6bbc2912e 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -8,6 +8,16 @@ import { schema } from '@kbn/config-schema'; import { RouteDependencies } from '../../plugin'; +const schemaValuesSchema = schema.recordOf( + schema.string(), + schema.oneOf([ + schema.literal('text'), + schema.literal('number'), + schema.literal('geolocation'), + schema.literal('date'), + ]) +); + const pageSchema = schema.object({ current: schema.number(), size: schema.number(), @@ -363,7 +373,7 @@ export function registerAccountSourceSchemasRoute({ { path: '/api/workplace_search/account/sources/{id}/schemas', validate: { - body: schema.object({}), + body: schemaValuesSchema, params: schema.object({ id: schema.string(), }), @@ -745,7 +755,7 @@ export function registerOrgSourceSchemasRoute({ { path: '/api/workplace_search/org/sources/{id}/schemas', validate: { - body: schema.object({}), + body: schemaValuesSchema, params: schema.object({ id: schema.string(), }), From d3af644bb81fb54c5c060ea98a15d6f168aea9e4 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 17:08:14 -0600 Subject: [PATCH 13/15] Add i18n --- .../components/schema/constants.ts | 105 ++++++++++++++++++ .../components/schema/schema.tsx | 32 +++--- .../schema/schema_change_errors.tsx | 3 +- .../components/schema/schema_fields_table.tsx | 20 +++- .../components/schema/schema_logic.ts | 12 +- 5 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/constants.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/constants.ts new file mode 100644 index 0000000000000..104331dcd97bb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/constants.ts @@ -0,0 +1,105 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const SCHEMA_ERRORS_HEADING = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.heading', + { + defaultMessage: 'Schema Change Errors', + } +); + +export const SCHEMA_ERRORS_TABLE_FIELD_NAME_HEADER = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.header.fieldName', + { + defaultMessage: 'Field Name', + } +); + +export const SCHEMA_ERRORS_TABLE_DATA_TYPE_HEADER = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.header.dataType', + { + defaultMessage: 'Data Type', + } +); + +export const SCHEMA_FIELD_ERRORS_ERROR_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.message', + { + defaultMessage: 'Oops, we were not able to find any errors for this Schema.', + } +); + +export const SCHEMA_FIELD_ADDED_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.fieldAdded.message', + { + defaultMessage: 'New field added.', + } +); + +export const SCHEMA_UPDATED_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.updated.message', + { + defaultMessage: 'Schema updated.', + } +); + +export const SCHEMA_ADD_FIELD_BUTTON = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.addField.button', + { + defaultMessage: 'Add field', + } +); + +export const SCHEMA_MANAGE_SCHEMA_TITLE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.manage.title', + { + defaultMessage: 'Manage source schema', + } +); + +export const SCHEMA_MANAGE_SCHEMA_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.manage.description', + { + defaultMessage: 'Add new fields or change the types of existing ones', + } +); + +export const SCHEMA_FILTER_PLACEHOLDER = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.filter.placeholder', + { + defaultMessage: 'Filter schema fields...', + } +); + +export const SCHEMA_UPDATING = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.updating', + { + defaultMessage: 'Updating schema...', + } +); + +export const SCHEMA_SAVE_BUTTON = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.save.button', + { + defaultMessage: 'Save schema', + } +); + +export const SCHEMA_EMPTY_SCHEMA_TITLE = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.title', + { + defaultMessage: 'Content source does not have a schema', + } +); + +export const SCHEMA_EMPTY_SCHEMA_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.empty.description', + { + defaultMessage: + 'A schema is created for you once you index some documents. Click below to create schema fields in advance.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx index 4ed8ddb84c739..6a1991e4c39e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.tsx @@ -31,6 +31,17 @@ import { IndexingStatus } from '../../../../../shared/indexing_status'; import { SchemaFieldsTable } from './schema_fields_table'; import { SchemaLogic } from './schema_logic'; +import { + SCHEMA_ADD_FIELD_BUTTON, + SCHEMA_MANAGE_SCHEMA_TITLE, + SCHEMA_MANAGE_SCHEMA_DESCRIPTION, + SCHEMA_FILTER_PLACEHOLDER, + SCHEMA_UPDATING, + SCHEMA_SAVE_BUTTON, + SCHEMA_EMPTY_SCHEMA_TITLE, + SCHEMA_EMPTY_SCHEMA_DESCRIPTION, +} from './constants'; + export const Schema: React.FC = () => { const { initializeSchema, @@ -66,7 +77,7 @@ export const Schema: React.FC = () => { const addFieldButton = ( - Add Field + {SCHEMA_ADD_FIELD_BUTTON} ); const statusPath = isOrganization @@ -76,8 +87,8 @@ export const Schema: React.FC = () => { return ( <>
{(isActive || hasErrors) && ( @@ -101,7 +112,7 @@ export const Schema: React.FC = () => { setFilterValue(e.target.value)} /> @@ -111,7 +122,7 @@ export const Schema: React.FC = () => { {percentageComplete < 100 ? ( - Updating schema... + {SCHEMA_UPDATING} ) : ( { onClick={updateFields} fill={true} > - Save Schema + {SCHEMA_SAVE_BUTTON} )} @@ -134,13 +145,8 @@ export const Schema: React.FC = () => { Content source does not have a schema} - body={ -

- A schema is created for you once you index some documents. Click below to create - schema fields in advance. -

- } + title={

{SCHEMA_EMPTY_SCHEMA_TITLE}

} + body={

{SCHEMA_EMPTY_SCHEMA_DESCRIPTION}

} actions={addFieldButton} />
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx index e96380c221a0a..7fc923875dcdf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.tsx @@ -14,6 +14,7 @@ import { EuiSpacer } from '@elastic/eui'; import { SchemaErrorsAccordion } from '../../../../../shared/schema/schema_errors_accordion'; import { ViewContentHeader } from '../../../../components/shared/view_content_header'; import { SchemaLogic } from './schema_logic'; +import { SCHEMA_ERRORS_HEADING } from './constants'; export const SchemaChangeErrors: React.FC = () => { const { activeReindexJobId, sourceId } = useParams() as { @@ -30,7 +31,7 @@ export const SchemaChangeErrors: React.FC = () => { return (
- +
{ const { updateExistingFieldType } = useActions(SchemaLogic); @@ -30,8 +36,8 @@ export const SchemaFieldsTable: React.FC = () => { return Object.keys(filteredSchemaFields).length > 0 ? ( - Field Name - Data Type + {SCHEMA_ERRORS_TABLE_FIELD_NAME_HEADER} + {SCHEMA_ERRORS_TABLE_DATA_TYPE_HEADER} {Object.keys(filteredSchemaFields).map((fieldName) => ( @@ -58,6 +64,14 @@ export const SchemaFieldsTable: React.FC = () => { ) : ( -

No results found for '{filterValue}'.

+

+ {i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.header.dataType', + { + defaultMessage: 'No results found for "{filterValue}".', + values: { filterValue }, + } + )} +

); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 2541d54c18f44..36eb3fc67b2c2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -23,6 +23,12 @@ import { import { AppLogic } from '../../../../app_logic'; import { SourceLogic } from '../../source_logic'; +import { + SCHEMA_FIELD_ERRORS_ERROR_MESSAGE, + SCHEMA_FIELD_ADDED_MESSAGE, + SCHEMA_UPDATED_MESSAGE, +} from './constants'; + interface SchemaActions { onInitializeSchema(schemaProps: SchemaInitialData): SchemaInitialData; onInitializeSchemaFieldErrors( @@ -112,8 +118,6 @@ const dataTypeOptions = [ { value: 'geolocation', text: 'Geo Location' }, ]; -const FIELD_ERRORS_ERROR = 'Oops, we were not able to find any errors for this Schema'; - export const SchemaLogic = kea>({ actions: { onInitializeSchema: (schemaProps: SchemaInitialData) => schemaProps, @@ -295,7 +299,7 @@ export const SchemaLogic = kea>({ fieldCoercionErrors: response.fieldCoercionErrors, }); } catch (e) { - flashAPIErrors({ ...e, message: FIELD_ERRORS_ERROR }); + flashAPIErrors({ ...e, message: SCHEMA_FIELD_ERRORS_ERROR_MESSAGE }); } }, addNewField: ({ fieldName, newFieldType }) => { @@ -314,7 +318,7 @@ export const SchemaLogic = kea>({ const { http } = HttpLogic.values; const isAdding = operation === ADD; const { sourceId } = values; - const successMessage = isAdding ? 'New field added.' : 'Schema updated.'; + const successMessage = isAdding ? SCHEMA_FIELD_ADDED_MESSAGE : SCHEMA_UPDATED_MESSAGE; const route = isOrganization ? `/api/workplace_search/org/sources/${sourceId}/schemas` : `/api/workplace_search/account/sources/${sourceId}/schemas`; From 43fd6aba35ef202a5938bf8df3952c64feba6062 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 17:30:47 -0600 Subject: [PATCH 14/15] Make sure i18n key is unique --- .../content_sources/components/schema/schema_fields_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx index bbb931da54f7a..b1eac0a3d8734 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx @@ -66,7 +66,7 @@ export const SchemaFieldsTable: React.FC = () => { ) : (

{i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.errors.header.dataType', + 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.filter.noResults.message', { defaultMessage: 'No results found for "{filterValue}".', values: { filterValue }, From c54a1320829127e79d68354f7a08b6e6f05b0190 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 2 Dec 2020 21:20:51 -0600 Subject: [PATCH 15/15] Lint --- .../enterprise_search/public/applications/shared/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 4d1ea5e579166..c1737142e482e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -29,7 +29,6 @@ export interface SchemaConflicts { [key: string]: SchemaConflictFieldTypes; } - export interface IIndexingStatus { percentageComplete: number; numDocumentsWithErrors: number;