Skip to content

Commit

Permalink
[App Search] Schema: Set up server routes, grand foray into shared/co…
Browse files Browse the repository at this point in the history
…nnected Kea logic (elastic#99548)

* Set up server API routes

* Set up types

* Set up shared/base Schema logic file

- values & actions shared between both default source engine & meta engine pages

* Add default/indexed engine SchemaLogic

* Add MetaEnginesSchemaLogic

- significantly different actions (no updating) & API response from source engines, hence the separate files

+ fix typing issue - without Partial<>, all 4 enum types are expected instead of 1-4
- for some reason this causes an error in a separate a util file, not sure why (Typescript issue?)

* Update Schema & MetaEngineSchema views with loaders

* PR feedback: comment nit

* PR feedback: Remove unnecessary async/awaits

* PR feedback: Simplify loadSchema to be shared by base logic

Much clean, such simple

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
Constance and kibanamachine committed May 11, 2021
1 parent 33d0212 commit 74d39ff
Show file tree
Hide file tree
Showing 17 changed files with 1,043 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EngineDetails } from '../../../engine/types';

export const getConflictingEnginesFromConflictingField = (
conflictingField: SchemaConflictFieldTypes
): string[] => Object.values(conflictingField).flat();
) => Object.values(conflictingField).flat() as string[];

export const getConflictingEnginesFromSchemaConflicts = (
schemaConflicts: SchemaConflicts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,18 @@ import { i18n } from '@kbn/i18n';
export const SCHEMA_TITLE = i18n.translate('xpack.enterpriseSearch.appSearch.engine.schema.title', {
defaultMessage: 'Schema',
});

export const ADD_SCHEMA_ERROR = (fieldName: string) =>
i18n.translate('xpack.enterpriseSearch.appSearch.engine.schema.addSchemaErrorMessage', {
defaultMessage: 'Field name already exists: {fieldName}',
values: { fieldName },
});
export const ADD_SCHEMA_SUCCESS = (fieldName: string) =>
i18n.translate('xpack.enterpriseSearch.appSearch.engine.schema.addSchemaSuccessMessage', {
defaultMessage: 'New field added: {fieldName}',
values: { fieldName },
});
export const UPDATE_SCHEMA_SUCCESS = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.schema.updateSchemaSuccessMessage',
{ defaultMessage: 'Schema updated' }
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../__mocks__';
import '../../__mocks__/engine_logic.mock';

import { nextTick } from '@kbn/test/jest';

import { SchemaType } from '../../../shared/schema/types';

import { SchemaBaseLogic } from './schema_base_logic';

describe('SchemaBaseLogic', () => {
const { mount } = new LogicMounter(SchemaBaseLogic);
const { http } = mockHttpValues;
const { flashAPIErrors } = mockFlashMessageHelpers;

const MOCK_SCHEMA = {
some_text_field: SchemaType.Text,
some_number_field: SchemaType.Number,
};
const MOCK_RESPONSE = {
schema: MOCK_SCHEMA,
} as any;

const DEFAULT_VALUES = {
dataLoading: true,
schema: {},
};

beforeEach(() => {
jest.clearAllMocks();
});

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

describe('actions', () => {
describe('onSchemaLoad', () => {
it('stores schema state and sets dataLoading to false', () => {
mount({ schema: {}, dataLoading: true });

SchemaBaseLogic.actions.onSchemaLoad(MOCK_RESPONSE);

expect(SchemaBaseLogic.values).toEqual({
...DEFAULT_VALUES,
dataLoading: false,
schema: MOCK_SCHEMA,
});
});
});

describe('setSchema', () => {
it('updates schema state', () => {
mount({ schema: {} });

SchemaBaseLogic.actions.setSchema(MOCK_SCHEMA);

expect(SchemaBaseLogic.values).toEqual({
...DEFAULT_VALUES,
schema: MOCK_SCHEMA,
});
});
});
});

describe('listeners', () => {
describe('loadSchema', () => {
it('should make an API call and then set schema state', async () => {
http.get.mockReturnValueOnce(Promise.resolve(MOCK_RESPONSE));
mount();
jest.spyOn(SchemaBaseLogic.actions, 'onSchemaLoad');

SchemaBaseLogic.actions.loadSchema();
await nextTick();

expect(http.get).toHaveBeenCalledWith('/api/app_search/engines/some-engine/schema');
expect(SchemaBaseLogic.actions.onSchemaLoad).toHaveBeenCalledWith(MOCK_RESPONSE);
});

it('handles errors', async () => {
http.get.mockReturnValueOnce(Promise.reject('error'));
mount();

SchemaBaseLogic.actions.loadSchema();
await nextTick();

expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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 { kea, MakeLogicType } from 'kea';

import { flashAPIErrors } from '../../../shared/flash_messages';
import { HttpLogic } from '../../../shared/http';
import { Schema } from '../../../shared/schema/types';
import { EngineLogic } from '../engine';

import { SchemaApiResponse, MetaEngineSchemaApiResponse } from './types';

export interface SchemaBaseValues {
dataLoading: boolean;
schema: Schema;
}

export interface SchemaBaseActions {
loadSchema(): void;
onSchemaLoad(
response: SchemaApiResponse | MetaEngineSchemaApiResponse
): SchemaApiResponse | MetaEngineSchemaApiResponse;
setSchema(schema: Schema): { schema: Schema };
}

export const SchemaBaseLogic = kea<MakeLogicType<SchemaBaseValues, SchemaBaseActions>>({
path: ['enterprise_search', 'app_search', 'schema_base_logic'],
actions: {
loadSchema: true,
onSchemaLoad: (response) => response,
setSchema: (schema) => ({ schema }),
},
reducers: {
dataLoading: [
true,
{
loadSchema: () => true,
onSchemaLoad: () => false,
},
],
schema: [
{},
{
onSchemaLoad: (_, { schema }) => schema,
setSchema: (_, { schema }) => schema,
},
],
},
listeners: ({ actions }) => ({
loadSchema: async () => {
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;

try {
const response = await http.get(`/api/app_search/engines/${engineName}/schema`);
actions.onSchemaLoad(response);
} catch (e) {
flashAPIErrors(e);
}
},
}),
});
Loading

0 comments on commit 74d39ff

Please sign in to comment.