mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Upgrade io-ts and fp-ts (#44753)
* Upgrade io-ts and fp-ts * Add io-ts/fp-ts as top level dependencies * Merge fp-ts changes from actions plugin * Use getOrElse instead of fold
This commit is contained in:
parent
c7112d06a0
commit
c01a5793ac
47 changed files with 404 additions and 213 deletions
|
@ -157,6 +157,7 @@
|
|||
"expiry-js": "0.1.7",
|
||||
"file-loader": "4.2.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"fp-ts": "^2.0.5",
|
||||
"getos": "^3.1.0",
|
||||
"glob": "^7.1.2",
|
||||
"glob-all": "^3.1.0",
|
||||
|
@ -173,6 +174,7 @@
|
|||
"https-proxy-agent": "^2.2.2",
|
||||
"inert": "^5.1.0",
|
||||
"inline-style": "^2.0.0",
|
||||
"io-ts": "^2.0.1",
|
||||
"joi": "^13.5.2",
|
||||
"jquery": "^3.4.1",
|
||||
"js-yaml": "3.13.1",
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { tryCatch, fromNullable } from 'fp-ts/lib/Option';
|
||||
import { tryCatch, fromNullable, isSome, map, mapNullable, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { URL } from 'url';
|
||||
import { curry } from 'lodash';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
export enum WhitelistedHosts {
|
||||
Any = '*',
|
||||
|
@ -47,20 +48,24 @@ function doesValueWhitelistAnyHostname(whitelistedHostname: string): boolean {
|
|||
function isWhitelisted({ whitelistedHosts }: ActionsKibanaConfig, hostname: string): boolean {
|
||||
return (
|
||||
Array.isArray(whitelistedHosts) &&
|
||||
fromNullable(
|
||||
whitelistedHosts.find(
|
||||
whitelistedHostname =>
|
||||
doesValueWhitelistAnyHostname(whitelistedHostname) || whitelistedHostname === hostname
|
||||
isSome(
|
||||
fromNullable(
|
||||
whitelistedHosts.find(
|
||||
whitelistedHostname =>
|
||||
doesValueWhitelistAnyHostname(whitelistedHostname) || whitelistedHostname === hostname
|
||||
)
|
||||
)
|
||||
).isSome()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isWhitelistedHostnameInUri(config: ActionsKibanaConfig, uri: string): boolean {
|
||||
return tryCatch(() => new URL(uri))
|
||||
.map(url => url.hostname)
|
||||
.mapNullable(hostname => isWhitelisted(config, hostname))
|
||||
.getOrElse(false);
|
||||
return pipe(
|
||||
tryCatch(() => new URL(uri)),
|
||||
map(url => url.hostname),
|
||||
mapNullable(hostname => isWhitelisted(config, hostname)),
|
||||
getOrElse(() => false)
|
||||
);
|
||||
}
|
||||
|
||||
export function getActionsConfigurationUtilities(
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { fromNullable, Option } from 'fp-ts/lib/Option';
|
||||
import { fromNullable, Option, map, filter } from 'fp-ts/lib/Option';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
export function getRetryAfterIntervalFromHeaders(headers: Record<string, string>): Option<number> {
|
||||
return fromNullable(headers['retry-after'])
|
||||
.map(retryAfter => parseInt(retryAfter, 10))
|
||||
.filter(retryAfter => !isNaN(retryAfter));
|
||||
return pipe(
|
||||
fromNullable(headers['retry-after']),
|
||||
map(retryAfter => parseInt(retryAfter, 10)),
|
||||
filter(retryAfter => !isNaN(retryAfter))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { IncomingWebhook, IncomingWebhookResult } from '@slack/webhook';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { getRetryAfterIntervalFromHeaders } from './lib/http_rersponse_retry_header';
|
||||
|
||||
import {
|
||||
|
@ -79,9 +81,11 @@ async function slackExecutor(
|
|||
|
||||
// special handling for rate limiting
|
||||
if (status === 429) {
|
||||
return getRetryAfterIntervalFromHeaders(headers)
|
||||
.map(retry => retryResultSeconds(id, err.message, retry))
|
||||
.getOrElse(retryResult(id, err.message));
|
||||
return pipe(
|
||||
getRetryAfterIntervalFromHeaders(headers),
|
||||
map(retry => retryResultSeconds(id, err.message, retry)),
|
||||
getOrElse(() => retryResult(id, err.message))
|
||||
);
|
||||
}
|
||||
|
||||
return errorResult(id, `${err.message} - ${statusText}`);
|
||||
|
|
|
@ -7,6 +7,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { curry } from 'lodash';
|
||||
import axios, { AxiosError, AxiosResponse } from 'axios';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { getRetryAfterIntervalFromHeaders } from './lib/http_rersponse_retry_header';
|
||||
import { nullableType } from './lib/nullable';
|
||||
import { isOk, promiseResult, Result } from './lib/result_type';
|
||||
|
@ -124,9 +126,11 @@ export async function executor(
|
|||
|
||||
// special handling for rate limiting
|
||||
if (status === 429) {
|
||||
return getRetryAfterIntervalFromHeaders(responseHeaders)
|
||||
.map(retry => retryResultSeconds(id, message, retry))
|
||||
.getOrElse(retryResult(id, message));
|
||||
return pipe(
|
||||
getRetryAfterIntervalFromHeaders(responseHeaders),
|
||||
map(retry => retryResultSeconds(id, message, retry)),
|
||||
getOrElse(() => retryResult(id, message))
|
||||
);
|
||||
}
|
||||
return errorResultInvalid(id, message);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
*/
|
||||
|
||||
import { dateAsStringRt } from './index';
|
||||
import { isLeft, isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
describe('dateAsStringRt', () => {
|
||||
it('validates whether a string is a valid date', () => {
|
||||
expect(dateAsStringRt.decode(1566299881499).isLeft()).toBe(true);
|
||||
expect(isLeft(dateAsStringRt.decode(1566299881499))).toBe(true);
|
||||
|
||||
expect(dateAsStringRt.decode('2019-08-20T11:18:31.407Z').isRight()).toBe(
|
||||
expect(isRight(dateAsStringRt.decode('2019-08-20T11:18:31.407Z'))).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
@ -18,6 +19,10 @@ describe('dateAsStringRt', () => {
|
|||
it('returns the string it was given', () => {
|
||||
const either = dateAsStringRt.decode('2019-08-20T11:18:31.407Z');
|
||||
|
||||
expect(either.value).toBe('2019-08-20T11:18:31.407Z');
|
||||
if (isRight(either)) {
|
||||
expect(either.right).toBe('2019-08-20T11:18:31.407Z');
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,20 +6,37 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import { jsonRt } from './index';
|
||||
import { isRight, Either, isLeft, fold } from 'fp-ts/lib/Either';
|
||||
import { Right } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
|
||||
function getValueOrThrow<TEither extends Either<any, any>>(
|
||||
either: TEither
|
||||
): Right<TEither> {
|
||||
const value = pipe(
|
||||
either,
|
||||
fold(() => {
|
||||
throw new Error('cannot get right value of left');
|
||||
}, identity)
|
||||
);
|
||||
|
||||
return value as Right<TEither>;
|
||||
}
|
||||
|
||||
describe('jsonRt', () => {
|
||||
it('validates json', () => {
|
||||
expect(jsonRt.decode('{}').isRight()).toBe(true);
|
||||
expect(jsonRt.decode('[]').isRight()).toBe(true);
|
||||
expect(jsonRt.decode('true').isRight()).toBe(true);
|
||||
expect(jsonRt.decode({}).isLeft()).toBe(true);
|
||||
expect(jsonRt.decode('foo').isLeft()).toBe(true);
|
||||
expect(isRight(jsonRt.decode('{}'))).toBe(true);
|
||||
expect(isRight(jsonRt.decode('[]'))).toBe(true);
|
||||
expect(isRight(jsonRt.decode('true'))).toBe(true);
|
||||
expect(isLeft(jsonRt.decode({}))).toBe(true);
|
||||
expect(isLeft(jsonRt.decode('foo'))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns parsed json when decoding', () => {
|
||||
expect(jsonRt.decode('{}').value).toEqual({});
|
||||
expect(jsonRt.decode('[]').value).toEqual([]);
|
||||
expect(jsonRt.decode('true').value).toEqual(true);
|
||||
expect(getValueOrThrow(jsonRt.decode('{}'))).toEqual({});
|
||||
expect(getValueOrThrow(jsonRt.decode('[]'))).toEqual([]);
|
||||
expect(getValueOrThrow(jsonRt.decode('true'))).toEqual(true);
|
||||
});
|
||||
|
||||
it('is pipable', () => {
|
||||
|
@ -31,10 +48,10 @@ describe('jsonRt', () => {
|
|||
const valid = piped.decode(JSON.stringify(validInput));
|
||||
const invalid = piped.decode(JSON.stringify(invalidInput));
|
||||
|
||||
expect(valid.isRight()).toBe(true);
|
||||
expect(valid.value).toEqual(validInput);
|
||||
expect(isRight(valid)).toBe(true);
|
||||
expect(getValueOrThrow(valid)).toEqual(validInput);
|
||||
|
||||
expect(invalid.isLeft()).toBe(true);
|
||||
expect(isLeft(invalid)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns strings when encoding', () => {
|
||||
|
|
|
@ -4,28 +4,29 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { transactionSampleRateRt } from './index';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
describe('transactionSampleRateRt', () => {
|
||||
it('accepts both strings and numbers as values', () => {
|
||||
expect(transactionSampleRateRt.decode('0.5').isRight()).toBe(true);
|
||||
expect(transactionSampleRateRt.decode(0.5).isRight()).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode('0.5'))).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0.5))).toBe(true);
|
||||
});
|
||||
|
||||
it('checks if the number falls within 0, 1', () => {
|
||||
expect(transactionSampleRateRt.decode(0).isRight()).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0))).toBe(true);
|
||||
|
||||
expect(transactionSampleRateRt.decode(0.5).isRight()).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0.5))).toBe(true);
|
||||
|
||||
expect(transactionSampleRateRt.decode(-0.1).isRight()).toBe(false);
|
||||
expect(transactionSampleRateRt.decode(1.1).isRight()).toBe(false);
|
||||
expect(isRight(transactionSampleRateRt.decode(-0.1))).toBe(false);
|
||||
expect(isRight(transactionSampleRateRt.decode(1.1))).toBe(false);
|
||||
|
||||
expect(transactionSampleRateRt.decode(NaN).isRight()).toBe(false);
|
||||
expect(isRight(transactionSampleRateRt.decode(NaN))).toBe(false);
|
||||
});
|
||||
|
||||
it('checks whether the number of decimals is 3', () => {
|
||||
expect(transactionSampleRateRt.decode(1).isRight()).toBe(true);
|
||||
expect(transactionSampleRateRt.decode(0.99).isRight()).toBe(true);
|
||||
expect(transactionSampleRateRt.decode(0.999).isRight()).toBe(true);
|
||||
expect(transactionSampleRateRt.decode(0.998).isRight()).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(1))).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0.99))).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0.999))).toBe(true);
|
||||
expect(isRight(transactionSampleRateRt.decode(0.998))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import React, { useState } from 'react';
|
|||
import { toastNotifications } from 'ui/notify';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
import { transactionSampleRateRt } from '../../../../../common/runtime_types/transaction_sample_rate_rt';
|
||||
import { AddSettingFlyoutBody } from './AddSettingFlyoutBody';
|
||||
import { Config } from '../SettingsList';
|
||||
|
@ -79,9 +80,7 @@ export function AddSettingsFlyout({
|
|||
{ preservePreviousData: false }
|
||||
);
|
||||
|
||||
const isSampleRateValid = transactionSampleRateRt
|
||||
.decode(sampleRate)
|
||||
.isRight();
|
||||
const isSampleRateValid = isRight(transactionSampleRateRt.decode(sampleRate));
|
||||
|
||||
const isSelectedEnvironmentValid = environments.some(
|
||||
env =>
|
||||
|
|
|
@ -221,7 +221,9 @@ describe('createApi', () => {
|
|||
});
|
||||
|
||||
it('validates query parameters', () => {
|
||||
const { handler, route } = initApi({ query: t.type({ bar: t.string }) });
|
||||
const { handler, route } = initApi({
|
||||
query: t.type({ bar: t.string })
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
route.handler({
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { merge, pick, omit } from 'lodash';
|
||||
import { merge, pick, omit, difference } from 'lodash';
|
||||
import Boom from 'boom';
|
||||
import { InternalCoreSetup } from 'src/core/server';
|
||||
import { Request, ResponseToolkit } from 'hapi';
|
||||
import * as t from 'io-ts';
|
||||
import { PathReporter } from 'io-ts/lib/PathReporter';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import {
|
||||
ServerAPI,
|
||||
RouteFactoryFn,
|
||||
|
@ -18,6 +19,8 @@ import {
|
|||
} from '../typings';
|
||||
import { jsonRt } from '../../../common/runtime_types/json_rt';
|
||||
|
||||
const debugRt = t.partial({ _debug: jsonRt.pipe(t.boolean) });
|
||||
|
||||
export function createApi() {
|
||||
const factoryFns: Array<RouteFactoryFn<any, any, any, any>> = [];
|
||||
const api: ServerAPI<{}> = {
|
||||
|
@ -36,21 +39,16 @@ export function createApi() {
|
|||
any
|
||||
>;
|
||||
|
||||
const bodyRt = params.body;
|
||||
const fallbackBodyRt = bodyRt || t.null;
|
||||
|
||||
const rts = {
|
||||
// add _debug query parameter to all routes
|
||||
query: params.query
|
||||
? t.exact(
|
||||
t.intersection([
|
||||
params.query,
|
||||
t.partial({ _debug: jsonRt.pipe(t.boolean) })
|
||||
])
|
||||
)
|
||||
: t.union([
|
||||
t.strict({}),
|
||||
t.strict({ _debug: jsonRt.pipe(t.boolean) })
|
||||
]),
|
||||
path: params.path || t.strict({}),
|
||||
body: params.body || t.null
|
||||
? t.exact(t.intersection([params.query, debugRt]))
|
||||
: t.exact(debugRt),
|
||||
path: params.path ? t.exact(params.path) : t.strict({}),
|
||||
body: bodyRt && 'props' in bodyRt ? t.exact(bodyRt) : fallbackBodyRt
|
||||
};
|
||||
|
||||
server.route(
|
||||
|
@ -74,25 +72,31 @@ export function createApi() {
|
|||
keyof typeof rts
|
||||
>).reduce(
|
||||
(acc, key) => {
|
||||
let codec = rts[key];
|
||||
const codec = rts[key];
|
||||
const value = paramMap[key];
|
||||
|
||||
// Use exact props where possible (only possible for types with props)
|
||||
if ('props' in codec) {
|
||||
codec = t.exact(codec);
|
||||
}
|
||||
|
||||
const result = codec.decode(value);
|
||||
|
||||
if (result.isLeft()) {
|
||||
if (isLeft(result)) {
|
||||
throw Boom.badRequest(PathReporter.report(result)[0]);
|
||||
}
|
||||
|
||||
const strippedKeys = difference(
|
||||
Object.keys(value || {}),
|
||||
Object.keys(result.right || {})
|
||||
);
|
||||
|
||||
if (strippedKeys.length) {
|
||||
throw Boom.badRequest(
|
||||
`Unknown keys specified: ${strippedKeys}`
|
||||
);
|
||||
}
|
||||
|
||||
// hide _debug from route handlers
|
||||
const parsedValue =
|
||||
key === 'query'
|
||||
? omit(result.value, '_debug')
|
||||
: result.value;
|
||||
? omit(result.right, '_debug')
|
||||
: result.right;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
|
|
|
@ -13,7 +13,7 @@ import { PickByValue, Optional } from 'utility-types';
|
|||
export interface Params {
|
||||
query?: t.HasProps;
|
||||
path?: t.HasProps;
|
||||
body?: t.Any;
|
||||
body?: t.Any | t.HasProps;
|
||||
}
|
||||
|
||||
type DecodeParams<TParams extends Params | undefined> = {
|
||||
|
|
|
@ -9,7 +9,10 @@ import { omit } from 'lodash';
|
|||
import { setupRequest, Setup } from '../lib/helpers/setup_request';
|
||||
import { getEnvironments } from '../lib/ui_filters/get_environments';
|
||||
import { Projection } from '../../common/projections/typings';
|
||||
import { localUIFilterNames } from '../lib/ui_filters/local_ui_filters/config';
|
||||
import {
|
||||
localUIFilterNames,
|
||||
LocalUIFilterName
|
||||
} from '../lib/ui_filters/local_ui_filters/config';
|
||||
import { getUiFiltersES } from '../lib/helpers/convert_ui_filters/get_ui_filters_es';
|
||||
import { getLocalUIFilters } from '../lib/ui_filters/local_ui_filters';
|
||||
import { getServicesProjection } from '../../common/projections/services';
|
||||
|
@ -40,7 +43,11 @@ export const uiFiltersEnvironmentsRoute = createRoute(() => ({
|
|||
|
||||
const filterNamesRt = t.type({
|
||||
filterNames: jsonRt.pipe(
|
||||
t.array(t.union(localUIFilterNames.map(name => t.literal(name))))
|
||||
t.array(
|
||||
t.keyof(Object.fromEntries(
|
||||
localUIFilterNames.map(filterName => [filterName, null])
|
||||
) as Record<LocalUIFilterName, null>)
|
||||
)
|
||||
)
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
import * as t from 'io-ts';
|
||||
import { PathReporter } from 'io-ts/lib/PathReporter';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import { configBlockSchemas } from './config_schemas';
|
||||
import { ConfigurationBlock, createConfigurationBlockInterface } from './domain_types';
|
||||
|
||||
|
@ -29,7 +30,9 @@ export const validateConfigurationBlocks = (configurationBlocks: ConfigurationBl
|
|||
const interfaceConfig = blockSchema.configs.reduce(
|
||||
(props, config) => {
|
||||
if (config.options) {
|
||||
props[config.id] = t.union(config.options.map(opt => t.literal(opt.value)));
|
||||
props[config.id] = t.keyof(Object.fromEntries(
|
||||
config.options.map(opt => [opt.value, null])
|
||||
) as Record<string, null>);
|
||||
} else if (config.validation) {
|
||||
props[config.id] = validationMap[config.validation];
|
||||
}
|
||||
|
@ -46,7 +49,7 @@ export const validateConfigurationBlocks = (configurationBlocks: ConfigurationBl
|
|||
|
||||
const validationResults = runtimeInterface.decode(block);
|
||||
|
||||
if (validationResults.isLeft()) {
|
||||
if (isLeft(validationResults)) {
|
||||
throw new Error(
|
||||
`configuration_blocks validation error, configuration_blocks at index ${index} is invalid. ${
|
||||
PathReporter.report(validationResults)[0]
|
||||
|
|
|
@ -13,9 +13,9 @@ export const OutputTypesArray = ['elasticsearch', 'logstash', 'kafka', 'redis'];
|
|||
// We can also pass in optional params to create spacific runtime checks that
|
||||
// can be used to validate blocs on the API and UI
|
||||
export const createConfigurationBlockInterface = (
|
||||
configType: t.LiteralType<string> | t.UnionType<Array<t.LiteralType<string>>> = t.union(
|
||||
configBlockSchemas.map(s => t.literal(s.id))
|
||||
),
|
||||
configType: t.LiteralType<string> | t.KeyofC<Record<string, null>> = t.keyof(Object.fromEntries(
|
||||
configBlockSchemas.map(s => [s.id, null])
|
||||
) as Record<string, null>),
|
||||
beatConfigInterface: t.Mixed = t.Dictionary
|
||||
) =>
|
||||
t.interface(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
export class DateFromStringType extends t.Type<Date, string, t.mixed> {
|
||||
// eslint-disable-next-line
|
||||
|
@ -15,10 +16,10 @@ export class DateFromStringType extends t.Type<Date, string, t.mixed> {
|
|||
(u): u is Date => u instanceof Date,
|
||||
(u, c) => {
|
||||
const validation = t.string.validate(u, c);
|
||||
if (validation.isLeft()) {
|
||||
if (!isRight(validation)) {
|
||||
return validation as any;
|
||||
} else {
|
||||
const s = validation.value;
|
||||
const s = validation.right;
|
||||
const d = new Date(s);
|
||||
return isNaN(d.getTime()) ? t.failure(s, c) : t.success(d);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ export interface FrameworkAdapter {
|
|||
export const RuntimeFrameworkInfo = t.type({
|
||||
basePath: t.string,
|
||||
license: t.type({
|
||||
type: t.union(LICENSES.map(s => t.literal(s))),
|
||||
type: t.keyof(Object.fromEntries(LICENSES.map(s => [s, null])) as Record<string, null>),
|
||||
expired: t.boolean,
|
||||
expiry_date_in_millis: t.number,
|
||||
}),
|
||||
|
|
|
@ -10,6 +10,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter';
|
|||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { UIRoutes } from 'ui/routes';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import { BufferedKibanaServiceCall, KibanaAdapterServiceRefs, KibanaUIConfig } from '../../types';
|
||||
import {
|
||||
FrameworkAdapter,
|
||||
|
@ -87,7 +88,7 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
|
|||
}
|
||||
|
||||
const assertData = RuntimeFrameworkInfo.decode(xpackInfoUnpacked);
|
||||
if (assertData.isLeft()) {
|
||||
if (isLeft(assertData)) {
|
||||
throw new Error(
|
||||
`Error parsing xpack info in ${this.PLUGIN_ID}, ${PathReporter.report(assertData)[0]}`
|
||||
);
|
||||
|
@ -98,7 +99,7 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
|
|||
this.shieldUser = await $injector.get('ShieldUser').getCurrent().$promise;
|
||||
const assertUser = RuntimeFrameworkUser.decode(this.shieldUser);
|
||||
|
||||
if (assertUser.isLeft()) {
|
||||
if (isLeft(assertUser)) {
|
||||
throw new Error(
|
||||
`Error parsing user info in ${this.PLUGIN_ID}, ${PathReporter.report(assertUser)[0]}`
|
||||
);
|
||||
|
|
|
@ -75,9 +75,14 @@ export const RuntimeFrameworkInfo = t.interface(
|
|||
version: t.string,
|
||||
}),
|
||||
license: t.type({
|
||||
type: t.union(
|
||||
['oss', 'trial', 'standard', 'basic', 'gold', 'platinum'].map(s => t.literal(s))
|
||||
),
|
||||
type: t.keyof({
|
||||
oss: null,
|
||||
trial: null,
|
||||
standard: null,
|
||||
basic: null,
|
||||
gold: null,
|
||||
platinum: null,
|
||||
}),
|
||||
expired: t.boolean,
|
||||
expiry_date_in_millis: t.number,
|
||||
}),
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { ResponseToolkit } from 'hapi';
|
||||
import { PathReporter } from 'io-ts/lib/PathReporter';
|
||||
import { get } from 'lodash';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
// @ts-ignore
|
||||
import { mirrorPluginStatus } from '../../../../../../server/lib/mirror_plugin_status';
|
||||
import {
|
||||
|
@ -137,7 +138,7 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
|
|||
return null;
|
||||
}
|
||||
const assertKibanaUser = RuntimeKibanaUser.decode(user);
|
||||
if (assertKibanaUser.isLeft()) {
|
||||
if (isLeft(assertKibanaUser)) {
|
||||
throw new Error(
|
||||
`Error parsing user info in ${this.PLUGIN_ID}, ${
|
||||
PathReporter.report(assertKibanaUser)[0]
|
||||
|
@ -186,7 +187,7 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
|
|||
}
|
||||
|
||||
const assertData = RuntimeFrameworkInfo.decode(xpackInfoUnpacked);
|
||||
if (assertData.isLeft()) {
|
||||
if (isLeft(assertData)) {
|
||||
throw new Error(
|
||||
`Error parsing xpack info in ${this.PLUGIN_ID}, ${PathReporter.report(assertData)[0]}`
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { PathReporter } from 'io-ts/lib/PathReporter';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import { BeatEvent, RuntimeBeatEvent } from '../../common/domain_types';
|
||||
import { BeatEventsAdapter } from './adapters/events/adapter_types';
|
||||
import { FrameworkUser } from './adapters/framework/adapter_types';
|
||||
|
@ -20,7 +21,7 @@ export class BeatEventsLib {
|
|||
): Promise<Array<{ success: boolean; reason?: string }>> => {
|
||||
return events.map((event, i) => {
|
||||
const assertData = RuntimeBeatEvent.decode(event);
|
||||
if (assertData.isLeft()) {
|
||||
if (isLeft(assertData)) {
|
||||
if (events.length - 1 === i) {
|
||||
this.beats
|
||||
.update(user, beatId, {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter';
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import Joi from 'joi';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import { REQUIRED_LICENSES } from '../../../common/constants';
|
||||
import {
|
||||
ConfigurationBlock,
|
||||
|
@ -35,7 +36,7 @@ export const upsertConfigurationRoute = (libs: CMServerLibs) => ({
|
|||
const result = await Promise.all<any>(
|
||||
request.payload.map(async (block: ConfigurationBlock) => {
|
||||
const assertData = createConfigurationBlockInterface().decode(block);
|
||||
if (assertData.isLeft()) {
|
||||
if (isLeft(assertData)) {
|
||||
return {
|
||||
error: `Error parsing block info, ${PathReporter.report(assertData)[0]}`,
|
||||
};
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
import * as rt from 'io-ts';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { getJobId } from '../../../../../common/log_analysis';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
import { getJobId } from '../../../../../common/log_analysis';
|
||||
|
||||
export const callJobsSummaryAPI = async (spaceId: string, sourceId: string) => {
|
||||
const response = await kfetch({
|
||||
|
@ -19,7 +22,10 @@ export const callJobsSummaryAPI = async (spaceId: string, sourceId: string) => {
|
|||
})
|
||||
),
|
||||
});
|
||||
return fetchJobStatusResponsePayloadRT.decode(response).getOrElseL(throwErrors(createPlainError));
|
||||
return pipe(
|
||||
fetchJobStatusResponsePayloadRT.decode(response),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
};
|
||||
|
||||
export const fetchJobStatusRequestPayloadRT = rt.type({
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
import * as rt from 'io-ts';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { getJobIdPrefix } from '../../../../../common/log_analysis';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
|
||||
import { getJobIdPrefix } from '../../../../../common/log_analysis';
|
||||
|
||||
const MODULE_ID = 'logs_ui_analysis';
|
||||
|
||||
|
@ -56,7 +59,10 @@ export const callSetupMlModuleAPI = async (
|
|||
),
|
||||
});
|
||||
|
||||
return setupMlModuleResponsePayloadRT.decode(response).getOrElseL(throwErrors(createPlainError));
|
||||
return pipe(
|
||||
setupMlModuleResponsePayloadRT.decode(response),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
};
|
||||
|
||||
const setupMlModuleTimeParamsRT = rt.partial({
|
||||
|
|
|
@ -8,6 +8,9 @@ import createContainer from 'constate-latest';
|
|||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { useTrackedPromise } from '../../../utils/use_tracked_promise';
|
||||
import {
|
||||
getMlCapabilitiesResponsePayloadRT,
|
||||
|
@ -29,9 +32,10 @@ export const useLogAnalysisCapabilities = () => {
|
|||
pathname: '/api/ml/ml_capabilities',
|
||||
});
|
||||
|
||||
return getMlCapabilitiesResponsePayloadRT
|
||||
.decode(rawResponse)
|
||||
.getOrElseL(throwErrors(createPlainError));
|
||||
return pipe(
|
||||
getMlCapabilitiesResponsePayloadRT.decode(rawResponse),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
},
|
||||
onResolve: response => {
|
||||
setMlCapabilities(response);
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import { useEffect } from 'react';
|
||||
import * as rt from 'io-ts';
|
||||
import { identity, constant } from 'fp-ts/lib/function';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { useUrlState } from '../../../utils/use_url_state';
|
||||
|
||||
const autoRefreshRT = rt.union([
|
||||
|
@ -33,7 +36,11 @@ export const useLogAnalysisResultsUrlState = () => {
|
|||
startTime: 'now-2w',
|
||||
endTime: 'now',
|
||||
},
|
||||
decodeUrlState: (value: unknown) => urlTimeRangeRT.decode(value).getOrElse(undefined),
|
||||
decodeUrlState: (value: unknown) =>
|
||||
pipe(
|
||||
urlTimeRangeRT.decode(value),
|
||||
fold(constant(undefined), identity)
|
||||
),
|
||||
encodeUrlState: urlTimeRangeRT.encode,
|
||||
urlStateKey: TIME_RANGE_URL_STATE_KEY,
|
||||
});
|
||||
|
@ -47,7 +54,11 @@ export const useLogAnalysisResultsUrlState = () => {
|
|||
isPaused: false,
|
||||
interval: 30000,
|
||||
},
|
||||
decodeUrlState: (value: unknown) => autoRefreshRT.decode(value).getOrElse(undefined),
|
||||
decodeUrlState: (value: unknown) =>
|
||||
pipe(
|
||||
autoRefreshRT.decode(value),
|
||||
fold(constant(undefined), identity)
|
||||
),
|
||||
encodeUrlState: autoRefreshRT.encode,
|
||||
urlStateKey: AUTOREFRESH_URL_STATE_KEY,
|
||||
});
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import {
|
||||
getLogEntryRateRequestPayloadRT,
|
||||
getLogEntryRateSuccessReponsePayloadRT,
|
||||
|
@ -53,9 +56,10 @@ export const useLogEntryRate = ({
|
|||
});
|
||||
},
|
||||
onResolve: response => {
|
||||
const { data } = getLogEntryRateSuccessReponsePayloadRT
|
||||
.decode(response)
|
||||
.getOrElseL(throwErrors(createPlainError));
|
||||
const { data } = pipe(
|
||||
getLogEntryRateSuccessReponsePayloadRT.decode(response),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
|
||||
setLogEntryRate(data);
|
||||
},
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { InfraNodeType } from '../../graphql/types';
|
||||
import { InfraMetricLayout } from '../../pages/metrics/layouts/types';
|
||||
import { InfraMetadata, InfraMetadataRT } from '../../../common/http_api/metadata_api';
|
||||
|
@ -19,7 +22,10 @@ export function useMetadata(
|
|||
sourceId: string
|
||||
) {
|
||||
const decodeResponse = (response: any) => {
|
||||
return InfraMetadataRT.decode(response).getOrElseL(throwErrors(createPlainError));
|
||||
return pipe(
|
||||
InfraMetadataRT.decode(response),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
};
|
||||
|
||||
const { error, loading, response, makeRequest } = useHTTPRequest<InfraMetadata>(
|
||||
|
|
|
@ -10,6 +10,7 @@ import { isNumber } from 'lodash';
|
|||
import moment from 'moment';
|
||||
import dateMath from '@elastic/datemath';
|
||||
import * as rt from 'io-ts';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../utils/url_state';
|
||||
import { InfraTimerangeInput } from '../../graphql/types';
|
||||
|
||||
|
@ -159,12 +160,13 @@ const MetricsTimeRT = rt.type({
|
|||
|
||||
const mapToTimeUrlState = (value: any) => {
|
||||
const result = MetricsTimeRT.decode(value);
|
||||
if (result.isRight()) {
|
||||
const to = isNumber(result.value.to) ? moment(result.value.to).toISOString() : result.value.to;
|
||||
const from = isNumber(result.value.from)
|
||||
? moment(result.value.from).toISOString()
|
||||
: result.value.from;
|
||||
return { ...result.value, from, to };
|
||||
if (isRight(result)) {
|
||||
const resultValue = result.right;
|
||||
const to = isNumber(resultValue.to) ? moment(resultValue.to).toISOString() : resultValue.to;
|
||||
const from = isNumber(resultValue.from)
|
||||
? moment(resultValue.from).toISOString()
|
||||
: resultValue.from;
|
||||
return { ...resultValue, from, to };
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
|
|
@ -79,7 +79,10 @@ function isMetricExplorerOptions(subject: any): subject is MetricsExplorerOption
|
|||
const MetricOptional = t.partial({
|
||||
field: t.string,
|
||||
rate: t.boolean,
|
||||
color: t.union(values(MetricsExplorerColor).map(c => t.literal(c as string))),
|
||||
color: t.keyof(Object.fromEntries(values(MetricsExplorerColor).map(c => [c, null])) as Record<
|
||||
string,
|
||||
null
|
||||
>),
|
||||
label: t.string,
|
||||
});
|
||||
|
||||
|
@ -110,8 +113,12 @@ function isMetricExplorerOptions(subject: any): subject is MetricsExplorerOption
|
|||
|
||||
function isMetricExplorerChartOptions(subject: any): subject is MetricsExplorerChartOptions {
|
||||
const ChartOptions = t.type({
|
||||
yAxisMode: t.union(values(MetricsExplorerYAxisMode).map(v => t.literal(v as string))),
|
||||
type: t.union(values(MetricsExplorerChartType).map(v => t.literal(v as string))),
|
||||
yAxisMode: t.keyof(Object.fromEntries(
|
||||
values(MetricsExplorerYAxisMode).map(v => [v, null])
|
||||
) as Record<string, null>),
|
||||
type: t.keyof(Object.fromEntries(
|
||||
values(MetricsExplorerChartType).map(v => [v, null])
|
||||
) as Record<string, null>),
|
||||
stack: t.boolean,
|
||||
});
|
||||
const result = ChartOptions.decode(subject);
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import * as runtimeTypes from 'io-ts';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { constant, identity } from 'fp-ts/lib/function';
|
||||
import { useUrlState, replaceStateKeyInQueryString } from '../../utils/use_url_state';
|
||||
|
||||
const SOURCE_ID_URL_STATE_KEY = 'sourceId';
|
||||
|
@ -25,4 +28,7 @@ export const replaceSourceIdInQueryString = (sourceId: string) =>
|
|||
const sourceIdRuntimeType = runtimeTypes.union([runtimeTypes.string, runtimeTypes.undefined]);
|
||||
const encodeSourceIdUrlState = sourceIdRuntimeType.encode;
|
||||
const decodeSourceIdUrlState = (value: unknown) =>
|
||||
sourceIdRuntimeType.decode(value).getOrElse(undefined);
|
||||
pipe(
|
||||
sourceIdRuntimeType.decode(value),
|
||||
fold(constant(undefined), identity)
|
||||
);
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import { failure } from 'io-ts/lib/PathReporter';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import {
|
||||
InfraLogEntryColumn,
|
||||
InfraLogEntryFieldColumn,
|
||||
|
@ -182,11 +185,12 @@ export const createLogEntriesResolvers = (libs: {
|
|||
}));
|
||||
},
|
||||
async logItem(source, args, { req }) {
|
||||
const sourceConfiguration = SourceConfigurationRuntimeType.decode(
|
||||
source.configuration
|
||||
).getOrElseL(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
const sourceConfiguration = pipe(
|
||||
SourceConfigurationRuntimeType.decode(source.configuration),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
}, identity)
|
||||
);
|
||||
|
||||
return await libs.logEntries.getLogItem(req, args.id, sourceConfiguration);
|
||||
},
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import { failure } from 'io-ts/lib/PathReporter';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'rxjs';
|
||||
import { InfraSourceResolvers } from '../../graphql/types';
|
||||
import { InfraMetricsDomain } from '../../lib/domains/metrics_domain';
|
||||
import { SourceConfigurationRuntimeType } from '../../lib/sources';
|
||||
|
@ -31,11 +34,12 @@ export const createMetricResolvers = (
|
|||
} => ({
|
||||
InfraSource: {
|
||||
async metrics(source, args, { req }) {
|
||||
const sourceConfiguration = SourceConfigurationRuntimeType.decode(
|
||||
source.configuration
|
||||
).getOrElseL(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
const sourceConfiguration = pipe(
|
||||
SourceConfigurationRuntimeType.decode(source.configuration),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
}, identity)
|
||||
);
|
||||
|
||||
UsageCollector.countNode(args.nodeType);
|
||||
const options = {
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
import { UserInputError } from 'apollo-server-errors';
|
||||
import { failure } from 'io-ts/lib/PathReporter';
|
||||
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import {
|
||||
InfraSourceLogColumn,
|
||||
InfraSourceResolvers,
|
||||
|
@ -184,8 +187,11 @@ const compactObject = <T>(obj: T): CompactObject<T> =>
|
|||
const decodeLogColumns = (logColumns?: UpdateSourceLogColumnInput[] | null) =>
|
||||
logColumns
|
||||
? logColumns.map(logColumn =>
|
||||
SavedSourceConfigurationColumnRuntimeType.decode(logColumn).getOrElseL(errors => {
|
||||
throw new UserInputError(failure(errors).join('\n'));
|
||||
})
|
||||
pipe(
|
||||
SavedSourceConfigurationColumnRuntimeType.decode(logColumn),
|
||||
fold(errors => {
|
||||
throw new UserInputError(failure(errors).join('\n'));
|
||||
}, identity)
|
||||
)
|
||||
)
|
||||
: undefined;
|
||||
|
|
|
@ -12,7 +12,9 @@ import first from 'lodash/fp/first';
|
|||
import get from 'lodash/fp/get';
|
||||
import has from 'lodash/fp/has';
|
||||
import zip from 'lodash/fp/zip';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { identity, constant } from 'fp-ts/lib/function';
|
||||
import { compareTimeKeys, isTimeKey, TimeKey } from '../../../../common/time';
|
||||
import { JsonObject } from '../../../../common/typed_json';
|
||||
import {
|
||||
|
@ -165,13 +167,15 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter {
|
|||
|
||||
const response = await this.framework.callWithRequest<any, {}>(request, 'search', query);
|
||||
|
||||
return LogSummaryResponseRuntimeType.decode(response)
|
||||
.map(logSummaryResponse =>
|
||||
return pipe(
|
||||
LogSummaryResponseRuntimeType.decode(response),
|
||||
map(logSummaryResponse =>
|
||||
logSummaryResponse.aggregations.count_by_date.buckets.map(
|
||||
convertDateRangeBucketToSummaryBucket
|
||||
)
|
||||
)
|
||||
.getOrElse([]);
|
||||
),
|
||||
fold(constant([]), identity)
|
||||
);
|
||||
}
|
||||
|
||||
public async getLogItem(
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import * as rt from 'io-ts';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { getJobId } from '../../../common/log_analysis';
|
||||
import { throwErrors, createPlainError } from '../../../common/runtime_types';
|
||||
import { InfraBackendFrameworkAdapter, InfraFrameworkRequest } from '../adapters/framework';
|
||||
|
@ -132,10 +135,11 @@ export class InfraLogAnalysis {
|
|||
);
|
||||
}
|
||||
|
||||
const mlModelPlotBuckets = logRateModelPlotResponseRT
|
||||
.decode(mlModelPlotResponse)
|
||||
.map(response => response.aggregations.timestamp_buckets.buckets)
|
||||
.getOrElseL(throwErrors(createPlainError));
|
||||
const mlModelPlotBuckets = pipe(
|
||||
logRateModelPlotResponseRT.decode(mlModelPlotResponse),
|
||||
map(response => response.aggregations.timestamp_buckets.buckets),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
|
||||
return mlModelPlotBuckets.map(bucket => ({
|
||||
anomalies: bucket.filter_records.top_hits_record.hits.hits.map(({ _source: record }) => ({
|
||||
|
|
|
@ -8,6 +8,9 @@ import * as runtimeTypes from 'io-ts';
|
|||
import { failure } from 'io-ts/lib/PathReporter';
|
||||
import { Legacy } from 'kibana';
|
||||
|
||||
import { identity, constant } from 'fp-ts/lib/function';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { Pick3 } from '../../../common/utility_types';
|
||||
import { InfraConfigurationAdapter } from '../adapters/configuration';
|
||||
import { InfraFrameworkRequest, internalInfraFrameworkRequest } from '../adapters/framework';
|
||||
|
@ -182,15 +185,17 @@ export class InfraSources {
|
|||
|
||||
private async getStaticDefaultSourceConfiguration() {
|
||||
const staticConfiguration = await this.libs.configuration.get();
|
||||
const staticSourceConfiguration = runtimeTypes
|
||||
.type({
|
||||
sources: runtimeTypes.type({
|
||||
default: StaticSourceConfigurationRuntimeType,
|
||||
}),
|
||||
})
|
||||
.decode(staticConfiguration)
|
||||
.map(({ sources: { default: defaultConfiguration } }) => defaultConfiguration)
|
||||
.getOrElse({});
|
||||
const staticSourceConfiguration = pipe(
|
||||
runtimeTypes
|
||||
.type({
|
||||
sources: runtimeTypes.type({
|
||||
default: StaticSourceConfigurationRuntimeType,
|
||||
}),
|
||||
})
|
||||
.decode(staticConfiguration),
|
||||
map(({ sources: { default: defaultConfiguration } }) => defaultConfiguration),
|
||||
fold(constant({}), identity)
|
||||
);
|
||||
|
||||
return mergeSourceConfiguration(defaultSourceConfiguration, staticSourceConfiguration);
|
||||
}
|
||||
|
@ -238,14 +243,16 @@ const mergeSourceConfiguration = (
|
|||
);
|
||||
|
||||
const convertSavedObjectToSavedSourceConfiguration = (savedObject: unknown) =>
|
||||
SourceConfigurationSavedObjectRuntimeType.decode(savedObject)
|
||||
.map(savedSourceConfiguration => ({
|
||||
pipe(
|
||||
SourceConfigurationSavedObjectRuntimeType.decode(savedObject),
|
||||
map(savedSourceConfiguration => ({
|
||||
id: savedSourceConfiguration.id,
|
||||
version: savedSourceConfiguration.version,
|
||||
updatedAt: savedSourceConfiguration.updated_at,
|
||||
origin: 'stored' as 'stored',
|
||||
configuration: savedSourceConfiguration.attributes,
|
||||
}))
|
||||
.getOrElseL(errors => {
|
||||
})),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
}, identity)
|
||||
);
|
||||
|
|
|
@ -8,17 +8,22 @@
|
|||
|
||||
import * as runtimeTypes from 'io-ts';
|
||||
import moment from 'moment';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { chain } from 'fp-ts/lib/Either';
|
||||
|
||||
export const TimestampFromString = new runtimeTypes.Type<number, string>(
|
||||
'TimestampFromString',
|
||||
(input): input is number => typeof input === 'number',
|
||||
(input, context) =>
|
||||
runtimeTypes.string.validate(input, context).chain(stringInput => {
|
||||
const momentValue = moment(stringInput);
|
||||
return momentValue.isValid()
|
||||
? runtimeTypes.success(momentValue.valueOf())
|
||||
: runtimeTypes.failure(stringInput, context);
|
||||
}),
|
||||
pipe(
|
||||
runtimeTypes.string.validate(input, context),
|
||||
chain(stringInput => {
|
||||
const momentValue = moment(stringInput);
|
||||
return momentValue.isValid()
|
||||
? runtimeTypes.success(momentValue.valueOf())
|
||||
: runtimeTypes.failure(stringInput, context);
|
||||
})
|
||||
),
|
||||
output => new Date(output).toISOString()
|
||||
);
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import Boom from 'boom';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { InfraBackendLibs } from '../../../lib/infra_types';
|
||||
import {
|
||||
LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH,
|
||||
|
@ -23,9 +26,10 @@ export const initLogAnalysisGetLogEntryRateRoute = ({
|
|||
method: 'POST',
|
||||
path: LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH,
|
||||
handler: async (req, res) => {
|
||||
const payload = getLogEntryRateRequestPayloadRT
|
||||
.decode(req.payload)
|
||||
.getOrElseL(throwErrors(Boom.badRequest));
|
||||
const payload = pipe(
|
||||
getLogEntryRateRequestPayloadRT.decode(req.payload),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
const logEntryRateBuckets = await logAnalysis
|
||||
.getLogEntryRateBuckets(
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import Boom, { boomify } from 'boom';
|
||||
import { get } from 'lodash';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import {
|
||||
InfraMetadata,
|
||||
InfraMetadataWrappedRequest,
|
||||
|
@ -29,9 +32,10 @@ export const initMetadataRoute = (libs: InfraBackendLibs) => {
|
|||
path: '/api/infra/metadata',
|
||||
handler: async req => {
|
||||
try {
|
||||
const { nodeId, nodeType, sourceId } = InfraMetadataRequestRT.decode(
|
||||
req.payload
|
||||
).getOrElseL(throwErrors(Boom.badRequest));
|
||||
const { nodeId, nodeType, sourceId } = pipe(
|
||||
InfraMetadataRequestRT.decode(req.payload),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
const { configuration } = await libs.sources.getSourceConfiguration(req, sourceId);
|
||||
const metricsMetadata = await getMetricMetadata(
|
||||
|
@ -60,12 +64,15 @@ export const initMetadataRoute = (libs: InfraBackendLibs) => {
|
|||
|
||||
const id = metricsMetadata.id;
|
||||
const name = metricsMetadata.name || id;
|
||||
return InfraMetadataRT.decode({
|
||||
id,
|
||||
name,
|
||||
features: [...metricFeatures, ...cloudMetricsFeatures, ...apmMetricFeatures],
|
||||
info,
|
||||
}).getOrElseL(throwErrors(Boom.badImplementation));
|
||||
return pipe(
|
||||
InfraMetadataRT.decode({
|
||||
id,
|
||||
name,
|
||||
features: [...metricFeatures, ...cloudMetricsFeatures, ...apmMetricFeatures],
|
||||
info,
|
||||
}),
|
||||
fold(throwErrors(Boom.badImplementation), identity)
|
||||
);
|
||||
} catch (error) {
|
||||
throw boomify(error);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ import uuid from 'uuid';
|
|||
|
||||
import { SavedObjectsFindOptions } from 'src/core/server';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { Pick3 } from '../../../common/utility_types';
|
||||
import {
|
||||
PageInfoNote,
|
||||
|
@ -215,16 +218,18 @@ const convertSavedObjectToSavedNote = (
|
|||
savedObject: unknown,
|
||||
timelineVersion?: string | undefined | null
|
||||
): NoteSavedObject =>
|
||||
NoteSavedObjectRuntimeType.decode(savedObject)
|
||||
.map(savedNote => ({
|
||||
pipe(
|
||||
NoteSavedObjectRuntimeType.decode(savedObject),
|
||||
map(savedNote => ({
|
||||
noteId: savedNote.id,
|
||||
version: savedNote.version,
|
||||
timelineVersion,
|
||||
...savedNote.attributes,
|
||||
}))
|
||||
.getOrElseL(errors => {
|
||||
})),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
}, identity)
|
||||
);
|
||||
|
||||
// we have to use any here because the SavedObjectAttributes interface is like below
|
||||
// export interface SavedObjectAttributes {
|
||||
|
|
|
@ -11,6 +11,9 @@ import { getOr } from 'lodash/fp';
|
|||
|
||||
import { SavedObjectsFindOptions } from 'src/core/server';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { Pick3 } from '../../../common/utility_types';
|
||||
import { FrameworkRequest, internalFrameworkRequest } from '../framework';
|
||||
import {
|
||||
|
@ -203,16 +206,18 @@ const convertSavedObjectToSavedPinnedEvent = (
|
|||
savedObject: unknown,
|
||||
timelineVersion?: string | undefined | null
|
||||
): PinnedEventSavedObject =>
|
||||
PinnedEventSavedObjectRuntimeType.decode(savedObject)
|
||||
.map(savedPinnedEvent => ({
|
||||
pipe(
|
||||
PinnedEventSavedObjectRuntimeType.decode(savedObject),
|
||||
map(savedPinnedEvent => ({
|
||||
pinnedEventId: savedPinnedEvent.id,
|
||||
version: savedPinnedEvent.version,
|
||||
timelineVersion,
|
||||
...savedPinnedEvent.attributes,
|
||||
}))
|
||||
.getOrElseL(errors => {
|
||||
})),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
}, identity)
|
||||
);
|
||||
|
||||
// we have to use any here because the SavedObjectAttributes interface is like below
|
||||
// export interface SavedObjectAttributes {
|
||||
|
|
|
@ -5,16 +5,23 @@
|
|||
*/
|
||||
|
||||
import { failure } from 'io-ts/lib/PathReporter';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fold } from 'fp-ts/lib/Either';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { TimelineSavedObjectRuntimeType, TimelineSavedObject } from './types';
|
||||
|
||||
export const convertSavedObjectToSavedTimeline = (savedObject: unknown): TimelineSavedObject => {
|
||||
return TimelineSavedObjectRuntimeType.decode(savedObject)
|
||||
.map(savedTimeline => ({
|
||||
const timeline = pipe(
|
||||
TimelineSavedObjectRuntimeType.decode(savedObject),
|
||||
map(savedTimeline => ({
|
||||
savedObjectId: savedTimeline.id,
|
||||
version: savedTimeline.version,
|
||||
...savedTimeline.attributes,
|
||||
}))
|
||||
.getOrElseL(errors => {
|
||||
})),
|
||||
fold(errors => {
|
||||
throw new Error(failure(errors).join('\n'));
|
||||
});
|
||||
}, identity)
|
||||
);
|
||||
|
||||
return timeline;
|
||||
};
|
||||
|
|
|
@ -261,7 +261,6 @@
|
|||
"immer": "^1.5.0",
|
||||
"inline-style": "^2.0.0",
|
||||
"intl": "^1.2.5",
|
||||
"io-ts": "^1.4.2",
|
||||
"isbinaryfile": "4.0.2",
|
||||
"isomorphic-git": "0.55.5",
|
||||
"joi": "^13.5.2",
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
import expect from '@kbn/expect';
|
||||
import Joi from 'joi';
|
||||
import Hapi, { Util } from 'hapi';
|
||||
import { fromNullable } from 'fp-ts/lib/Option';
|
||||
import { fromNullable, map, filter, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { constant } from 'fp-ts/lib/function';
|
||||
|
||||
interface WebhookRequest extends Hapi.Request {
|
||||
payload: string;
|
||||
|
@ -19,16 +21,18 @@ export async function initPlugin(server: Hapi.Server, path: string) {
|
|||
) {
|
||||
const scheme = {
|
||||
async authenticate(request: Hapi.Request, h: Hapi.ResponseToolkit) {
|
||||
const credentials = fromNullable(request.headers.authorization)
|
||||
.map(authorization => authorization.split(/\s+/))
|
||||
.filter(parts => parts.length > 1)
|
||||
.map(parts => Buffer.from(parts[1], 'base64').toString())
|
||||
.filter(credentialsPart => credentialsPart.indexOf(':') !== -1)
|
||||
.map(credentialsPart => {
|
||||
const credentials = pipe(
|
||||
fromNullable(request.headers.authorization),
|
||||
map(authorization => authorization.split(/\s+/)),
|
||||
filter(parts => parts.length > 1),
|
||||
map(parts => Buffer.from(parts[1], 'base64').toString()),
|
||||
filter(credentialsPart => credentialsPart.indexOf(':') !== -1),
|
||||
map(credentialsPart => {
|
||||
const [username, password] = credentialsPart.split(':');
|
||||
return { username, password };
|
||||
})
|
||||
.getOrElse({ username: '', password: '' });
|
||||
}),
|
||||
getOrElse(constant({ username: '', password: '' }))
|
||||
);
|
||||
|
||||
return h.authenticated({ credentials });
|
||||
},
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { identity } from 'fp-ts/lib/function';
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import {
|
||||
LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH,
|
||||
getLogEntryRateRequestPayloadRT,
|
||||
|
@ -55,9 +58,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
const logEntryRateBuckets = getLogEntryRateSuccessReponsePayloadRT
|
||||
.decode(body)
|
||||
.getOrElseL(throwErrors(createPlainError));
|
||||
const logEntryRateBuckets = pipe(
|
||||
getLogEntryRateSuccessReponsePayloadRT.decode(body),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
|
||||
expect(logEntryRateBuckets.data.bucketDuration).to.be(15 * 60 * 1000);
|
||||
expect(logEntryRateBuckets.data.histogramBuckets).to.not.be.empty();
|
||||
|
@ -84,9 +88,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
const logEntryRateBuckets = getLogEntryRateSuccessReponsePayloadRT
|
||||
.decode(body)
|
||||
.getOrElseL(throwErrors(createPlainError));
|
||||
const logEntryRateBuckets = pipe(
|
||||
getLogEntryRateSuccessReponsePayloadRT.decode(body),
|
||||
fold(throwErrors(createPlainError), identity)
|
||||
);
|
||||
|
||||
expect(logEntryRateBuckets.data.bucketDuration).to.be(15 * 60 * 1000);
|
||||
expect(logEntryRateBuckets.data.histogramBuckets).to.be.empty();
|
||||
|
|
31
yarn.lock
31
yarn.lock
|
@ -6281,10 +6281,10 @@ base64-arraybuffer@0.1.5:
|
|||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
|
||||
|
||||
base64-js@0.0.2, base64-js@^1.2.1, base64-js@^1.2.3:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||
integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
|
||||
base64-js@0.0.2, base64-js@^1.2.1, base64-js@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||
|
||||
base64-js@0.0.8:
|
||||
version "0.0.8"
|
||||
|
@ -6296,10 +6296,10 @@ base64-js@^1.0.2, base64-js@^1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
|
||||
integrity sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||
base64-js@^1.2.3:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||
integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
|
||||
|
||||
base64id@1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -12637,6 +12637,11 @@ fp-ts@^1.14.2:
|
|||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.17.3.tgz#5064afc4bee8ddcaea567479bfc62d527e015825"
|
||||
integrity sha512-r4gHfAWaRrYPsmdzRl1U9CkpbdOi8fPg5F5KiazAadENz5DKdWEaCDPl2Tf92fvkZGD/ekZ3EHu3gtXIVcsXtA==
|
||||
|
||||
fp-ts@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.0.5.tgz#9560d8a6a4f53cbda9f9b31ed8d1458e41939e07"
|
||||
integrity sha512-opI5r+rVlpZE7Rhk0YtqsrmxGkbIw0dRNqGca8FEAMMnjomXotG+R9QkLQg20onx7R8qhepAn4CCOP8usma/Xw==
|
||||
|
||||
fragment-cache@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||
|
@ -15423,12 +15428,10 @@ invert-kv@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
||||
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
|
||||
|
||||
io-ts@^1.4.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.4.2.tgz#d3cb1ef7d7ba68d59af85d839a728aad7f4b1c28"
|
||||
integrity sha512-U4uw8jjj8jYZip7zHgBj40GW0DpYdVi1i0J3anezp2ytYHDg7+cKc7iIFlIyCh+NLwMxzwu6OQ/b9S61KUjPGg==
|
||||
dependencies:
|
||||
fp-ts "^1.0.0"
|
||||
io-ts@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.0.1.tgz#1261c12f915c2f48d16393a36966636b48a45aa1"
|
||||
integrity sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ==
|
||||
|
||||
ip-regex@^2.1.0:
|
||||
version "2.1.0"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue