[Workplace Search] Port PR 4033 from ent-search to Kibana and update typings (#105054)

* Improve typings

Custom API Source allow indexing several types of data. We didn't
account for all of them.
For example, geolocation can be array of arrays of numbers.

This commit improves typings.
The following commits mostly fix TS errors
that appear after this commit.

* Remove type castings to account for all possible variable types

* Update helper functions to accept new CustomAPIFieldValue

* Fix TS error: convert url to string before using it in EuiLink

* Update mock and tests to match updated typings

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Vadim Yakhin 2021-07-12 17:20:48 -03:00 committed by GitHub
parent e279042c56
commit fdc99681a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 65 additions and 17 deletions

View file

@ -303,6 +303,7 @@ export const exampleResult = {
titleField: 'otherTitle',
subtitleField: 'otherSubtitle',
urlField: 'myLink',
urlFieldIsLinkable: true,
color: '#e3e3e3',
descriptionField: 'about',
typeField: 'otherType',
@ -314,14 +315,18 @@ export const exampleResult = {
{ fieldName: 'dogs', label: 'Canines' },
],
},
titleFieldHover: false,
urlFieldHover: false,
exampleDocuments: [
{
myLink: 'http://foo',
otherTitle: 'foo',
content_source_id: '60e85e7ea2564c265a88a4f0',
external_id: 'doc-60e85eb7a2564c937a88a4f3',
last_updated: '2021-07-09T14:35:35+00:00',
updated_at: '2021-07-09T14:35:35+00:00',
source: 'custom',
},
],
schemaFields: {},
};
export const mostRecentIndexJob = {

View file

@ -96,7 +96,7 @@ export interface ContentSource {
export interface SourceContentItem {
id: string;
last_updated: string;
[key: string]: string;
[key: string]: string | CustomAPIFieldValue;
}
export interface ContentSourceDetails extends ContentSource {
@ -186,8 +186,25 @@ export interface CustomSource {
id: string;
}
// https://www.elastic.co/guide/en/workplace-search/current/workplace-search-custom-sources-api.html#_schema_data_types
type CustomAPIString = string | string[];
type CustomAPINumber = number | number[];
type CustomAPIDate = string | string[];
type CustomAPIGeolocation = string | string[] | number[] | number[][];
export type CustomAPIFieldValue =
| CustomAPIString
| CustomAPINumber
| CustomAPIDate
| CustomAPIGeolocation;
export interface Result {
[key: string]: string | string[];
content_source_id: string;
last_updated: string;
external_id: string;
updated_at: string;
source: string;
[key: string]: CustomAPIFieldValue;
}
export interface OptionValue {

View file

@ -14,6 +14,12 @@ describe('getAsLocalDateTimeString', () => {
expect(getAsLocalDateTimeString(date)).toEqual(new Date(Date.parse(date)).toLocaleString());
});
it('returns null if passed value is not a string', () => {
const date = ['1', '2'];
expect(getAsLocalDateTimeString(date)).toEqual(null);
});
it('returns null if string cannot be parsed as date', () => {
const date = 'foo';

View file

@ -5,7 +5,11 @@
* 2.0.
*/
export const getAsLocalDateTimeString = (str: string) => {
const dateValue = Date.parse(str);
import { CustomAPIFieldValue } from '../types';
export const getAsLocalDateTimeString = (maybeDate: CustomAPIFieldValue) => {
if (typeof maybeDate !== 'string') return null;
const dateValue = Date.parse(maybeDate);
return dateValue ? new Date(dateValue).toLocaleString() : null;
};

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { CustomAPIFieldValue } from '../types';
const mimeTypes = {
'application/iwork-keynote-sffkey': 'Keynote',
'application/x-iwork-keynote-sffkey': 'Keynote',
@ -51,4 +53,5 @@ const mimeTypes = {
'video/quicktime': 'MOV',
} as { [key: string]: string };
export const mimeType = (type: string) => mimeTypes[type.toLowerCase()] || type;
export const mimeType = (type: CustomAPIFieldValue) =>
mimeTypes[type.toString().toLowerCase()] || type;

View file

@ -62,7 +62,7 @@ export const ExampleResultDetailCard: React.FC = () => {
<div className="example-result-detail-card__content">
{detailFields.length > 0 ? (
detailFields.map(({ fieldName, label }, index) => {
const value = result[fieldName] as string;
const value = result[fieldName];
const dateValue = getAsLocalDateTimeString(value);
return (

View file

@ -117,7 +117,7 @@ export const ExampleSearchResultGroup: React.FC = () => {
data-test-subj="MediaTypeField"
>
<span className="example-search-result__tag-content">
{mimeType(result[mediaTypeField] as string)}
{mimeType(result[mediaTypeField])}
</span>
</div>
)}
@ -135,8 +135,7 @@ export const ExampleSearchResultGroup: React.FC = () => {
by {result[updatedByField]}&nbsp;
</span>
)}
{getAsLocalDateTimeString(result.last_updated as string) ||
result.last_updated}
{getAsLocalDateTimeString(result.last_updated) || result.last_updated}
</span>
</div>
</div>

View file

@ -109,7 +109,7 @@ export const ExampleStandoutResult: React.FC = () => {
data-test-subj="MediaTypeField"
>
<span className="example-search-result__tag-content">
{mimeType(result[mediaTypeField] as string)}
{mimeType(result[mediaTypeField])}
</span>
</div>
)}
@ -127,7 +127,7 @@ export const ExampleStandoutResult: React.FC = () => {
by {result[updatedByField]}&nbsp;
</span>
)}
{getAsLocalDateTimeString(result.last_updated as string) || result.last_updated}
{getAsLocalDateTimeString(result.last_updated) || result.last_updated}
</span>
</div>
</div>

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { exampleResult } from '../../../../__mocks__/content_sources.mock';
import React from 'react';
import { shallow } from 'enzyme';
@ -12,7 +14,11 @@ import { shallow } from 'enzyme';
import { SubtitleField } from './subtitle_field';
describe('SubtitleField', () => {
const result = { foo: 'bar' };
const result = {
...exampleResult.exampleDocuments[0],
foo: 'bar',
};
it('renders', () => {
const props = {
result,

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { exampleResult } from '../../../../__mocks__/content_sources.mock';
import React from 'react';
import { shallow } from 'enzyme';
@ -12,7 +14,10 @@ import { shallow } from 'enzyme';
import { TitleField } from './title_field';
describe('TitleField', () => {
const result = { foo: 'bar' };
const result = {
...exampleResult.exampleDocuments[0],
foo: 'bar',
};
it('renders', () => {
const props = {
result,
@ -26,7 +31,10 @@ describe('TitleField', () => {
it('handles title when array', () => {
const props = {
result: { foo: ['baz', 'bar'] },
result: {
...exampleResult.exampleDocuments[0],
foo: ['baz', 'bar'],
},
titleField: 'foo',
titleFieldHover: false,
};

View file

@ -137,7 +137,7 @@ export const SourceContent: React.FC = () => {
<TruncatedContent tooltipType="title" content={url.toString()} length={MAX_LENGTH} />
)}
{urlFieldIsLinkable && (
<EuiLink target="_blank" href={url}>
<EuiLink target="_blank" href={url.toString()}>
<TruncatedContent tooltipType="title" content={url.toString()} length={MAX_LENGTH} />
</EuiLink>
)}