Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Logs UI] Reimplement log source configuration routes in plain HTTP+JSON #64021

Merged
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4b54c1b
Add initial draft of http+json log source route
weltenwort Mar 13, 2020
9dec483
Add api test for log source read route
weltenwort Apr 15, 2020
2eccbe4
Extract common log source api types
weltenwort Apr 15, 2020
0426212
Add support for PATCH method in framework adapter
weltenwort Apr 16, 2020
12945ae
Add route to update source config
weltenwort Apr 16, 2020
8633729
Add tests for source config PATCH route
weltenwort Apr 16, 2020
5cceac0
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
weltenwort Apr 17, 2020
e9d4f89
Add log source status route
weltenwort Apr 17, 2020
308eb59
Start moving Logs UI over to the new source API (WIP)
weltenwort Apr 17, 2020
bc6c2e2
Correctly concatenate index names in fields domain
weltenwort Apr 20, 2020
cbe5a55
Only query field caps when at least one index exists
weltenwort Apr 20, 2020
7441b24
Provide more values from source hook to stay compatible
weltenwort Apr 20, 2020
a4d554a
Switch more pages to the new log source container
weltenwort Apr 20, 2020
a8d8a0e
Load data source on log rate and categories pages
weltenwort Apr 20, 2020
1a7369e
Add update operation to log source hook
weltenwort Apr 20, 2020
19a79c2
Move source configuration ui code to logs page dir
weltenwort Apr 20, 2020
8b23fe3
Trigger log source loading on a higher page level
weltenwort Apr 20, 2020
3cb1126
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
weltenwort Apr 20, 2020
bac844b
Make redirect tests more stable
weltenwort Apr 21, 2020
90795b9
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
elasticmachine Apr 23, 2020
1e9fe00
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
elasticmachine Apr 23, 2020
2476c50
Improve generic argument names
weltenwort Apr 24, 2020
fee6041
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
weltenwort Apr 24, 2020
df3eadb
Use scopedHistory-compatible Prompt component
weltenwort Apr 24, 2020
241a9ed
Fix custom prompt usage
weltenwort Apr 24, 2020
18d303f
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
weltenwort Apr 27, 2020
fff15e1
Use the new source provider in the context provider
weltenwort Apr 27, 2020
e1f753c
Move "view in context" modal into the page content
weltenwort Apr 27, 2020
e7c0806
Merge branch 'master' into logs-ui-reimplement-source-routes-as-http
weltenwort Apr 27, 2020
8cc81b5
Adapt to new source config getter signature
weltenwort Apr 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import * as rt from 'io-ts';
import { badRequestErrorRT, forbiddenErrorRT, routeTimingMetadataRT } from '../shared';
import { logSourceConfigurationRT } from './log_source_configuration';

/**
* request
*/

export const getLogSourceConfigurationRequestParamsRT = rt.type({
// the id of the source configuration
sourceId: rt.string,
});

export type GetLogSourceConfigurationRequestParams = rt.TypeOf<
typeof getLogSourceConfigurationRequestParamsRT
>;

/**
* response
*/

export const getLogSourceConfigurationSuccessResponsePayloadRT = rt.intersection([
rt.type({
data: logSourceConfigurationRT,
}),
rt.partial({
timing: routeTimingMetadataRT,
}),
]);

export type GetLogSourceConfigurationSuccessResponsePayload = rt.TypeOf<
typeof getLogSourceConfigurationSuccessResponsePayloadRT
>;

export const getLogSourceConfigurationErrorResponsePayloadRT = rt.union([
badRequestErrorRT,
forbiddenErrorRT,
]);

export type GetLogSourceConfigurationErrorReponsePayload = rt.TypeOf<
typeof getLogSourceConfigurationErrorResponsePayloadRT
>;

export const getLogSourceConfigurationResponsePayloadRT = rt.union([
getLogSourceConfigurationSuccessResponsePayloadRT,
getLogSourceConfigurationErrorResponsePayloadRT,
]);

export type GetLogSourceConfigurationReponsePayload = rt.TypeOf<
typeof getLogSourceConfigurationResponsePayloadRT
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import * as rt from 'io-ts';
import { routeTimingMetadataRT } from '../shared';
import {
getLogSourceConfigurationPath,
LOG_SOURCE_CONFIGURATION_PATH,
} from './log_source_configuration';

export const LOG_SOURCE_STATUS_PATH_SUFFIX = 'status';
export const LOG_SOURCE_STATUS_PATH = `${LOG_SOURCE_CONFIGURATION_PATH}/${LOG_SOURCE_STATUS_PATH_SUFFIX}`;
export const getLogSourceStatusPath = (sourceId: string) =>
`${getLogSourceConfigurationPath(sourceId)}/${LOG_SOURCE_STATUS_PATH_SUFFIX}`;

/**
* request
*/

export const getLogSourceStatusRequestParamsRT = rt.type({
// the id of the source configuration
sourceId: rt.string,
});

export type GetLogSourceStatusRequestParams = rt.TypeOf<typeof getLogSourceStatusRequestParamsRT>;

