mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Dataset quality] Enable page for synthetics (#191846)
Closes https://github.com/elastic/observability-dev/issues/3457. This PR enables Dataset quality for being used for synthetics datasets. ### Changes - Added `synthetics` to `KNOWN_TYPES` array. - Permissions were updated in `Data_quality` plugin. https://github.com/user-attachments/assets/e9945012-166b-4704-bb73-11e6fe6eed76
This commit is contained in:
parent
4ca6f1d77d
commit
1b2cbf15d8
27 changed files with 542 additions and 77 deletions
|
@ -35,3 +35,4 @@ export { appendHash, hashKeysOf } from './src/lib/utils/hash';
|
|||
export type { ESDocumentWithOperation, SynthtraceESAction, SynthtraceGenerator } from './src/types';
|
||||
export { log, type LogDocument, LONG_FIELD_NAME } from './src/lib/logs';
|
||||
export { type AssetDocument } from './src/lib/assets';
|
||||
export { syntheticsMonitor, type SyntheticsMonitorDocument } from './src/lib/synthetics';
|
||||
|
|
103
packages/kbn-apm-synthtrace-client/src/lib/synthetics/index.ts
Normal file
103
packages/kbn-apm-synthtrace-client/src/lib/synthetics/index.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Fields } from '../entity';
|
||||
import { Serializable } from '../serializable';
|
||||
|
||||
export type SyntheticsMonitorDocument = Fields &
|
||||
Partial<{
|
||||
'data_stream.namespace': string;
|
||||
'data_stream.type': string;
|
||||
'data_stream.dataset': string;
|
||||
'monitor.id': string;
|
||||
'monitor.origin': string;
|
||||
'monitor.name': string;
|
||||
'monitor.type': string;
|
||||
'monitor.check_group': string;
|
||||
'monitor.timespan.lt': string;
|
||||
'monitor.timespan.gte': string;
|
||||
'monitor.duration.us'?: number;
|
||||
'monitor.ip'?: string;
|
||||
'monitor.project.name'?: string;
|
||||
'monitor.project.id'?: string;
|
||||
'monitor.fleet_managed'?: boolean;
|
||||
'monitor.status'?: string;
|
||||
'synthetics.type'?: string;
|
||||
'synthetics.step.index'?: number;
|
||||
'observer.os.name'?: string;
|
||||
'observer.product'?: string;
|
||||
}>;
|
||||
|
||||
type MonitorDataStream =
|
||||
| 'http'
|
||||
| 'tcp'
|
||||
| 'icmp'
|
||||
| 'browser'
|
||||
| 'browser.screenshot'
|
||||
| 'browser.network';
|
||||
|
||||
class SyntheticsMonitor extends Serializable<SyntheticsMonitorDocument> {
|
||||
constructor(fields: SyntheticsMonitorDocument) {
|
||||
super({
|
||||
...fields,
|
||||
});
|
||||
}
|
||||
|
||||
namespace(value: string) {
|
||||
this.fields['data_stream.namespace'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
dataset(value: MonitorDataStream) {
|
||||
this.fields['data_stream.dataset'] = value;
|
||||
|
||||
if (value === 'browser.screenshot' || value === 'browser.network') {
|
||||
this.fields['monitor.type'] = 'browser';
|
||||
return this;
|
||||
}
|
||||
|
||||
this.fields['monitor.type'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
name(value: string) {
|
||||
this.fields['monitor.name'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
origin(value: string) {
|
||||
this.fields['monitor.origin'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
ip(value: string) {
|
||||
this.fields['monitor.ip'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
status(value: string) {
|
||||
this.fields['monitor.status'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
timestamp(time: number) {
|
||||
super.timestamp(time);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function create(): SyntheticsMonitor {
|
||||
return new SyntheticsMonitor({
|
||||
'data_stream.namespace': 'default',
|
||||
'data_stream.type': 'synthetics',
|
||||
}).dataset('http');
|
||||
}
|
||||
|
||||
export const syntheticsMonitor = {
|
||||
create,
|
||||
};
|
|
@ -22,6 +22,7 @@ This library can currently be used in two ways:
|
|||
- `Timerange`: an object that will return an array of timestamps based on an interval and a rate. These timestamps can be used to generate events/metricsets.
|
||||
- `Transaction`, `Span`, `APMError` and `Metricset`: events/metricsets that occur on an instance. For more background, see the [explanation of the APM data model](https://www.elastic.co/guide/en/apm/get-started/7.15/apm-data-model.html)
|
||||
- `Log`: An instance of Log generating Service which supports additional helpers to customise fields like `messages`, `logLevel`
|
||||
- `SyntheticsMonitor`: An instance of Synthetic monitor. For more information see [Synthetic monitoring](https://www.elastic.co/guide/en/observability/current/monitor-uptime-synthetics.html).
|
||||
|
||||
#### Example
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ export { InfraSynthtraceKibanaClient } from './src/lib/infra/infra_synthtrace_ki
|
|||
export { MonitoringSynthtraceEsClient } from './src/lib/monitoring/monitoring_synthtrace_es_client';
|
||||
export { LogsSynthtraceEsClient } from './src/lib/logs/logs_synthtrace_es_client';
|
||||
export { AssetsSynthtraceEsClient } from './src/lib/assets/assets_synthtrace_es_client';
|
||||
export { SyntheticsSynthtraceEsClient } from './src/lib/synthetics/synthetics_synthtrace_es_client';
|
||||
export {
|
||||
addObserverVersionTransform,
|
||||
deleteSummaryFieldTransform,
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
*/
|
||||
|
||||
import { Timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { ApmSynthtraceEsClient, InfraSynthtraceEsClient, LogsSynthtraceEsClient } from '../..';
|
||||
import {
|
||||
ApmSynthtraceEsClient,
|
||||
InfraSynthtraceEsClient,
|
||||
LogsSynthtraceEsClient,
|
||||
SyntheticsSynthtraceEsClient,
|
||||
} from '../..';
|
||||
import { AssetsSynthtraceEsClient } from '../lib/assets/assets_synthtrace_es_client';
|
||||
import { Logger } from '../lib/utils/create_logger';
|
||||
import { ScenarioReturnType } from '../lib/utils/with_client';
|
||||
|
@ -18,6 +23,7 @@ interface EsClients {
|
|||
logsEsClient: LogsSynthtraceEsClient;
|
||||
infraEsClient: InfraSynthtraceEsClient;
|
||||
assetsEsClient: AssetsSynthtraceEsClient;
|
||||
syntheticsEsClient: SyntheticsSynthtraceEsClient;
|
||||
}
|
||||
|
||||
type Generate<TFields> = (options: {
|
||||
|
|
|
@ -14,6 +14,7 @@ import { getKibanaClient } from './get_kibana_client';
|
|||
import { getServiceUrls } from './get_service_urls';
|
||||
import { RunOptions } from './parse_run_cli_flags';
|
||||
import { getAssetsEsClient } from './get_assets_es_client';
|
||||
import { getSyntheticsEsClient } from './get_synthetics_es_client';
|
||||
|
||||
export async function bootstrap(runOptions: RunOptions) {
|
||||
const logger = createLogger(runOptions.logLevel);
|
||||
|
@ -61,11 +62,18 @@ export async function bootstrap(runOptions: RunOptions) {
|
|||
concurrency: runOptions.concurrency,
|
||||
});
|
||||
|
||||
const syntheticsEsClient = getSyntheticsEsClient({
|
||||
target: esUrl,
|
||||
logger,
|
||||
concurrency: runOptions.concurrency,
|
||||
});
|
||||
|
||||
if (runOptions.clean) {
|
||||
await apmEsClient.clean();
|
||||
await logsEsClient.clean();
|
||||
await infraEsClient.clean();
|
||||
await assetsEsClient.clean();
|
||||
await syntheticsEsClient.clean();
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -74,6 +82,7 @@ export async function bootstrap(runOptions: RunOptions) {
|
|||
logsEsClient,
|
||||
infraEsClient,
|
||||
assetsEsClient,
|
||||
syntheticsEsClient,
|
||||
version,
|
||||
kibanaUrl,
|
||||
esUrl,
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import { Logger } from '../../lib/utils/create_logger';
|
||||
import { RunOptions } from './parse_run_cli_flags';
|
||||
import { getEsClientTlsSettings } from './ssl';
|
||||
import { SyntheticsSynthtraceEsClient } from '../../lib/synthetics/synthetics_synthtrace_es_client';
|
||||
|
||||
export function getSyntheticsEsClient({
|
||||
target,
|
||||
logger,
|
||||
concurrency,
|
||||
}: Pick<RunOptions, 'concurrency'> & {
|
||||
target: string;
|
||||
logger: Logger;
|
||||
}) {
|
||||
const client = new Client({
|
||||
node: target,
|
||||
tls: getEsClientTlsSettings(target),
|
||||
});
|
||||
|
||||
return new SyntheticsSynthtraceEsClient({
|
||||
client,
|
||||
logger,
|
||||
concurrency,
|
||||
});
|
||||
}
|
|
@ -25,9 +25,8 @@ export async function startLiveDataUpload({
|
|||
}) {
|
||||
const file = runOptions.file;
|
||||
|
||||
const { logger, apmEsClient, logsEsClient, infraEsClient, assetsEsClient } = await bootstrap(
|
||||
runOptions
|
||||
);
|
||||
const { logger, apmEsClient, logsEsClient, infraEsClient, assetsEsClient, syntheticsEsClient } =
|
||||
await bootstrap(runOptions);
|
||||
|
||||
const scenario = await getScenario({ file, logger });
|
||||
const { generate } = await scenario({ ...runOptions, logger });
|
||||
|
@ -65,7 +64,7 @@ export async function startLiveDataUpload({
|
|||
|
||||
const generatorsAndClients = generate({
|
||||
range: timerange(bucketFrom.getTime(), bucketTo.getTime()),
|
||||
clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient },
|
||||
clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient, syntheticsEsClient },
|
||||
});
|
||||
|
||||
const generatorsAndClientsArray = castArray(generatorsAndClients);
|
||||
|
|
|
@ -17,6 +17,7 @@ import { RunOptions } from './parse_run_cli_flags';
|
|||
import { getLogsEsClient } from './get_logs_es_client';
|
||||
import { getInfraEsClient } from './get_infra_es_client';
|
||||
import { getAssetsEsClient } from './get_assets_es_client';
|
||||
import { getSyntheticsEsClient } from './get_synthetics_es_client';
|
||||
|
||||
export interface WorkerData {
|
||||
bucketFrom: Date;
|
||||
|
@ -56,6 +57,12 @@ async function start() {
|
|||
logger,
|
||||
});
|
||||
|
||||
const syntheticsEsClient = getSyntheticsEsClient({
|
||||
concurrency: runOptions.concurrency,
|
||||
target: esUrl,
|
||||
logger,
|
||||
});
|
||||
|
||||
const file = runOptions.file;
|
||||
|
||||
const scenario = await logger.perf('get_scenario', () => getScenario({ file, logger }));
|
||||
|
@ -70,6 +77,7 @@ async function start() {
|
|||
logsEsClient,
|
||||
infraEsClient,
|
||||
assetsEsClient,
|
||||
syntheticsEsClient,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,7 +86,7 @@ async function start() {
|
|||
const generatorsAndClients = logger.perf('generate_scenario', () =>
|
||||
generate({
|
||||
range: timerange(bucketFrom, bucketTo),
|
||||
clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient },
|
||||
clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient, syntheticsEsClient },
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
*/
|
||||
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import { ESDocumentWithOperation } from '@kbn/apm-synthtrace-client';
|
||||
import { pipeline, Readable, Transform } from 'stream';
|
||||
import { pipeline, Readable } from 'stream';
|
||||
import { LogDocument } from '@kbn/apm-synthtrace-client/src/lib/logs';
|
||||
import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { SynthtraceEsClient, SynthtraceEsClientOptions } from '../shared/base_client';
|
||||
import { getSerializeTransform } from '../shared/get_serialize_transform';
|
||||
import { Logger } from '../utils/create_logger';
|
||||
import { indexTemplates, IndexTemplateName } from './custom_logsdb_index_templates';
|
||||
import { getRoutingTransform } from '../shared/data_stream_get_routing_transform';
|
||||
|
||||
export type LogsSynthtraceEsClientOptions = Omit<SynthtraceEsClientOptions, 'pipeline'>;
|
||||
|
||||
|
@ -66,7 +66,7 @@ function logsPipeline() {
|
|||
return pipeline(
|
||||
base,
|
||||
getSerializeTransform<LogDocument>(),
|
||||
getRoutingTransform(),
|
||||
getRoutingTransform('logs'),
|
||||
(err: unknown) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
|
@ -75,22 +75,3 @@ function logsPipeline() {
|
|||
);
|
||||
};
|
||||
}
|
||||
|
||||
function getRoutingTransform() {
|
||||
return new Transform({
|
||||
objectMode: true,
|
||||
transform(document: ESDocumentWithOperation<LogDocument>, encoding, callback) {
|
||||
if (
|
||||
'data_stream.type' in document &&
|
||||
'data_stream.dataset' in document &&
|
||||
'data_stream.namespace' in document
|
||||
) {
|
||||
document._index = `${document['data_stream.type']}-${document['data_stream.dataset']}-${document['data_stream.namespace']}`;
|
||||
} else {
|
||||
throw new Error('Cannot determine index for event');
|
||||
}
|
||||
|
||||
callback(null, document);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ESDocumentWithOperation, Fields } from '@kbn/apm-synthtrace-client';
|
||||
import { Transform } from 'stream';
|
||||
|
||||
export function getRoutingTransform<T extends Fields>(dataStreamType: string) {
|
||||
return new Transform({
|
||||
objectMode: true,
|
||||
transform(document: ESDocumentWithOperation<T>, encoding, callback) {
|
||||
if ('data_stream.dataset' in document && 'data_stream.namespace' in document) {
|
||||
document._index = `${dataStreamType}-${document['data_stream.dataset']}-${document['data_stream.namespace']}`;
|
||||
} else {
|
||||
throw new Error('Cannot determine index for event');
|
||||
}
|
||||
|
||||
callback(null, document);
|
||||
},
|
||||
});
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import { SyntheticsMonitorDocument } from '@kbn/apm-synthtrace-client';
|
||||
import { pipeline, Readable } from 'stream';
|
||||
import { SynthtraceEsClient, SynthtraceEsClientOptions } from '../shared/base_client';
|
||||
import { getSerializeTransform } from '../shared/get_serialize_transform';
|
||||
import { Logger } from '../utils/create_logger';
|
||||
import { getRoutingTransform } from '../shared/data_stream_get_routing_transform';
|
||||
|
||||
export type SyntheticsSynthtraceEsClientOptions = Omit<SynthtraceEsClientOptions, 'pipeline'>;
|
||||
|
||||
export class SyntheticsSynthtraceEsClient extends SynthtraceEsClient<SyntheticsMonitorDocument> {
|
||||
constructor(options: { client: Client; logger: Logger } & SyntheticsSynthtraceEsClientOptions) {
|
||||
super({
|
||||
...options,
|
||||
pipeline: syntheticsPipeline(),
|
||||
});
|
||||
this.dataStreams = ['synthetics-*-*'];
|
||||
}
|
||||
}
|
||||
|
||||
function syntheticsPipeline() {
|
||||
return (base: Readable) => {
|
||||
return pipeline(
|
||||
base,
|
||||
getSerializeTransform<SyntheticsMonitorDocument>(),
|
||||
getRoutingTransform('synthetics'),
|
||||
(err: unknown) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import {
|
||||
generateShortId,
|
||||
SyntheticsMonitorDocument,
|
||||
syntheticsMonitor,
|
||||
} from '@kbn/apm-synthtrace-client';
|
||||
import { Scenario } from '../cli/scenario';
|
||||
import { withClient } from '../lib/utils/with_client';
|
||||
import { getIpAddress } from './helpers/logs_mock_data';
|
||||
import { getAtIndexOrRandom } from './helpers/get_at_index_or_random';
|
||||
|
||||
const MORE_THAN_1024_CHARS =
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?';
|
||||
|
||||
const MONITOR_NAMES = Array(4)
|
||||
.fill(null)
|
||||
.map((_, idx) => `synth-monitor-${idx}`);
|
||||
|
||||
const ORIGINS = Array(4)
|
||||
.fill(null)
|
||||
.map((_, idx) => `synth-origin-${idx}`);
|
||||
|
||||
const STATUS = ['up', 'down', 'disabled'];
|
||||
|
||||
const OS = ['linux', 'windows', 'mac'];
|
||||
|
||||
const scenario: Scenario<SyntheticsMonitorDocument> = async (runOptions) => {
|
||||
return {
|
||||
generate: ({ range, clients: { syntheticsEsClient } }) => {
|
||||
const { logger } = runOptions;
|
||||
|
||||
const constructSyntheticsMonitorCommonData = (isMalformed?: boolean) => {
|
||||
const index = Math.floor(Math.random() * 4);
|
||||
const monitorName = getAtIndexOrRandom(MONITOR_NAMES, index);
|
||||
const origin = getAtIndexOrRandom(ORIGINS, index);
|
||||
const ip = getIpAddress(index);
|
||||
const status = getAtIndexOrRandom(STATUS, index);
|
||||
const os = getAtIndexOrRandom(OS, index);
|
||||
|
||||
const commonSyntheticsMonitorEntryFields: SyntheticsMonitorDocument = {
|
||||
'monitor.id': generateShortId(),
|
||||
'monitor.check_group': generateShortId(),
|
||||
'monitor.timespan.lt': '2024-08-30T11:03:33.594Z',
|
||||
'monitor.timespan.gte': '2024-08-30T11:02:33.594Z',
|
||||
};
|
||||
|
||||
return {
|
||||
index,
|
||||
monitorName,
|
||||
origin,
|
||||
ip,
|
||||
status,
|
||||
os,
|
||||
commonLongEntryFields: commonSyntheticsMonitorEntryFields,
|
||||
};
|
||||
};
|
||||
|
||||
const datasetSynth1Monitors = (timestamp: number) => {
|
||||
const { monitorName, origin, ip, status, commonLongEntryFields } =
|
||||
constructSyntheticsMonitorCommonData();
|
||||
|
||||
return syntheticsMonitor
|
||||
.create()
|
||||
.dataset('http')
|
||||
.name(monitorName)
|
||||
.origin(origin)
|
||||
.ip(ip)
|
||||
.defaults(commonLongEntryFields)
|
||||
.timestamp(timestamp)
|
||||
.status(status);
|
||||
};
|
||||
|
||||
const datasetSynth2Monitors = (i: number, timestamp: number) => {
|
||||
const { monitorName, origin, commonLongEntryFields } =
|
||||
constructSyntheticsMonitorCommonData();
|
||||
const isMalformed = i % 90 === 0;
|
||||
return syntheticsMonitor
|
||||
.create()
|
||||
.dataset('browser')
|
||||
.name(monitorName)
|
||||
.origin(origin)
|
||||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'synthetics.type': isMalformed
|
||||
? MORE_THAN_1024_CHARS // "ignore_above": 1024 in mapping
|
||||
: 'step/metrics',
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
};
|
||||
|
||||
const datasetSynth3Monitors = (i: number, timestamp: number) => {
|
||||
const { monitorName, origin, os, commonLongEntryFields } =
|
||||
constructSyntheticsMonitorCommonData();
|
||||
const isMalformed = i % 60 === 0;
|
||||
return syntheticsMonitor
|
||||
.create()
|
||||
.dataset('browser.screenshot')
|
||||
.name(monitorName)
|
||||
.origin(origin)
|
||||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'synthetics.type': 'step/screenshot_ref',
|
||||
'observer.os.name': isMalformed
|
||||
? MORE_THAN_1024_CHARS // "ignore_above": 1024 in mapping
|
||||
: os,
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
};
|
||||
|
||||
const datasetSynth4Monitors = (i: number, timestamp: number) => {
|
||||
const { monitorName, origin, commonLongEntryFields } =
|
||||
constructSyntheticsMonitorCommonData();
|
||||
const isMalformed = i % 30 === 0;
|
||||
return syntheticsMonitor
|
||||
.create()
|
||||
.dataset('browser.network')
|
||||
.name(monitorName)
|
||||
.origin(origin)
|
||||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'synthetics.type': isMalformed
|
||||
? MORE_THAN_1024_CHARS // "ignore_above": 1024 in mapping
|
||||
: 'journey/network_info',
|
||||
'observer.product': isMalformed
|
||||
? MORE_THAN_1024_CHARS // "ignore_above": 1024 in mapping
|
||||
: `synth-product-${i}`,
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
};
|
||||
|
||||
const monitors = range
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => {
|
||||
return Array(200)
|
||||
.fill(0)
|
||||
.flatMap((_, index) => [
|
||||
datasetSynth1Monitors(timestamp),
|
||||
datasetSynth2Monitors(index, timestamp),
|
||||
datasetSynth3Monitors(index, timestamp),
|
||||
datasetSynth4Monitors(index, timestamp),
|
||||
]);
|
||||
});
|
||||
|
||||
return withClient(
|
||||
syntheticsEsClient,
|
||||
logger.perf('generating_synthetics_monitors', () => monitors)
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default scenario;
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { randomInt } from 'crypto';
|
||||
|
||||
// Utility function to get a random element from an array
|
||||
export const getAtIndexOrRandom = <T>(values: T[], index?: number) =>
|
||||
values[index ?? randomInt(values.length)];
|
|
@ -10,16 +10,13 @@ import { generateShortId } from '@kbn/apm-synthtrace-client';
|
|||
import { faker } from '@faker-js/faker';
|
||||
import { randomInt } from 'crypto';
|
||||
import moment from 'moment';
|
||||
import { getAtIndexOrRandom } from './get_at_index_or_random';
|
||||
|
||||
const {
|
||||
internet: { ipv4, userAgent, httpMethod, httpStatusCode },
|
||||
word: { noun, verb },
|
||||
} = faker;
|
||||
|
||||
// Utility function to get a random element from an array
|
||||
const getAtIndexOrRandom = <T>(values: T[], index?: number) =>
|
||||
values[index ?? randomInt(values.length)];
|
||||
|
||||
// Arrays for data
|
||||
const LOG_LEVELS: string[] = ['FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'];
|
||||
|
||||
|
|
|
@ -39,6 +39,13 @@ export class DataQualityPlugin implements Plugin<void, void, any, any> {
|
|||
['metrics-*-*']: ['read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
ui: [],
|
||||
requiredClusterPrivileges: [],
|
||||
requiredIndexPrivileges: {
|
||||
['synthetics-*-*']: ['read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
|
|
@ -42,4 +42,4 @@ export const MAX_DEGRADED_FIELDS = 1000;
|
|||
export const MASKED_FIELD_PLACEHOLDER = '<custom field>';
|
||||
export const UNKOWN_FIELD_PLACEHOLDER = '<unkwon>';
|
||||
|
||||
export const KNOWN_TYPES: DataStreamType[] = ['logs', 'metrics', 'traces'];
|
||||
export const KNOWN_TYPES: DataStreamType[] = ['logs', 'metrics', 'traces', 'synthetics'];
|
||||
|
|
|
@ -10,20 +10,20 @@ export enum DatasetQualityUsername {
|
|||
viewerUser = 'viewer',
|
||||
readUser = 'readUser',
|
||||
editorUser = 'editor',
|
||||
datasetQualityLogsUser = 'dataset_quality_logs_user',
|
||||
datasetQualityMonitorUser = 'dataset_quality_monitor_user',
|
||||
}
|
||||
|
||||
export enum DatasetQualityCustomRolename {
|
||||
datasetQualityLogsUser = 'dataset_quality_logs_user',
|
||||
datasetQualityMonitorUser = 'dataset_quality_monitor_user',
|
||||
datasetQualityReadUser = 'dataset_quality_read_user',
|
||||
}
|
||||
|
||||
export const customRoles = {
|
||||
[DatasetQualityCustomRolename.datasetQualityLogsUser]: {
|
||||
[DatasetQualityCustomRolename.datasetQualityMonitorUser]: {
|
||||
elasticsearch: {
|
||||
indices: [
|
||||
{
|
||||
names: ['logs-*-*'],
|
||||
names: ['logs-*-*', 'metrics-*-*', 'traces-*-*', 'synthetics-*-*'],
|
||||
privileges: ['monitor', 'view_index_metadata'],
|
||||
},
|
||||
],
|
||||
|
@ -55,9 +55,9 @@ export const users: Record<
|
|||
[DatasetQualityUsername.editorUser]: {
|
||||
builtInRoleNames: ['editor'],
|
||||
},
|
||||
[DatasetQualityUsername.datasetQualityLogsUser]: {
|
||||
[DatasetQualityUsername.datasetQualityMonitorUser]: {
|
||||
builtInRoleNames: ['editor'],
|
||||
customRoleNames: [DatasetQualityCustomRolename.datasetQualityLogsUser],
|
||||
customRoleNames: [DatasetQualityCustomRolename.datasetQualityMonitorUser],
|
||||
},
|
||||
[DatasetQualityUsername.readUser]: {
|
||||
customRoleNames: [DatasetQualityCustomRolename.datasetQualityReadUser],
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LogLevel, LogsSynthtraceEsClient, createLogger } from '@kbn/apm-synthtrace';
|
||||
import {
|
||||
LogLevel,
|
||||
LogsSynthtraceEsClient,
|
||||
SyntheticsSynthtraceEsClient,
|
||||
createLogger,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import { createDatasetQualityUsers } from '@kbn/dataset-quality-plugin/server/test_helpers/create_dataset_quality_users';
|
||||
import {
|
||||
DATASET_QUALITY_TEST_PASSWORD,
|
||||
|
@ -55,7 +60,7 @@ export type DatasetQualityApiClientKey =
|
|||
| 'readUser'
|
||||
| 'adminUser'
|
||||
| 'writeUser'
|
||||
| 'datasetQualityLogsUser';
|
||||
| 'datasetQualityMonitorUser';
|
||||
|
||||
export type DatasetQualityApiClient = Record<
|
||||
DatasetQualityApiClientKey,
|
||||
|
@ -72,6 +77,9 @@ export interface CreateTest {
|
|||
logSynthtraceEsClient: (
|
||||
context: InheritedFtrProviderContext
|
||||
) => Promise<LogsSynthtraceEsClient>;
|
||||
syntheticsSynthtraceEsClient: (
|
||||
context: InheritedFtrProviderContext
|
||||
) => SyntheticsSynthtraceEsClient;
|
||||
datasetQualityApiClient: (context: InheritedFtrProviderContext) => DatasetQualityApiClient;
|
||||
packageService: ({ getService }: FtrProviderContext) => ReturnType<typeof PackageService>;
|
||||
};
|
||||
|
@ -133,6 +141,12 @@ export function createTestConfig(
|
|||
logger: createLogger(LogLevel.info),
|
||||
refreshAfterIndex: true,
|
||||
}),
|
||||
syntheticsSynthtraceEsClient: (context: InheritedFtrProviderContext) =>
|
||||
new SyntheticsSynthtraceEsClient({
|
||||
client: context.getService('es'),
|
||||
logger: createLogger(LogLevel.info),
|
||||
refreshAfterIndex: true,
|
||||
}),
|
||||
datasetQualityApiClient: async (_: InheritedFtrProviderContext) => {
|
||||
const { username, password } = servers.kibana;
|
||||
const esUrl = format(esServer);
|
||||
|
@ -164,9 +178,9 @@ export function createTestConfig(
|
|||
kibanaServer,
|
||||
username: DatasetQualityUsername.editorUser,
|
||||
}),
|
||||
datasetQualityLogsUser: await getDatasetQualityApiClient({
|
||||
datasetQualityMonitorUser: await getDatasetQualityApiClient({
|
||||
kibanaServer,
|
||||
username: DatasetQualityUsername.datasetQualityLogsUser,
|
||||
username: DatasetQualityUsername.datasetQualityMonitorUser,
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
|
|
@ -73,7 +73,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
it('returns error when dataStream param is not provided', async () => {
|
||||
const expectedMessage = 'Data Stream name cannot be empty';
|
||||
const err = await expectToReject<DatasetQualityApiError>(() =>
|
||||
callApiAs('datasetQualityLogsUser', encodeURIComponent(' '))
|
||||
callApiAs('datasetQualityMonitorUser', encodeURIComponent(' '))
|
||||
);
|
||||
expect(err.res.status).to.be(400);
|
||||
expect(err.res.body.message.indexOf(expectedMessage)).to.greaterThan(-1);
|
||||
|
@ -82,18 +82,24 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
it('returns {} if matching data stream is not available', async () => {
|
||||
const nonExistentDataSet = 'Non-existent';
|
||||
const nonExistentDataStream = `${type}-${nonExistentDataSet}-${namespace}`;
|
||||
const resp = await callApiAs('datasetQualityLogsUser', nonExistentDataStream);
|
||||
const resp = await callApiAs('datasetQualityMonitorUser', nonExistentDataStream);
|
||||
expect(resp.body).empty();
|
||||
});
|
||||
|
||||
it('returns "sizeBytes" correctly', async () => {
|
||||
const resp = await callApiAs('datasetQualityLogsUser', `${type}-${dataset}-${namespace}`);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
expect(isNaN(resp.body.sizeBytes as number)).to.be(false);
|
||||
expect(resp.body.sizeBytes).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('returns service.name and host.name correctly', async () => {
|
||||
const resp = await callApiAs('datasetQualityLogsUser', `${type}-${dataset}-${namespace}`);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
expect(resp.body.services).to.eql({ ['service.name']: [serviceName] });
|
||||
expect(resp.body.hosts?.['host.name']).to.eql([hostName]);
|
||||
});
|
||||
|
|
|
@ -97,7 +97,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
it('returns error when dataStream param is not provided', async () => {
|
||||
const expectedMessage = 'Data Stream name cannot be empty';
|
||||
const err = await expectToReject<DatasetQualityApiError>(() =>
|
||||
callApiAs('datasetQualityLogsUser', encodeURIComponent(' '))
|
||||
callApiAs('datasetQualityMonitorUser', encodeURIComponent(' '))
|
||||
);
|
||||
expect(err.res.status).to.be(400);
|
||||
expect(err.res.body.message.indexOf(expectedMessage)).to.greaterThan(-1);
|
||||
|
@ -106,7 +106,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
it('returns only privileges if matching data stream is not available', async () => {
|
||||
const nonExistentDataSet = 'Non-existent';
|
||||
const nonExistentDataStream = `${type}-${nonExistentDataSet}-${namespace}`;
|
||||
const resp = await callApiAs('datasetQualityLogsUser', nonExistentDataStream);
|
||||
const resp = await callApiAs('datasetQualityMonitorUser', nonExistentDataStream);
|
||||
expect(resp.body).eql(defaultDataStreamPrivileges);
|
||||
});
|
||||
|
||||
|
@ -115,7 +115,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
esClient,
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
const resp = await callApiAs('datasetQualityLogsUser', `${type}-${dataset}-${namespace}`);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
expect(resp.body.createdOn).to.be(Number(dataStreamSettings?.index?.creation_date));
|
||||
});
|
||||
|
||||
|
@ -125,7 +128,10 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
esClient,
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
const resp = await callApiAs('datasetQualityLogsUser', `${type}-${dataset}-${namespace}`);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
expect(resp.body.createdOn).to.be(Number(dataStreamSettings?.index?.creation_date));
|
||||
});
|
||||
|
||||
|
@ -135,7 +141,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
`${type}-${integrationDataset}-${namespace}`
|
||||
);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityLogsUser',
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${integrationDataset}-${namespace}`
|
||||
);
|
||||
expect(resp.body.createdOn).to.be(Number(dataStreamSettings?.index?.creation_date));
|
||||
|
|
|
@ -74,7 +74,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns stats correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
expect(stats.body.degradedDocs.length).to.be(2);
|
||||
|
||||
const degradedDocsStats = stats.body.degradedDocs.reduce(
|
||||
|
@ -105,7 +105,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
describe('and there are not log documents', () => {
|
||||
it('returns stats correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
|
||||
expect(stats.body.degradedDocs.length).to.be(0);
|
||||
});
|
||||
|
@ -154,7 +154,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns counts and list of datasets correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
expect(stats.body.degradedDocs.length).to.be(18);
|
||||
|
||||
const expected = {
|
||||
|
|
|
@ -79,7 +79,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns no values when provided field has no degraded values', async () => {
|
||||
const resp = await callApiAs({
|
||||
user: 'datasetQualityLogsUser',
|
||||
user: 'datasetQualityMonitorUser',
|
||||
dataStream: degradedFieldsDatastream,
|
||||
degradedField: regularFieldName,
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns values when provided field has degraded values', async () => {
|
||||
const resp = await callApiAs({
|
||||
user: 'datasetQualityLogsUser',
|
||||
user: 'datasetQualityMonitorUser',
|
||||
dataStream: degradedFieldsDatastream,
|
||||
degradedField: degradedFieldName,
|
||||
});
|
||||
|
|
|
@ -87,14 +87,17 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns no results when dataStream does not have any degraded fields', async () => {
|
||||
const resp = await callApiAs('datasetQualityLogsUser', `${type}-${dataset}-${namespace}`);
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${dataset}-${namespace}`
|
||||
);
|
||||
expect(resp.body.degradedFields.length).to.be(0);
|
||||
});
|
||||
|
||||
it('returns results when dataStream do have degraded fields', async () => {
|
||||
const expectedDegradedFields = ['log.level', 'trace.id'];
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityLogsUser',
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${degradedFieldDataset}-${namespace}`
|
||||
);
|
||||
const degradedFields = resp.body.degradedFields.map((field: DegradedField) => field.name);
|
||||
|
@ -117,7 +120,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
];
|
||||
|
||||
const resp = await callApiAs(
|
||||
'datasetQualityLogsUser',
|
||||
'datasetQualityMonitorUser',
|
||||
`${type}-${degradedFieldDataset}-${namespace}`
|
||||
);
|
||||
|
||||
|
|
|
@ -5,24 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { log, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { log, syntheticsMonitor, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import expect from '@kbn/expect';
|
||||
import rison from '@kbn/rison';
|
||||
import { DatasetQualityApiClientKey } from '../../common/config';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { cleanLogIndexTemplate, addIntegrationToLogIndexTemplate } from './es_utils';
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const synthtrace = getService('logSynthtraceEsClient');
|
||||
const logsSynthtrace = getService('logSynthtraceEsClient');
|
||||
const syntheticsSynthrace = getService('syntheticsSynthtraceEsClient');
|
||||
const datasetQualityApiClient = getService('datasetQualityApiClient');
|
||||
const es = getService('es');
|
||||
|
||||
async function callApiAs(user: DatasetQualityApiClientKey) {
|
||||
async function callApiAs(
|
||||
user: DatasetQualityApiClientKey,
|
||||
types: Array<'logs' | 'metrics' | 'traces' | 'synthetics'> = ['logs']
|
||||
) {
|
||||
return await datasetQualityApiClient[user]({
|
||||
endpoint: 'GET /internal/dataset_quality/data_streams/stats',
|
||||
params: {
|
||||
query: {
|
||||
types: ['logs'],
|
||||
types: rison.encodeArray(types),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -35,7 +40,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
rate = 1,
|
||||
dataset = 'synth.1',
|
||||
}: { from?: string; to?: string; interval?: string; rate?: number; dataset?: string } = {}) {
|
||||
await synthtrace.index([
|
||||
await logsSynthtrace.index([
|
||||
timerange(from, to)
|
||||
.interval(interval)
|
||||
.rate(rate)
|
||||
|
@ -84,18 +89,18 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns non empty stats for an authorized user', async () => {
|
||||
await ingestDocuments();
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
|
||||
expect(stats.body.dataStreamsStats[0].size).not.empty();
|
||||
expect(stats.body.dataStreamsStats[0].sizeBytes).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[0].lastActivity).greaterThan(0);
|
||||
});
|
||||
|
||||
it('get list of privileged data streams for datasetQualityLogsUser', async () => {
|
||||
it('get list of privileged data streams for datasetQualityMonitorUser', async () => {
|
||||
// Index only one document to logs-test-1-default and logs-test-1-default data stream using synthtrace
|
||||
await ingestDocuments({ dataset: 'test.1' });
|
||||
await ingestDocuments({ dataset: 'test.2' });
|
||||
const resp = await callApiAs('datasetQualityLogsUser');
|
||||
const resp = await callApiAs('datasetQualityMonitorUser');
|
||||
|
||||
expect(resp.body.datasetUserPrivileges.canMonitor).to.be(true);
|
||||
expect(
|
||||
|
@ -112,7 +117,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await logsSynthtrace.clean();
|
||||
await cleanLogIndexTemplate({ esClient: es });
|
||||
});
|
||||
});
|
||||
|
@ -124,7 +129,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
before(async () => {
|
||||
await addIntegrationToLogIndexTemplate({ esClient: es, name: integration });
|
||||
|
||||
await synthtrace.index([
|
||||
await logsSynthtrace.index([
|
||||
timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z')
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
|
@ -137,7 +142,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns stats correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
|
||||
expect(stats.body.dataStreamsStats.length).to.be(1);
|
||||
expect(stats.body.dataStreamsStats[0].integration).to.be(integration);
|
||||
|
@ -148,14 +153,14 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await logsSynthtrace.clean();
|
||||
await cleanLogIndexTemplate({ esClient: es });
|
||||
});
|
||||
});
|
||||
|
||||
describe('and uncategorized datastreams', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index([
|
||||
await logsSynthtrace.index([
|
||||
timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z')
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
|
@ -168,7 +173,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns stats correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityLogsUser');
|
||||
const stats = await callApiAs('datasetQualityMonitorUser');
|
||||
|
||||
expect(stats.body.dataStreamsStats.length).to.be(1);
|
||||
expect(stats.body.dataStreamsStats[0].integration).not.ok();
|
||||
|
@ -179,7 +184,53 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await logsSynthtrace.clean();
|
||||
});
|
||||
});
|
||||
|
||||
describe('and multiple dataStream types are requested', () => {
|
||||
before(async () => {
|
||||
await logsSynthtrace.index([
|
||||
timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z')
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => [
|
||||
log.create().message('This is a log message').timestamp(timestamp).defaults({
|
||||
'log.file.path': '/my-service.log',
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
await syntheticsSynthrace.index([
|
||||
timerange('2023-11-20T15:00:00.000Z', '2023-11-20T15:01:00.000Z')
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => [
|
||||
syntheticsMonitor.create().dataset('http').timestamp(timestamp),
|
||||
]),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns stats correctly', async () => {
|
||||
const stats = await callApiAs('datasetQualityMonitorUser', ['logs', 'synthetics']);
|
||||
|
||||
expect(stats.body.dataStreamsStats.length).to.be(2);
|
||||
expect(stats.body.dataStreamsStats[0].size).not.empty();
|
||||
expect(stats.body.dataStreamsStats[0].sizeBytes).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[0].lastActivity).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[0].totalDocs).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[0].name).match(new RegExp(/^logs-[\w.]+-[\w.]+/));
|
||||
expect(stats.body.dataStreamsStats[1].size).not.empty();
|
||||
expect(stats.body.dataStreamsStats[1].sizeBytes).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[1].lastActivity).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[1].totalDocs).greaterThan(0);
|
||||
expect(stats.body.dataStreamsStats[1].name).match(
|
||||
new RegExp(/^synthetics-[\w.]+-[\w.]+/)
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await logsSynthtrace.clean();
|
||||
await syntheticsSynthrace.clean();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
const integrationPackages = ['nginx', 'apm'];
|
||||
|
||||
async function callApiAs(integration: string) {
|
||||
const user = 'datasetQualityLogsUser' as DatasetQualityApiClientKey;
|
||||
const user = 'datasetQualityMonitorUser' as DatasetQualityApiClientKey;
|
||||
return await datasetQualityApiClient[user]({
|
||||
endpoint: 'GET /internal/dataset_quality/integrations/{integration}/dashboards',
|
||||
params: {
|
||||
|
|
|
@ -35,7 +35,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
];
|
||||
|
||||
async function callApiAs() {
|
||||
const user = 'datasetQualityLogsUser' as DatasetQualityApiClientKey;
|
||||
const user = 'datasetQualityMonitorUser' as DatasetQualityApiClientKey;
|
||||
|
||||
return await datasetQualityApiClient[user]({
|
||||
endpoint: 'GET /internal/dataset_quality/integrations',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue