remove remaining idx usage (#52354)

* remove remaining idx usage

* handle possibly undefined value

* update NOTICE.txt
This commit is contained in:
Spencer 2019-12-09 15:59:07 -07:00 committed by GitHub
parent a863dca9c2
commit 3293ede421
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 43 additions and 1271 deletions

View file

@ -186,32 +186,6 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
This product includes code that is based on facebookincubator/idx, which was
available under a "MIT" license.
MIT License
Copyright (c) 2013-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
This product includes code that was extracted from angular@1.3.
Original license:

View file

@ -28,6 +28,11 @@ module.exports = {
to: false,
disallowedMessage: `Don't use 'mkdirp', use the new { recursive: true } option of Fs.mkdir instead`
},
{
from: '@kbn/elastic-idx',
to: false,
disallowedMessage: `Don't use idx(), use optional chaining syntax instead https://ela.st/optchain`
},
{
from: 'x-pack',
toRelative: 'x-pack',

View file

@ -35,13 +35,6 @@ const plugins = [
// Need this since we are using TypeScript 3.7+
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
];
const isTestEnv = process.env.BABEL_ENV === 'test' || process.env.NODE_ENV === 'test';
// Only load the idx plugin in non-test environments, since it conflicts with
// Jest's coverage mapping.
if (!isTestEnv) {
plugins.push(require.resolve('@kbn/elastic-idx/babel'));
}
module.exports = {
presets: [require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],

View file

@ -12,7 +12,6 @@
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@kbn/elastic-idx": "1.0.0",
"babel-plugin-add-module-exports": "^1.0.2",
"babel-plugin-filter-imports": "^3.0.0",
"babel-plugin-transform-define": "^1.3.1",

View file

@ -1,3 +0,0 @@
/tsconfig.json
/src
/babel/index.test.js

View file

@ -1,76 +0,0 @@
Kibana elastic-idx Library
==========================
The `@kbn/elastic-idx` package provides the `idx` function used for optional
chaining. Currently, the optional chaining draft is in stage 1, making it too
uncertain to add syntax support within Kibana. Other optional chaining
libraries require the Proxy object to be polyfilled for browser support,
however, this polyfill is not fully supported across all browsers that Kibana
requires. The facebookincubator `idx` project
(https://github.com/facebookincubator/idx) provides an answer to this with a
specific implementation that is understood by TypeScript so that type
information does not get lost (unlike lodash get) The `@kbn/elastic-idx`
library makes use the `idx` idiom but differs in the way null values within the
property chain are handled.
Similar to the facebookincubator `idx` project, `@kbn/elastic-idx` also
provides the Babel plugin to transform `idx()` function calls into the expanded
form. This Babel plugin was based off the facebookincubator `idx` Babel
plugin, since the invocation syntax is almost identical, but the transformed
code differs to match how the `@kbn/elastic-idx` library treats null values.
App Usage
----------
Within Kibana, `@kbn/elastic-idx` can be imported and used in any JavaScript or
TypeScript project:
```
import { idx } from '@kbn/elastic-idx';
const obj0 = { a: { b: { c: { d: 'iamdefined' } } } };
const obj1 = { a: { b: null } };
idx(obj0, _ => _.a.b.c.d); // returns 'iamdefined'
idx(obj1, _ => _.a.b.c.e); // returns undefined
idx(obj1, _ => _.a.b); // returns null
```
Build Optimization
-------------------
Similar to the facebookincubator `idx` project, it is NOT RECOMMENDED to use
idx in shipped app code. The implementation details which make
`@kbn/elastic-idx` possible comes at a non-negligible performance cost. This
usually isn't noticable during development, but for production builds, it is
recommended to transform idx calls into native, expanded form JS. Use the
plugin `@kbn/elastic-idx/babel` within your Babel configuration:
```
{ "plugins": [ "@kbn/elastic-idx/babel" ] }
```
The resulting Babel transforms the following:
```
import { idx } from '@kbn/elastic-idx';
const obj = { a: { b: { c: { d: 'iamdefined' } } } };
idx(obj, _ => _.a.b.c.d);
```
into this:
```
obj != null &&
obj.a != null &&
obj.a.b != null &&
obj.a.b.c != null ?
obj.a.b.c.d : undefined
```
Note that this also removes the import statement from the source code, since it
no longer needs to be bundled.
Testing
--------
Tests can be run with `npm test`. This includes "functional" tests that
transform and evaluate idx calls.

View file

@ -1,304 +0,0 @@
/* eslint-disable @kbn/eslint/require-license-header */
/* @notice
* This product includes code that is based on facebookincubator/idx, which was
* available under a "MIT" license.
*
* MIT License
*
* Copyright (c) 2013-present, Facebook, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* eslint strict: 0, new-cap: 0 */
'use strict';
module.exports = context => {
const t = context.types;
const idxRe = /\bidx\b/;
function checkIdxArguments(file, node) {
const args = node.arguments;
if (args.length !== 2) {
throw file.buildCodeFrameError(node, 'The `idx` function takes exactly two arguments.');
}
const arrowFunction = args[1];
if (!t.isArrowFunctionExpression(arrowFunction)) {
throw file.buildCodeFrameError(
arrowFunction,
'The second argument supplied to `idx` must be an arrow function.'
);
}
if (!t.isExpression(arrowFunction.body)) {
throw file.buildCodeFrameError(
arrowFunction.body,
'The body of the arrow function supplied to `idx` must be a single ' +
'expression (without curly braces).'
);
}
if (arrowFunction.params.length !== 1) {
throw file.buildCodeFrameError(
arrowFunction.params[2] || arrowFunction,
'The arrow function supplied to `idx` must take exactly one parameter.'
);
}
const input = arrowFunction.params[0];
if (!t.isIdentifier(input)) {
throw file.buildCodeFrameError(
arrowFunction.params[0],
'The parameter supplied to `idx` must be an identifier.'
);
}
}
function checkIdxBindingNode(file, node) {
if (t.isImportDeclaration(node)) {
// E.g. `import '...'`
if (node.specifiers.length === 0) {
throw file.buildCodeFrameError(node, 'The idx import must have a value.');
}
// E.g. `import A, {B} from '...'`
// `import A, * as B from '...'`
// `import {A, B} from '...'`
if (node.specifiers.length > 1) {
throw file.buildCodeFrameError(
node.specifiers[1],
'The idx import must be a single specifier.'
);
}
// `importKind` is not a property unless flow syntax is enabled.
// On specifiers, `importKind` is not "value" when it's not a type, it's
// `null`.
// E.g. `import type {...} from '...'`
// `import typeof {...} from '...'`
// `import {type ...} from '...'`.
// `import {typeof ...} from '...'`
if (
node.importKind === 'type' ||
node.importKind === 'typeof' ||
node.specifiers[0].importKind === 'type' ||
node.specifiers[0].importKind === 'typeof'
) {
throw file.buildCodeFrameError(node, 'The idx import must be a value import.');
}
} else if (t.isVariableDeclarator(node)) {
// E.g. var {idx} or var [idx]
if (!t.isIdentifier(node.id)) {
throw file.buildCodeFrameError(
node.specifiers[0],
'The idx declaration must be an identifier.'
);
}
}
}
class UnsupportedNodeTypeError extends Error {
constructor(node, ...params) {
super(`Node type is not supported: ${node.type}`, ...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, UnsupportedNodeTypeError);
}
this.name = 'UnsupportedNodeTypeError';
}
}
function getDeepProperties(node, properties = [], computedProperties = new Set()) {
if (t.isMemberExpression(node)) {
if (node.computed) {
computedProperties.add(node.property);
}
return getDeepProperties(node.object, [node.property, ...properties], computedProperties);
} else if (t.isIdentifier(node)) {
return [[node, ...properties], computedProperties];
}
throw new UnsupportedNodeTypeError(node);
}
function buildMemberChain(properties, computedProperties) {
if (properties.length > 1) {
const lead = properties.slice(0, properties.length - 1);
const last = properties[properties.length - 1];
return t.MemberExpression(
buildMemberChain(lead, computedProperties),
last,
computedProperties.has(last)
);
} else if (properties.length === 1) {
return properties[0];
}
return t.identifier('undefined');
}
function buildExpandedMemberNullChecks(
leadingProperties = [],
trailingProperties = [],
computedProperties
) {
const propertyChainNullCheck = t.BinaryExpression(
'!=',
buildMemberChain(leadingProperties, computedProperties),
t.NullLiteral()
);
if (trailingProperties.length <= 1) {
return propertyChainNullCheck;
}
const [headTrailingProperty, ...tailProperties] = trailingProperties;
return t.LogicalExpression(
'&&',
propertyChainNullCheck,
buildExpandedMemberNullChecks(
[...leadingProperties, headTrailingProperty],
tailProperties,
computedProperties
)
);
}
function buildExpandedMemberAccess(node, state) {
let baseNode;
let properties;
let computedProperties;
try {
[[baseNode, ...properties], computedProperties] = getDeepProperties(node);
} catch (error) {
if (error instanceof UnsupportedNodeTypeError) {
throw state.file.buildCodeFrameError(
node,
'idx callbacks may only access properties on the callback parameter.'
);
}
throw error;
}
if (baseNode.name !== state.base.name) {
throw state.file.buildCodeFrameError(
node,
'The parameter of the arrow function supplied to `idx` must match ' +
'the base of the body expression.'
);
}
return t.ConditionalExpression(
buildExpandedMemberNullChecks([state.input], properties, computedProperties),
buildMemberChain([state.input, ...properties], computedProperties),
t.identifier('undefined')
);
}
function visitIdxCallExpression(path, state) {
const node = path.node;
checkIdxArguments(state.file, node);
const replacement = buildExpandedMemberAccess(node.arguments[1].body, {
file: state.file,
input: node.arguments[0],
base: node.arguments[1].params[0],
});
path.replaceWith(replacement);
}
function isIdxImportOrRequire(node, name) {
if (t.isImportDeclaration(node)) {
if (name instanceof RegExp) {
return name.test(node.source.value);
} else {
return t.isStringLiteral(node.source, { value: name });
}
} else if (t.isVariableDeclarator(node)) {
return (
t.isCallExpression(node.init) &&
t.isIdentifier(node.init.callee, { name: 'require' }) &&
(name instanceof RegExp
? name.test(node.init.arguments[0].value)
: t.isLiteral(node.init.arguments[0], { value: name }))
);
} else {
return false;
}
}
const declareVisitor = {
'ImportDeclaration|VariableDeclarator'(path, state) {
if (!isIdxImportOrRequire(path.node, state.importName)) {
return;
}
checkIdxBindingNode(state.file, path.node);
const bindingName = t.isImportDeclaration(path.node)
? path.node.specifiers[0].local.name
: path.node.id.name;
const idxBinding = path.scope.getOwnBinding(bindingName);
idxBinding.constantViolations.forEach(refPath => {
throw state.file.buildCodeFrameError(refPath.node, '`idx` cannot be redefined.');
});
let didTransform = false;
let didSkip = false;
// Traverse the references backwards to process inner calls before
// outer calls.
idxBinding.referencePaths
.slice()
.reverse()
.forEach(refPath => {
if (refPath.node === idxBinding.node) {
// Do nothing...
} else if (refPath.parentPath.isMemberExpression()) {
visitIdxCallExpression(refPath.parentPath.parentPath, state);
didTransform = true;
} else if (refPath.parentPath.isCallExpression()) {
visitIdxCallExpression(refPath.parentPath, state);
didTransform = true;
} else {
// Should this throw?
didSkip = true;
}
});
if (didTransform && !didSkip) {
path.remove();
}
},
};
return {
visitor: {
Program(path, state) {
const importName = state.opts.importName || '@kbn/elastic-idx';
// If there can't reasonably be an idx call, exit fast.
if (importName !== '@kbn/elastic-idx' || idxRe.test(state.file.code)) {
// We're very strict about the shape of idx. Some transforms, like
// "babel-plugin-transform-async-to-generator", will convert arrow
// functions inside async functions into regular functions. So we do
// our transformation before any one else interferes.
const newState = { file: state.file, importName };
path.traverse(declareVisitor, newState);
}
},
},
};
};

View file

@ -1,694 +0,0 @@
/* eslint-disable @kbn/eslint/require-license-header */
/* @notice
* This product includes code that is based on facebookincubator/idx, which was
* available under a "MIT" license.
*
* MIT License
*
* Copyright (c) 2013-present, Facebook, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
'use strict'; // eslint-disable-line strict
jest.autoMockOff();
const { transformSync: babelTransform } = require('@babel/core');
const babelPluginIdx = require('./index');
const transformAsyncToGenerator = require('@babel/plugin-transform-async-to-generator');
const vm = require('vm');
function transform(source, plugins, options) {
return babelTransform(source, {
plugins: plugins || [[babelPluginIdx, options]],
babelrc: false,
highlightCode: false,
}).code;
}
const asyncToGeneratorHelperCode = `
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
`;
function stringByTrimmingSpaces(string) {
return string.replace(/\s+/g, '');
}
expect.extend({
toTransformInto: (input, expected) => {
const plugins = typeof input === 'string' ? null : input.plugins;
const options = typeof input === 'string' ? undefined : input.options;
const code = typeof input === 'string' ? input : input.code;
const actual = transform(code, plugins, options);
const pass = stringByTrimmingSpaces(actual) === stringByTrimmingSpaces(expected);
return {
pass,
message: () =>
'Expected input to transform into:\n' + expected + '\n' + 'Instead, got:\n' + actual,
};
},
toThrowTransformError: (input, expected) => {
try {
const plugins = typeof input === 'string' ? null : input.plugins;
const options = typeof input === 'string' ? undefined : input.options;
const code = typeof input === 'string' ? input : input.code;
transform(code, plugins, options);
} catch (error) {
const actual = /^.+:\s*(.*)/.exec(error.message)[1]; // Strip "undefined: " and code snippet
return {
pass: actual === expected,
message: () =>
'Expected transform to throw "' + expected + '", but instead ' + 'got "' + actual + '".',
};
}
return {
pass: false,
message: () => 'Expected transform to throw "' + expected + '".',
};
},
toReturn: (input, expected) => {
const code = transform(input, undefined);
const actual = vm.runInNewContext(code);
return {
pass: actual === expected,
message: () => 'Expected "' + expected + '" but got "' + actual + '".',
};
},
});
describe('kbn-babel-plugin-apm-idx', () => {
it('transforms member expressions', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _.b.c.d.e);
`).toTransformInto(`
base != null && base.b != null && base.b.c != null && base.b.c.d != null
? base.b.c.d.e
: undefined;
`);
});
it('throws on call expressions', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _.b.c(...foo)().d(bar, null, [...baz]));
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
});
it('transforms bracket notation', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _["b"][0][c + d]);
`).toTransformInto(`
base != null && base["b"] != null && base["b"][0] != null ? base["b"][0][c + d] : undefined;
`);
});
it('throws on bracket notation call expressions', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _["b"](...foo)()[0][c + d](bar, null, [...baz]));
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
});
it('transforms combination of both member access notations', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _.a["b"].c[d[e[f]]].g);
`).toTransformInto(`
base != null && base.a != null && base.a["b"] != null && base.a["b"].c != null && base.a["b"].c[d[e[f]]] != null
? base.a["b"].c[d[e[f]]].g
: undefined;
`);
});
it('transforms if the base is an expression', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(this.props.base[5], _ => _.property);
`).toTransformInto(`
this.props.base[5] != null ? this.props.base[5].property : undefined;
`);
});
it('throws if the arrow function has more than one param', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, (a, b) => _.property);
`).toThrowTransformError(
'The arrow function supplied to `idx` must take exactly one parameter.'
);
});
it('throws if the arrow function has an invalid base', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, a => b.property)
`).toThrowTransformError(
'The parameter of the arrow function supplied to `idx` must match the ' +
'base of the body expression.'
);
});
it('throws if the arrow function expression has non-properties/methods', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => (_.a++).b.c);
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
});
it('throws if the body of the arrow function is not an expression', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => {})
`).toThrowTransformError(
'The body of the arrow function supplied to `idx` must be a single ' +
'expression (without curly braces).'
);
});
it('ignores non-function call idx', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
result = idx;
`).toTransformInto(`
import { idx } from '@kbn/elastic-idx';
result = idx;
`);
});
it('throws if idx is called with zero arguments', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx();
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
});
it('throws if idx is called with one argument', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(1);
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
});
it('throws if idx is called with three arguments', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(1, 2, 3);
`).toThrowTransformError('The `idx` function takes exactly two arguments.');
});
it('transforms idx calls as part of another expressions', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
paddingStatement();
a = idx(base, _ => _.b[c]);
`).toTransformInto(`
paddingStatement();
a = base != null && base.b != null ? base.b[c] : undefined;
`);
});
it('transforms nested idx calls', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(
idx(
idx(base, _ => _.a.b),
_ => _.c.d
),
_ => _.e.f
);
`).toTransformInto(`
(
(base != null && base.a != null ? base.a.b : undefined) != null &&
(base != null && base.a != null ? base.a.b : undefined).c != null ?
(base != null && base.a != null ? base.a.b : undefined).c.d :
undefined
) != null
&&
(
(base != null && base.a != null ? base.a.b : undefined) != null &&
(base != null && base.a != null ? base.a.b : undefined).c != null ?
(base != null && base.a != null ? base.a.b : undefined).c.d :
undefined
).e != null ?
(
(base != null && base.a != null ? base.a.b : undefined) != null &&
(base != null && base.a != null ? base.a.b : undefined).c != null ?
(base != null && base.a != null ? base.a.b : undefined).c.d :
undefined
).e.f :
undefined;
`);
});
it('transforms idx calls inside async functions (plugin order #1)', () => {
expect({
plugins: [babelPluginIdx, transformAsyncToGenerator],
code: `
import { idx } from '@kbn/elastic-idx';
async function f() {
idx(base, _ => _.b.c.d.e);
}
`,
}).toTransformInto(`
${asyncToGeneratorHelperCode}
function f() {
return _f.apply(this, arguments);
}
function _f() {
_f = _asyncToGenerator(function* () {
base != null && base.b != null && base.b.c != null && base.b.c.d != null ? base.b.c.d.e : undefined;
});
return _f.apply(this, arguments);
}
`);
});
it('transforms idx calls inside async functions (plugin order #2)', () => {
expect({
plugins: [transformAsyncToGenerator, babelPluginIdx],
code: `
import { idx } from '@kbn/elastic-idx';
async function f() {
idx(base, _ => _.b.c.d.e);
}
`,
}).toTransformInto(`
${asyncToGeneratorHelperCode}
function f() {
return _f.apply(this, arguments);
}
function _f() {
_f = _asyncToGenerator(function* () {
base != null && base.b != null && base.b.c != null && base.b.c.d != null ? base.b.c.d.e : undefined;
});
return _f.apply(this, arguments);
}
`);
});
it('transforms idx calls in async methods', () => {
expect({
plugins: [transformAsyncToGenerator, babelPluginIdx],
code: `
import { idx } from '@kbn/elastic-idx';
class Foo {
async bar() {
idx(base, _ => _.b);
return this;
}
}
`,
}).toTransformInto(`
${asyncToGeneratorHelperCode}
class Foo {
bar() {
var _this = this;
return _asyncToGenerator(function* () {
base != null ? base.b : undefined;
return _this;
})();
}
}
`);
});
it('transforms idx calls when an idx import binding is in scope', () => {
expect(`
import idx from '@kbn/elastic-idx';
idx(base, _ => _.b);
`).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('transforms idx calls when an idx const binding is in scope', () => {
expect(`
const idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
`).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('transforms deep idx calls when an idx import binding is in scope', () => {
expect(`
import idx from '@kbn/elastic-idx';
function f() {
idx(base, _ => _.b);
}
`).toTransformInto(`
function f() {
base != null ? base.b : undefined;
}
`);
});
it('transforms deep idx calls when an idx const binding is in scope', () => {
expect(`
const idx = require('@kbn/elastic-idx');
function f() {
idx(base, _ => _.b);
}
`).toTransformInto(`
function f() {
base != null ? base.b : undefined;
}
`);
});
it('transforms idx calls when an idx is called as a member function on the binding in scope', () => {
expect(`
const elastic_idx = require("@kbn/elastic-idx");
const result = elastic_idx.idx(base, _ => _.a.b.c.d);
`).toTransformInto(`
const result = base != null &&
base.a != null &&
base.a.b != null &&
base.a.b.c != null ?
base.a.b.c.d :
undefined;
`);
});
it('throws on base call expressions', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _().b.c);
`).toThrowTransformError('idx callbacks may only access properties on the callback parameter.');
});
it('transforms when the idx parent is a scope creating expression', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
(() => idx(base, _ => _.b));
`).toTransformInto(`
() => base != null ? base.b : undefined;
`);
});
it('throws if redefined before use', () => {
expect(`
let idx = require('@kbn/elastic-idx');
idx = null;
idx(base, _ => _.b);
`).toThrowTransformError('`idx` cannot be redefined.');
});
it('throws if redefined after use', () => {
expect(`
let idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
idx = null;
`).toThrowTransformError('`idx` cannot be redefined.');
});
it('throws if there is a duplicate declaration', () => {
expect(() =>
transform(`
let idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
function idx() {}
`)
).toThrow();
});
it('handles sibling scopes with unique idx', () => {
expect(`
function aaa() {
const idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
}
function bbb() {
const idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
}
`).toTransformInto(`
function aaa() {
base != null ? base.b : undefined;
}
function bbb() {
base != null ? base.b : undefined;
}
`);
});
it('handles sibling scopes with and without idx', () => {
expect(`
function aaa() {
const idx = require('@kbn/elastic-idx');
idx(base, _ => _.b);
}
function bbb() {
idx(base, _ => _.b);
}
`).toTransformInto(`
function aaa() {
base != null ? base.b : undefined;
}
function bbb() {
idx(base, _ => _.b);
}
`);
});
it('handles nested scopes with shadowing', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _.b);
function aaa() {
idx(base, _ => _.b);
function bbb(idx) {
idx(base, _ => _.b);
}
}
`).toTransformInto(`
base != null ? base.b : undefined;
function aaa() {
base != null ? base.b : undefined;
function bbb(idx) {
idx(base, _ => _.b);
}
}
`);
});
it('handles named idx import', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
idx(base, _ => _.b);
`).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('throws on default plus named import', () => {
expect(`
import idx, {foo} from '@kbn/elastic-idx';
idx(base, _ => _.b);
`).toThrowTransformError('The idx import must be a single specifier.');
});
it('throws on default plus namespace import', () => {
expect(`
import idx, * as foo from '@kbn/elastic-idx';
idx(base, _ => _.b);
`).toThrowTransformError('The idx import must be a single specifier.');
});
it('throws on named default plus other import', () => {
expect(`
import {default as idx, foo} from '@kbn/elastic-idx';
idx(base, _ => _.b);
`).toThrowTransformError('The idx import must be a single specifier.');
});
it('unused idx import should be left alone', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
`).toTransformInto(`
import { idx } from '@kbn/elastic-idx';
`);
});
it('allows configuration of the import name', () => {
expect({
code: `
import { idx } from 'i_d_x';
idx(base, _ => _.b);
`,
options: { importName: 'i_d_x' },
}).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('follows configuration of the import name', () => {
expect({
code: `
import { idx } from '@kbn/elastic-idx';
import { idx as i_d_x } from 'i_d_x';
i_d_x(base, _ => _.b);
idx(base, _ => _.c);
`,
options: { importName: 'i_d_x' },
}).toTransformInto(`
import { idx } from '@kbn/elastic-idx';
base != null ? base.b : undefined;
idx(base, _ => _.c);
`);
});
it('allows configuration of the require name as a string', () => {
expect({
code: `
import { idx } from 'i_d_x';
idx(base, _ => _.b);
`,
options: { importName: 'i_d_x' },
}).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('allows configuration of the require name as a RegExp', () => {
expect({
code: `
import { idx } from '../../common/idx';
idx(base, _ => _.b);
`,
options: { importName: /.*idx$/ },
}).toTransformInto(`
base != null ? base.b : undefined;
`);
});
it('follows configuration of the require name', () => {
expect({
code: `
const idx = require('@kbn/elastic-idx');
const i_d_x = require('i_d_x');
i_d_x(base, _ => _.b);
idx(base, _ => _.c);
`,
options: { importName: 'i_d_x' },
}).toTransformInto(`
const idx = require('@kbn/elastic-idx');
base != null ? base.b : undefined;
idx(base, _ => _.c);
`);
});
describe('functional', () => {
it('works with only properties', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
const base = {a: {b: {c: 2}}};
idx(base, _ => _.a.b.c);
`).toReturn(2);
});
it('works with missing properties', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
const base = {a: {b: {}}};
idx(base, _ => _.a.b.c);
`).toReturn(undefined);
});
it('works with null properties', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
const base = {a: {b: null}};
idx(base, _ => _.a.b.c);
`).toReturn(undefined);
});
it('works with nested idx calls', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
const base = {a: {b: {c: {d: {e: {f: 2}}}}}};
idx(
idx(
idx(base, _ => _.a.b),
_ => _.c.d
),
_ => _.e.f
);
`).toReturn(2);
});
it('works with nested idx calls with missing properties', () => {
expect(`
import { idx } from '@kbn/elastic-idx';
const base = {a: {b: {c: null}}};
idx(
idx(
idx(base, _ => _.a.b),
_ => _.c.d
),
_ => _.e.f
);
`).toReturn(undefined);
});
});
});

View file

@ -1,28 +0,0 @@
{
"name": "@kbn/elastic-idx",
"version": "1.0.0",
"private": true,
"license": "Apache-2.0",
"description": "Library for optional chaining & the Babel plugin to transpile idx calls to plain, optimized JS",
"main": "target/index.js",
"types": "target/index.d.js",
"repository": {
"type": "git",
"url": "https://github.com/elastic/kibana/tree/master/packages/kbn-elastic-idx"
},
"scripts": {
"build": "tsc",
"kbn:bootstrap": "yarn build",
"kbn:watch": "yarn build --watch",
"test": "jest"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-transform-async-to-generator": "^7.5.0",
"jest": "^24.9.0",
"typescript": "3.7.2"
},
"jest": {
"testEnvironment": "node"
}
}

View file

@ -1,65 +0,0 @@
/*
* 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.
*/
/**
* DeepRequiredArray
* Nested array condition handler
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface DeepRequiredArray<T> extends Array<DeepRequired<T>> {}
/**
* DeepRequiredObject
* Nested object condition handler
*/
type DeepRequiredObject<T> = { [P in keyof T]-?: DeepRequired<T[P]> };
/**
* Function that has deeply required return type
*/
type FunctionWithRequiredReturnType<T extends (...args: any[]) => any> = T extends (
...args: infer A
) => infer R
? (...args: A) => DeepRequired<R>
: never;
/**
* DeepRequired
* Required that works for deeply nested structure
*/
type DeepRequired<T> = NonNullable<T> extends never
? T
: T extends any[]
? DeepRequiredArray<T[number]>
: T extends (...args: any[]) => any
? FunctionWithRequiredReturnType<T>
: NonNullable<T> extends object
? DeepRequiredObject<NonNullable<T>>
: T;
export function idx<T1, T2>(
input: T1,
accessor: (input: NonNullable<DeepRequired<T1>>) => T2
): T2 | undefined {
try {
return accessor(input as NonNullable<DeepRequired<T1>>);
} catch (error) {
return undefined;
}
}

View file

@ -1,8 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "./target"
},
"include": ["src/**/*"]
}

View file

@ -17,7 +17,6 @@
* under the License.
*/
import { idx } from '@kbn/elastic-idx';
import {
SavedObjectsClientContract,
SimpleSavedObject,
@ -64,7 +63,7 @@ export class IndexPatterns {
if (!this.savedObjectsCache) {
return [];
}
return this.savedObjectsCache.map(obj => idx(obj, _ => _.id));
return this.savedObjectsCache.map(obj => obj?.id);
};
getTitles = async (refresh: boolean = false): Promise<string[]> => {
@ -74,7 +73,7 @@ export class IndexPatterns {
if (!this.savedObjectsCache) {
return [];
}
return this.savedObjectsCache.map(obj => idx(obj, _ => _.attributes.title));
return this.savedObjectsCache.map(obj => obj?.attributes?.title);
};
getFields = async (fields: string[], refresh: boolean = false) => {
@ -86,7 +85,7 @@ export class IndexPatterns {
}
return this.savedObjectsCache.map((obj: Record<string, any>) => {
const result: Record<string, any> = {};
fields.forEach((f: string) => (result[f] = obj[f] || idx(obj, _ => _.attributes[f])));
fields.forEach((f: string) => (result[f] = obj[f] || obj?.attributes?.[f]));
return result;
});
};

View file

@ -11,7 +11,6 @@
* crashing due to the requirement of the `window` object.
*/
import { idx } from '@kbn/elastic-idx';
import { i18n } from '@kbn/i18n';
import { ReactNode, FunctionComponent } from 'react';
@ -32,7 +31,7 @@ const layouts: Layouts = {
};
export const findLayout = (type: InventoryItemType) => {
const Layout = idx(layouts, _ => _[type]);
const Layout = layouts?.[type];
if (!Layout) {
throw new Error(
i18n.translate('xpack.infra.inventoryModels.findLayout.error', {

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx/target';
import { ReactNode, FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { InventoryItemType } from './types';
@ -24,7 +23,7 @@ const toolbars: Toolbars = {
};
export const findToolbar = (type: InventoryItemType) => {
const Toolbar = idx(toolbars, _ => _[type]);
const Toolbar = toolbars?.[type];
if (!Toolbar) {
throw new Error(
i18n.translate('xpack.infra.inventoryModels.findToolbar.error', {

View file

@ -8,7 +8,6 @@ import React, { useMemo, useState } from 'react';
import { kfetch } from 'ui/kfetch';
import { toastNotifications } from 'ui/notify';
import { i18n } from '@kbn/i18n';
import { idx } from '@kbn/elastic-idx/target';
import { KFetchError } from 'ui/kfetch/kfetch_error';
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
import { useTrackedPromise } from '../utils/use_tracked_promise';
@ -44,13 +43,13 @@ export function useHTTPRequest<Response>(
defaultMessage: `Error`,
})}
</h5>
{idx(err.res, r => r.statusText)} ({idx(err.res, r => r.status)})
{err.res?.statusText} ({err.res?.status})
<h5>
{i18n.translate('xpack.infra.useHTTPRequest.error.url', {
defaultMessage: `URL`,
})}
</h5>
{idx(err.res, r => r.url)}
{err.res?.url}
</div>
),
});

View file

@ -5,7 +5,6 @@
*/
import { startsWith, uniq, first } from 'lodash';
import { idx } from '@kbn/elastic-idx';
import { RequestHandlerContext } from 'src/core/server';
import { InfraDatabaseSearchResponse } from '../framework';
import { KibanaFramework } from '../framework/kibana_framework_adapter';
@ -105,8 +104,9 @@ export class FrameworkFieldsAdapter implements FieldsAdapter {
const bucketSelector = (response: InfraDatabaseSearchResponse<{}, DataSetResponse>) =>
(response.aggregations && response.aggregations.datasets.buckets) || [];
const handleAfterKey = createAfterKeyHandler('body.aggs.datasets.composite.after', input =>
idx(input, _ => _.aggregations.datasets.after_key)
const handleAfterKey = createAfterKeyHandler(
'body.aggs.datasets.composite.after',
input => input?.aggregations?.datasets?.after_key
);
const buckets = await getAllCompositeData<DataSetResponse, Bucket>(

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { RequestHandlerContext } from 'src/core/server';
import {
InfraSnapshotGroupbyInput,
@ -70,8 +69,9 @@ const bucketSelector = (
response: InfraDatabaseSearchResponse<{}, InfraSnapshotAggregationResponse>
) => (response.aggregations && response.aggregations.nodes.buckets) || [];
const handleAfterKey = createAfterKeyHandler('body.aggregations.nodes.composite.after', input =>
idx(input, _ => _.aggregations.nodes.after_key)
const handleAfterKey = createAfterKeyHandler(
'body.aggregations.nodes.composite.after',
input => input?.aggregations?.nodes?.after_key
);
const requestGroupedNodes = async (

View file

@ -8,7 +8,6 @@ import { useEffect } from 'react';
import { BehaviorSubject } from 'rxjs';
import { filter, distinctUntilChanged } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { idx } from '@kbn/elastic-idx';
import { cloneDeep } from 'lodash';
import { ml } from '../../services/ml_api_service';
import { Dictionary } from '../../../../common/types/common';
@ -242,12 +241,13 @@ export const useRefreshAnalyticsList = (
const DEFAULT_SIG_FIGS = 3;
export function getValuesFromResponse(response: RegressionEvaluateResponse) {
let meanSquaredError = idx(response, _ => _.regression.mean_squared_error.error) as number;
let meanSquaredError = response?.regression?.mean_squared_error?.error;
if (meanSquaredError) {
meanSquaredError = Number(meanSquaredError.toPrecision(DEFAULT_SIG_FIGS));
}
let rSquared = idx(response, _ => _.regression.r_squared.value) as number;
let rSquared = response?.regression?.r_squared?.value;
if (rSquared) {
rSquared = Number(rSquared.toPrecision(DEFAULT_SIG_FIGS));
}

View file

@ -6,7 +6,6 @@
import React, { FC, Fragment, useState, useEffect } from 'react';
import moment from 'moment-timezone';
import { idx } from '@kbn/elastic-idx';
import { EuiIcon, EuiLoadingSpinner, EuiTabbedContent } from '@elastic/eui';
@ -64,7 +63,7 @@ export const ExpandedRow: FC<Props> = ({ item }) => {
const [generalizationEval, setGeneralizationEval] = useState<Eval>(defaultEval);
const [isLoadingTraining, setIsLoadingTraining] = useState<boolean>(false);
const [isLoadingGeneralization, setIsLoadingGeneralization] = useState<boolean>(false);
const index = idx(item, _ => _.config.dest.index) as string;
const index = item?.config?.dest?.index;
const dependentVariable = getDependentVar(item.config.analysis);
const predictionFieldName = getPredictionFieldName(item.config.analysis);
// default is 'ml'

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { i18n } from '@kbn/i18n';
import { validateIndexPattern } from 'ui/index_patterns';
@ -43,7 +42,7 @@ export const mmlUnitInvalidErrorMessage = i18n.translate(
const getSourceIndexString = (state: State) => {
const { jobConfig } = state;
const sourceIndex = idx(jobConfig, _ => _.source.index);
const sourceIndex = jobConfig?.source?.index;
if (typeof sourceIndex === 'string') {
return sourceIndex;
@ -70,17 +69,17 @@ export const validateAdvancedEditor = (state: State): State => {
// `validateIndexPattern()` returns a map of messages, we're only interested here if it's valid or not.
// If there are no messages, it means the index pattern is valid.
let sourceIndexNameValid = Object.keys(validateIndexPattern(sourceIndexName)).length === 0;
const sourceIndex = idx(jobConfig, _ => _.source.index);
const sourceIndex = jobConfig?.source?.index;
if (sourceIndexNameValid) {
if (typeof sourceIndex === 'string') {
sourceIndexNameValid = !sourceIndex.includes(',');
}
if (Array.isArray(sourceIndex)) {
sourceIndexNameValid = !sourceIndex.some(d => d.includes(','));
sourceIndexNameValid = !sourceIndex.some(d => d?.includes(','));
}
}
const destinationIndexName = idx(jobConfig, _ => _.dest.index) || '';
const destinationIndexName = jobConfig?.dest?.index ?? '';
const destinationIndexNameEmpty = destinationIndexName === '';
const destinationIndexNameValid = isValidIndexName(destinationIndexName);
const destinationIndexPatternTitleExists =

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { getInitialState, getJobConfigFromFormState } from './state';
describe('useCreateAnalyticsForm', () => {
@ -17,15 +15,15 @@ describe('useCreateAnalyticsForm', () => {
const jobConfig = getJobConfigFromFormState(state.form);
expect(idx(jobConfig, _ => _.dest.index)).toBe('the-destination-index');
expect(idx(jobConfig, _ => _.source.index)).toBe('the-source-index');
expect(idx(jobConfig, _ => _.analyzed_fields.excludes)).toStrictEqual([]);
expect(typeof idx(jobConfig, _ => _.analyzed_fields.includes)).toBe('undefined');
expect(jobConfig?.dest?.index).toBe('the-destination-index');
expect(jobConfig?.source?.index).toBe('the-source-index');
expect(jobConfig?.analyzed_fields?.excludes).toStrictEqual([]);
expect(typeof jobConfig?.analyzed_fields?.includes).toBe('undefined');
// test the conversion of comma-separated Kibana index patterns to ES array based index patterns
state.form.sourceIndex = 'the-source-index-1,the-source-index-2';
const jobConfigSourceIndexArray = getJobConfigFromFormState(state.form);
expect(idx(jobConfigSourceIndexArray, _ => _.source.index)).toStrictEqual([
expect(jobConfigSourceIndexArray?.source?.index).toStrictEqual([
'the-source-index-1',
'the-source-index-2',
]);

View file

@ -7,7 +7,6 @@
import { useReducer } from 'react';
import { i18n } from '@kbn/i18n';
import { idx } from '@kbn/elastic-idx';
import { SimpleSavedObject } from 'src/core/public';
import { ml } from '../../../../../services/ml_api_service';
@ -229,9 +228,9 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
const indexPatternsMap: SourceIndexMap = {};
const savedObjects = (await kibanaContext.indexPatterns.getCache()) || [];
savedObjects.forEach((obj: SimpleSavedObject<Record<string, any>>) => {
const title = idx(obj, _ => _.attributes.title);
const title = obj?.attributes?.title;
if (title !== undefined) {
const id = idx(obj, _ => _.id) || '';
const id = obj?.id || '';
indexPatternsMap[title] = { label: title, value: id };
}
});

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { Job, Datafeed, Detector } from '../configs';
import { newJobCapsService } from '../../../../../services/new_job_capabilities_service';
import {
@ -120,10 +119,9 @@ function getDetectors(job: Job, datafeed: Datafeed) {
) {
// distinct count detector, field has been removed.
// determine field from datafeed aggregations
const field = idx<Datafeed, string>(
datafeed,
_ => _.aggregations.buckets.aggregations.dc_region.cardinality.field
);
const field = datafeed?.aggregations?.buckets?.aggregations?.dc_region?.cardinality
?.field as string;
if (field !== undefined) {
detectors = [
{
@ -193,10 +191,9 @@ function processFieldlessAggs(detectors: Detector[]) {
export function isSparseDataJob(job: Job, datafeed: Datafeed): boolean {
const detectors = job.analysis_config.detectors;
const distinctCountField = idx<Datafeed, string>(
datafeed,
_ => _.aggregations.buckets.aggregations.dc_region.cardinality.field
);
const distinctCountField = datafeed?.aggregations?.buckets?.aggregations?.dc_region?.cardinality
?.field as string;
// if distinctCountField is undefined, and any detectors contain a sparse data function
// return true
if (distinctCountField === undefined) {

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { mlJobService } from '../../../../services/job_service';
import { loadIndexPatterns, getIndexPatternIdFromName } from '../../../../util/index_utils';
import { CombinedJob } from '../../common/job_creator/configs';
@ -29,7 +28,7 @@ export async function preConfiguredJobRedirect() {
}
function getWizardUrlFromCloningJob(job: CombinedJob) {
const created = idx(job, _ => _.custom_settings.created_by);
const created = job?.custom_settings?.created_by;
let page = '';
if (created === CREATED_BY_LABEL.SINGLE_METRIC) {

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
// This is similar to lodash's get() except that it's TypeScript aware and is able to infer return types.
// It splits the attribute key string and uses reduce with an idx check to access nested attributes.
export const getNestedProperty = (
@ -13,5 +11,5 @@ export const getNestedProperty = (
accessor: string,
defaultValue?: any
) => {
return accessor.split('.').reduce((o, i) => idx(o, _ => _[i]), obj) || defaultValue;
return accessor.split('.').reduce((o, i) => o?.[i], obj) || defaultValue;
};

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
// This is similar to lodash's get() except that it's TypeScript aware and is able to infer return types.
// It splits the attribute key string and uses reduce with an idx check to access nested attributes.
export const getNestedProperty = (
@ -13,5 +11,5 @@ export const getNestedProperty = (
accessor: string,
defaultValue?: any
) => {
return accessor.split('.').reduce((o, i) => idx(o, _ => _[i]), obj) || defaultValue;
return accessor.split('.').reduce((o, i) => o?.[i], obj) || defaultValue;
};

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { idx } from '@kbn/elastic-idx';
import { TransformId } from './transform';
import { TransformListRow } from './transform_list';
@ -78,8 +76,7 @@ export function getTransformProgress(item: TransformListRow) {
return 100;
}
const progress = idx(item, _ => _.stats.checkpointing.next.checkpoint_progress.percent_complete);
const progress = item?.stats?.checkpointing?.next?.checkpoint_progress?.percent_complete;
return progress !== undefined ? Math.round(progress) : undefined;
}

View file

@ -183,7 +183,6 @@
"@elastic/numeral": "2.3.3",
"@kbn/babel-preset": "1.0.0",
"@kbn/config-schema": "1.0.0",
"@kbn/elastic-idx": "1.0.0",
"@kbn/i18n": "1.0.0",
"@kbn/interpreter": "1.0.0",
"@kbn/ui-framework": "1.0.0",