Convert mappings to TypeScript (#32842)

* Convert mappings to TypeScript

* Use any for FieldMapping

* Revert "Use any for FieldMapping"

This reverts commit 2042601f42.

* Use union type for FieldMapping

* Wrap toPath inside module declaration
This commit is contained in:
Mike Côté 2019-03-13 10:41:43 -04:00 committed by GitHub
parent 2b606b25ed
commit 0f9779e842
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 196 additions and 149 deletions

View file

@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { getTypes, getProperty, getRootProperties, getRootPropertiesObjects } from './lib';
export { FieldMapping, MappingMeta, MappingProperties, IndexMapping } from './types';

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { FieldMapping, IndexMapping } from '../types';
import { getProperty } from './get_property';
const MAPPINGS = {
@ -24,12 +25,12 @@ const MAPPINGS = {
foo: {
properties: {
name: {
type: 'text'
type: 'text',
},
description: {
type: 'text'
}
}
type: 'text',
},
},
},
bar: {
properties: {
@ -37,16 +38,16 @@ const MAPPINGS = {
type: 'text',
fields: {
box: {
type: 'keyword'
}
}
}
}
}
}
type: 'keyword',
},
},
},
},
},
},
};
function runTest(key, mapping) {
function runTest(key: string | string[], mapping: IndexMapping | FieldMapping) {
expect(typeof key === 'string' || Array.isArray(key)).toBeTruthy();
expect(typeof mapping).toBe('object');

View file

@ -18,40 +18,27 @@
*/
import toPath from 'lodash/internal/toPath';
import { CoreFieldMapping, FieldMapping, IndexMapping } from '../types';
/**
* Recursively read properties from the mapping object of type "object"
* until the `path` is resolved.
* @param {EsObjectMapping} mapping
* @param {Array<string>} path
* @return {Objects|undefined}
*/
function getPropertyMappingFromObjectMapping(mapping, path) {
const props = mapping && (mapping.properties || mapping.fields);
function getPropertyMappingFromObjectMapping(
mapping: IndexMapping | FieldMapping,
path: string[]
): FieldMapping | undefined {
const props =
(mapping && (mapping as IndexMapping).properties) ||
(mapping && (mapping as CoreFieldMapping).fields);
if (!props) {
return undefined;
}
if (path.length > 1) {
return getPropertyMappingFromObjectMapping(
props[path[0]],
path.slice(1)
);
return getPropertyMappingFromObjectMapping(props[path[0]], path.slice(1));
} else {
return props[path[0]];
}
}
/**
* Get the mapping for a specific property within the root type of the EsMappingsDsl.
* @param {EsMappingsDsl} mappings
* @param {string|Array<string>} path
* @return {Object|undefined}
*/
export function getProperty(mappings, path) {
return getPropertyMappingFromObjectMapping(
mappings,
toPath(path)
);
export function getProperty(mappings: IndexMapping | FieldMapping, path: string | string[]) {
return getPropertyMappingFromObjectMapping(mappings, toPath(path));
}

View file

@ -17,6 +17,8 @@
* under the License.
*/
import { IndexMapping } from '../types';
/**
* Get the property mappings for the root type in the EsMappingsDsl
*
@ -31,14 +33,11 @@
*
* This data can be found at `{indexName}.mappings.{typeName}.properties`
* in the es indices.get() response.
*
* @param {EsMappingsDsl} mapping
* @return {EsPropertyMappings}
*/
export function getRootProperties(mapping) {
if (mapping.type !== 'object' && !mapping.properties) {
export function getRootProperties(mapping: IndexMapping) {
if (!mapping.properties) {
throw new TypeError('Unable to get property names non-object root mapping');
}
return mapping.properties || {};
return mapping.properties;
}

View file

@ -23,16 +23,16 @@ test(`returns single object with properties`, () => {
const mappings = {
properties: {
foo: {
properties: {}
}
}
properties: {},
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
properties: {}
}
properties: {},
},
});
});
@ -40,16 +40,16 @@ test(`returns single object with type === 'object'`, () => {
const mappings = {
properties: {
foo: {
type: 'object'
}
}
type: 'object',
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
type: 'object'
}
type: 'object',
},
});
});
@ -57,22 +57,22 @@ test(`returns two objects with properties`, () => {
const mappings = {
properties: {
foo: {
properties: {}
properties: {},
},
bar: {
properties: {}
}
}
properties: {},
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
properties: {}
properties: {},
},
bar: {
properties: {}
}
properties: {},
},
});
});
@ -80,22 +80,22 @@ test(`returns two objects with type === 'object'`, () => {
const mappings = {
properties: {
foo: {
type: 'object'
type: 'object',
},
bar: {
type: 'object'
}
}
type: 'object',
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
type: 'object'
type: 'object',
},
bar: {
type: 'object'
}
type: 'object',
},
});
});
@ -103,9 +103,9 @@ test(`excludes objects without properties and type of keyword`, () => {
const mappings = {
properties: {
foo: {
type: 'keyword'
}
}
type: 'keyword',
},
},
};
const result = getRootPropertiesObjects(mappings);
@ -116,12 +116,12 @@ test(`excludes two objects without properties and type of keyword`, () => {
const mappings = {
properties: {
foo: {
type: 'keyword'
type: 'keyword',
},
bar: {
type: 'keyword'
}
}
type: 'keyword',
},
},
};
const result = getRootPropertiesObjects(mappings);
@ -132,19 +132,19 @@ test(`includes one object with properties and excludes one object without proper
const mappings = {
properties: {
foo: {
properties: {}
properties: {},
},
bar: {
type: 'keyword'
}
}
type: 'keyword',
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
properties: {}
}
properties: {},
},
});
});
@ -152,19 +152,19 @@ test(`includes one object with type === 'object' and excludes one object without
const mappings = {
properties: {
foo: {
type: 'object'
type: 'object',
},
bar: {
type: 'keyword'
}
}
type: 'keyword',
},
},
};
const result = getRootPropertiesObjects(mappings);
expect(result).toEqual({
foo: {
type: 'object'
}
type: 'object',
},
});
});

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { ComplexFieldMapping, IndexMapping, MappingProperties } from '../types';
import { getRootProperties } from './get_root_properties';
/**
@ -34,20 +35,21 @@ import { getRootProperties } from './get_root_properties';
* @return {EsPropertyMappings}
*/
const blacklist = [
'migrationVersion',
'references',
];
const blacklist = ['migrationVersion', 'references'];
export function getRootPropertiesObjects(mappings) {
export function getRootPropertiesObjects(mappings: IndexMapping) {
const rootProperties = getRootProperties(mappings);
return Object.entries(rootProperties).reduce((acc, [key, value]) => {
// we consider the existence of the properties or type of object to designate that this is an object datatype
if (!blacklist.includes(key) && (value.properties || value.type === 'object')) {
acc[key] = value;
}
return acc;
}, {});
return Object.entries(rootProperties).reduce(
(acc, [key, value]) => {
// we consider the existence of the properties or type of object to designate that this is an object datatype
if (
!blacklist.includes(key) &&
((value as ComplexFieldMapping).properties || value.type === 'object')
) {
acc[key] = value;
}
return acc;
},
{} as MappingProperties
);
}

View file

@ -17,12 +17,11 @@
* under the License.
*/
import { IndexMapping } from '../types';
/**
* Get the names of the types defined in the EsMappingsDsl
*
* @param {EsMappingsDsl} mappings
* @return {Array<string>}
*/
export function getTypes(mappings) {
export function getTypes(mappings: IndexMapping) {
return Object.keys(mappings).filter(type => type !== '_default_');
}

View file

@ -0,0 +1,58 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// FieldMapping isn't 1:1 with the options available,
// modify as needed.
export interface CoreFieldMapping {
type: string;
fields?: {
[subfield: string]: {
type: string;
};
};
}
// FieldMapping isn't 1:1 with the options available,
// modify as needed.
export interface ComplexFieldMapping {
dynamic?: string;
type?: string;
properties: MappingProperties;
}
export type FieldMapping = CoreFieldMapping | ComplexFieldMapping;
export interface MappingProperties {
[field: string]: FieldMapping;
}
export interface MappingMeta {
// A dictionary of key -> md5 hash (e.g. 'dashboard': '24234qdfa3aefa3wa')
// with each key being a root-level mapping property, and each value being
// the md5 hash of that mapping's value when the index was created.
migrationMappingPropertyHashes?: { [k: string]: string };
}
// IndexMapping isn't 1:1 with the options available,
// modify as needed.
export interface IndexMapping {
dynamic?: string;
properties: MappingProperties;
_meta?: MappingMeta;
}

View file

@ -17,8 +17,8 @@
* under the License.
*/
import { IndexMapping } from './../../../mappings';
import { buildActiveMappings, diffMappings } from './build_active_mappings';
import { IndexMapping } from './call_cluster';
describe('buildActiveMappings', () => {
test('combines all mappings and includes core mappings', () => {
@ -39,7 +39,7 @@ describe('buildActiveMappings', () => {
});
test('disallows mappings with leading underscore', () => {
const properties = { _hm: 'You shall not pass!' };
const properties = { _hm: { type: 'keyword' } };
expect(() => buildActiveMappings({ properties })).toThrow(
/Invalid mapping \"_hm\"\. Mappings cannot start with _/
@ -48,9 +48,9 @@ describe('buildActiveMappings', () => {
test('generated hashes are stable', () => {
const properties = {
aaa: { a: '...', b: '...', c: new Date('2019-01-02'), d: [{ hello: 'world' }] },
bbb: { c: new Date('2019-01-02'), d: [{ hello: 'world' }], a: '...', b: '...' },
ccc: { c: new Date('2020-01-02'), d: [{ hello: 'world' }], a: '...', b: '...' },
aaa: { type: 'keyword', fields: { a: { type: 'keyword' }, b: { type: 'text' } } },
bbb: { fields: { b: { type: 'text' }, a: { type: 'keyword' } }, type: 'keyword' },
ccc: { fields: { b: { type: 'text' }, a: { type: 'text' } }, type: 'keyword' },
};
const mappings = buildActiveMappings({ properties });
@ -108,7 +108,7 @@ describe('diffMappings', () => {
},
dynamic: 'strict',
properties: {
foo: 'bar',
foo: { type: 'keyword' },
},
};
const expected: IndexMapping = {
@ -117,7 +117,7 @@ describe('diffMappings', () => {
},
dynamic: 'strict',
properties: {
foo: 'baz',
foo: { type: 'text' },
},
};

View file

@ -23,7 +23,7 @@
import crypto from 'crypto';
import _ from 'lodash';
import { IndexMapping, MappingProperties } from './call_cluster';
import { IndexMapping, MappingProperties } from './../../../mappings';
/**
* Creates an index mapping with the core properties required by saved object

View file

@ -23,6 +23,8 @@
* funcationality contained here.
*/
import { IndexMapping } from '../../../mappings';
export interface CallCluster {
(path: 'bulk', opts: { body: object[] }): Promise<BulkResult>;
(path: 'count', opts: CountOpts): Promise<{ count: number; _shards: ShardsInfo }>;
@ -185,20 +187,3 @@ export interface IndexInfo {
export interface IndicesInfo {
[index: string]: IndexInfo;
}
export interface MappingProperties {
[type: string]: any;
}
export interface MappingMeta {
// A dictionary of key -> md5 hash (e.g. 'dashboard': '24234qdfa3aefa3wa')
// with each key being a root-level mapping property, and each value being
// the md5 hash of that mapping's value when the index was created.
migrationMappingPropertyHashes?: { [k: string]: string };
}
export interface IndexMapping {
dynamic: string;
properties: MappingProperties;
_meta?: MappingMeta;
}

View file

@ -220,7 +220,7 @@ describe('ElasticIndex', () => {
expect(arg.body).toEqual({
mappings: {
dynamic: 'strict',
properties: { foo: 'bar' },
properties: { foo: { type: 'keyword' } },
},
settings: { auto_expand_replicas: '0-1', number_of_shards: 1 },
});
@ -264,7 +264,7 @@ describe('ElasticIndex', () => {
indexName: '.ze-index',
mappings: {
dynamic: 'strict',
properties: { foo: 'bar' },
properties: { foo: { type: 'keyword' } },
},
};
await Index.convertToAlias(callCluster as any, info, '.muchacha', 10);
@ -286,7 +286,7 @@ describe('ElasticIndex', () => {
expect(arg.body).toEqual({
mappings: {
dynamic: 'strict',
properties: { foo: 'bar' },
properties: { foo: { type: 'keyword' } },
},
settings: { auto_expand_replicas: '0-1', number_of_shards: 1 },
});
@ -323,7 +323,7 @@ describe('ElasticIndex', () => {
indexName: '.ze-index',
mappings: {
dynamic: 'strict',
properties: { foo: 'bar' },
properties: { foo: { type: 'keyword' } },
},
};
await expect(Index.convertToAlias(callCluster as any, info, '.muchacha', 10)).rejects.toThrow(

View file

@ -23,15 +23,9 @@
*/
import _ from 'lodash';
import { IndexMapping } from '../../../mappings';
import { MigrationVersion } from '../../serialization';
import {
AliasAction,
CallCluster,
IndexMapping,
NotFound,
RawDoc,
ShardsInfo,
} from './call_cluster';
import { AliasAction, CallCluster, NotFound, RawDoc, ShardsInfo } from './call_cluster';
// @ts-ignore untyped dependency
import { getTypes } from '../../../mappings';

View file

@ -20,6 +20,6 @@
export { DocumentMigrator } from './document_migrator';
export { IndexMigrator } from './index_migrator';
export { buildActiveMappings } from './build_active_mappings';
export { CallCluster, MappingProperties } from './call_cluster';
export { CallCluster } from './call_cluster';
export { LogFn } from './migration_logger';
export { MigrationResult } from './migration_coordinator';

View file

@ -25,8 +25,9 @@
*/
import { SavedObjectsSerializer } from '../../serialization';
import { MappingProperties } from './../../../mappings';
import { buildActiveMappings } from './build_active_mappings';
import { CallCluster, MappingProperties } from './call_cluster';
import { CallCluster } from './call_cluster';
import { VersionedTransformer } from './document_migrator';
import { fetchInfo, FullIndexInfo } from './elastic_index';
import { LogFn, Logger, MigrationLogger } from './migration_logger';

View file

@ -23,10 +23,11 @@
*/
import { once } from 'lodash';
import { MappingProperties } from '../../../mappings';
import { SavedObjectsSchema, SavedObjectsSchemaDefinition } from '../../schema';
import { RawSavedObjectDoc, SavedObjectsSerializer } from '../../serialization';
import { docValidator } from '../../validation';
import { buildActiveMappings, CallCluster, IndexMigrator, LogFn, MappingProperties } from '../core';
import { buildActiveMappings, CallCluster, IndexMigrator, LogFn } from '../core';
import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator';
export interface KbnServer {

View file

@ -17,9 +17,8 @@
* under the License.
*/
export {
getTypes,
getProperty,
getRootProperties,
getRootPropertiesObjects,
} from './lib';
declare module 'lodash/internal/toPath' {
function toPath(value: string | string[]): string[]
export = toPath;
}