/**
* response
*/

const logIndexFieldRT = rt.strict({
name: rt.string,
type: rt.string,
searchable: rt.boolean,
aggregatable: rt.boolean,
});

export type LogIndexField = rt.TypeOf<typeof logIndexFieldRT>;

const logSourceStatusRT = rt.strict({
logIndexFields: rt.array(logIndexFieldRT),
logIndexNames: rt.array(rt.string),
});

export type LogSourceStatus = rt.TypeOf<typeof logSourceStatusRT>;

export const getLogSourceStatusSuccessResponsePayloadRT = rt.intersection([
rt.type({
data: logSourceStatusRT,
}),
rt.partial({
timing: routeTimingMetadataRT,
}),
]);

export type GetLogSourceStatusSuccessResponsePayload = rt.TypeOf<
typeof getLogSourceStatusSuccessResponsePayloadRT
>;
10 changes: 10 additions & 0 deletions x-pack/plugins/infra/common/http_api/log_sources/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export * from './get_log_source_configuration';
export * from './get_log_source_status';
export * from './log_source_configuration';
export * from './patch_log_source_configuration';
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import * as rt from 'io-ts';

export const LOG_SOURCE_CONFIGURATION_PATH_PREFIX = '/api/infra/log_source_configurations';
export const LOG_SOURCE_CONFIGURATION_PATH = `${LOG_SOURCE_CONFIGURATION_PATH_PREFIX}/{sourceId}`;
export const getLogSourceConfigurationPath = (sourceId: string) =>
`${LOG_SOURCE_CONFIGURATION_PATH_PREFIX}/${sourceId}`;

export const logSourceConfigurationOriginRT = rt.keyof({
fallback: null,
internal: null,
stored: null,
});

export type LogSourceConfigurationOrigin = rt.TypeOf<typeof logSourceConfigurationOriginRT>;

const logSourceFieldsConfigurationRT = rt.strict({
timestamp: rt.string,
tiebreaker: rt.string,
});

const logSourceCommonColumnConfigurationRT = rt.strict({
id: rt.string,
});

const logSourceTimestampColumnConfigurationRT = rt.strict({
timestampColumn: logSourceCommonColumnConfigurationRT,
});

const logSourceMessageColumnConfigurationRT = rt.strict({
messageColumn: logSourceCommonColumnConfigurationRT,
});

const logSourceFieldColumnConfigurationRT = rt.strict({
fieldColumn: rt.intersection([
logSourceCommonColumnConfigurationRT,
rt.strict({
field: rt.string,
}),
]),
});

const logSourceColumnConfigurationRT = rt.union([
logSourceTimestampColumnConfigurationRT,
logSourceMessageColumnConfigurationRT,
logSourceFieldColumnConfigurationRT,
]);

export const logSourceConfigurationPropertiesRT = rt.strict({
name: rt.string,
description: rt.string,
logAlias: rt.string,
fields: logSourceFieldsConfigurationRT,
logColumns: rt.array(logSourceColumnConfigurationRT),
});

export type LogSourceConfigurationProperties = rt.TypeOf<typeof logSourceConfigurationPropertiesRT>;

export const logSourceConfigurationRT = rt.exact(
rt.intersection([
rt.type({
id: rt.string,
origin: logSourceConfigurationOriginRT,
configuration: logSourceConfigurationPropertiesRT,
}),
rt.partial({
updatedAt: rt.number,
version: rt.string,
}),
])
);

export type LogSourceConfiguration = rt.TypeOf<typeof logSourceConfigurationRT>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import * as rt from 'io-ts';
import { badRequestErrorRT, forbiddenErrorRT } from '../shared';
import { getLogSourceConfigurationSuccessResponsePayloadRT } from './get_log_source_configuration';
import { logSourceConfigurationPropertiesRT } from './log_source_configuration';

/**
* request
*/

export const patchLogSourceConfigurationRequestParamsRT = rt.type({
// the id of the source configuration
sourceId: rt.string,
});

export type PatchLogSourceConfigurationRequestParams = rt.TypeOf<
typeof patchLogSourceConfigurationRequestParamsRT
>;

const logSourceConfigurationProperiesPatchRT = rt.partial({
...logSourceConfigurationPropertiesRT.type.props,
fields: rt.partial(logSourceConfigurationPropertiesRT.type.props.fields.type.props),
});

export type LogSourceConfigurationPropertiesPatch = rt.TypeOf<
typeof logSourceConfigurationProperiesPatchRT
>;

export const patchLogSourceConfigurationRequestBodyRT = rt.type({
data: logSourceConfigurationProperiesPatchRT,
});

export type PatchLogSourceConfigurationRequestBody = rt.TypeOf<
typeof patchLogSourceConfigurationRequestBodyRT
>;

/**
* response
*/

export const patchLogSourceConfigurationSuccessResponsePayloadRT = getLogSourceConfigurationSuccessResponsePayloadRT;

export type PatchLogSourceConfigurationSuccessResponsePayload = rt.TypeOf<
typeof patchLogSourceConfigurationSuccessResponsePayloadRT
>;

export const patchLogSourceConfigurationResponsePayloadRT = rt.union([
patchLogSourceConfigurationSuccessResponsePayloadRT,
badRequestErrorRT,
forbiddenErrorRT,
]);

export type PatchLogSourceConfigurationReponsePayload = rt.TypeOf<
typeof patchLogSourceConfigurationResponsePayloadRT
>;
14 changes: 14 additions & 0 deletions x-pack/plugins/infra/common/runtime_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
import { Errors, Type } from 'io-ts';
import { failure } from 'io-ts/lib/PathReporter';
import { RouteValidationFunction } from 'kibana/server';

type ErrorFactory = (message: string) => Error;

Expand All @@ -23,3 +24,16 @@ export const decodeOrThrow = <A, O, I>(
createError: ErrorFactory = createPlainError
) => (inputValue: I) =>
pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity));

type ValdidationResult<Value> = ReturnType<RouteValidationFunction<Value>>;

export const createValidationFunction = <A, O, I>(
runtimeType: Type<A, O, I>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit: I personally find generics easier / quicker to parse with meaningful type variables, could we change these from A, O, I?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I absolutely agree. I stuck with the io-ts naming scheme during experimentation and forgot to clean it up afterwards.

): RouteValidationFunction<A> => (inputValue, { badRequest, ok }) =>
pipe(
runtimeType.decode(inputValue),
fold<Errors, A, ValdidationResult<A>>(
(errors: Errors) => badRequest(failure(errors).join('\n')),
(result: A) => ok(result)
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

export * from './input_fields';
export { SourceConfigurationSettings } from './source_configuration_settings';
export { ViewSourceConfigurationButton } from './view_source_configuration_button';
6 changes: 3 additions & 3 deletions x-pack/plugins/infra/public/containers/logs/log_flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import createContainer from 'constate';
import { isString } from 'lodash';
import React, { useContext, useEffect, useMemo, useState } from 'react';

import { LogEntriesItem } from '../../../common/http_api';
import { UrlStateContainer } from '../../utils/url_state';
import { useTrackedPromise } from '../../utils/use_tracked_promise';
import { Source } from '../source';
import { fetchLogEntriesItem } from './log_entries/api/fetch_log_entries_item';
import { LogEntriesItem } from '../../../common/http_api';
import { useLogSourceContext } from './log_source';

export enum FlyoutVisibility {
hidden = 'hidden',
Expand All @@ -26,7 +26,7 @@ export interface FlyoutOptionsUrlState {
}

export const useLogFlyout = () => {
const { sourceId } = useContext(Source.Context);
const { sourceId } = useLogSourceContext();
const [flyoutVisible, setFlyoutVisibility] = useState<boolean>(false);
const [flyoutId, setFlyoutId] = useState<string | null>(null);
const [flyoutItem, setFlyoutItem] = useState<LogEntriesItem | null>(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
getLogSourceConfigurationPath,
getLogSourceConfigurationSuccessResponsePayloadRT,
} from '../../../../../common/http_api/log_sources';
import { decodeOrThrow } from '../../../../../common/runtime_types';
import { npStart } from '../../../../legacy_singletons';

export const callFetchLogSourceConfigurationAPI = async (sourceId: string) => {
const response = await npStart.http.fetch(getLogSourceConfigurationPath(sourceId), {
method: 'GET',
});

return decodeOrThrow(getLogSourceConfigurationSuccessResponsePayloadRT)(response);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
getLogSourceStatusPath,
getLogSourceStatusSuccessResponsePayloadRT,
} from '../../../../../common/http_api/log_sources';
import { decodeOrThrow } from '../../../../../common/runtime_types';
import { npStart } from '../../../../legacy_singletons';

export const callFetchLogSourceStatusAPI = async (sourceId: string) => {
const response = await npStart.http.fetch(getLogSourceStatusPath(sourceId), {
method: 'GET',
});

return decodeOrThrow(getLogSourceStatusSuccessResponsePayloadRT)(response);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
getLogSourceConfigurationPath,
patchLogSourceConfigurationSuccessResponsePayloadRT,
patchLogSourceConfigurationRequestBodyRT,
LogSourceConfigurationPropertiesPatch,
} from '../../../../../common/http_api/log_sources';
import { decodeOrThrow } from '../../../../../common/runtime_types';
import { npStart } from '../../../../legacy_singletons';

export const callPatchLogSourceConfigurationAPI = async (
sourceId: string,
patchedProperties: LogSourceConfigurationPropertiesPatch
) => {
const response = await npStart.http.fetch(getLogSourceConfigurationPath(sourceId), {
method: 'PATCH',
body: JSON.stringify(
patchLogSourceConfigurationRequestBodyRT.encode({
data: patchedProperties,
})
),
});

return decodeOrThrow(patchLogSourceConfigurationSuccessResponsePayloadRT)(response);
};
Loading