Skip to content

Commit 5da99bb

Browse files
authored
[APM] Improve router types (#83620) (#83699)
* [APM] Improve router types * Pass processorEvent param to useDynamicIndexPattern
1 parent 71efb4e commit 5da99bb

File tree

84 files changed

+1044
-623
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1044
-623
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import * as t from 'io-ts';
7+
import { isLeft } from 'fp-ts/lib/Either';
8+
import { merge } from './';
9+
import { jsonRt } from '../json_rt';
10+
11+
describe('merge', () => {
12+
it('fails on one or more errors', () => {
13+
const type = merge([t.type({ foo: t.string }), t.type({ bar: t.number })]);
14+
15+
const result = type.decode({ foo: '' });
16+
17+
expect(isLeft(result)).toBe(true);
18+
});
19+
20+
it('merges left to right', () => {
21+
const typeBoolean = merge([
22+
t.type({ foo: t.string }),
23+
t.type({ foo: jsonRt.pipe(t.boolean) }),
24+
]);
25+
26+
const resultBoolean = typeBoolean.decode({
27+
foo: 'true',
28+
});
29+
30+
// @ts-expect-error
31+
expect(resultBoolean.right).toEqual({
32+
foo: true,
33+
});
34+
35+
const typeString = merge([
36+
t.type({ foo: jsonRt.pipe(t.boolean) }),
37+
t.type({ foo: t.string }),
38+
]);
39+
40+
const resultString = typeString.decode({
41+
foo: 'true',
42+
});
43+
44+
// @ts-expect-error
45+
expect(resultString.right).toEqual({
46+
foo: 'true',
47+
});
48+
});
49+
50+
it('deeply merges values', () => {
51+
const type = merge([
52+
t.type({ foo: t.type({ baz: t.string }) }),
53+
t.type({ foo: t.type({ bar: t.string }) }),
54+
]);
55+
56+
const result = type.decode({
57+
foo: {
58+
bar: '',
59+
baz: '',
60+
},
61+
});
62+
63+
// @ts-expect-error
64+
expect(result.right).toEqual({
65+
foo: {
66+
bar: '',
67+
baz: '',
68+
},
69+
});
70+
});
71+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import * as t from 'io-ts';
7+
import { merge as lodashMerge } from 'lodash';
8+
import { isLeft } from 'fp-ts/lib/Either';
9+
import { ValuesType } from 'utility-types';
10+
11+
export type MergeType<
12+
T extends t.Any[],
13+
U extends ValuesType<T> = ValuesType<T>
14+
> = t.Type<U['_A'], U['_O'], U['_I']> & {
15+
_tag: 'MergeType';
16+
types: T;
17+
};
18+
19+
// this is similar to t.intersection, but does a deep merge
20+
// instead of a shallow merge
21+
22+
export function merge<A extends t.Mixed, B extends t.Mixed>(
23+
types: [A, B]
24+
): MergeType<[A, B]>;
25+
26+
export function merge(types: t.Any[]) {
27+
const mergeType = new t.Type(
28+
'merge',
29+
(u): u is unknown => {
30+
return types.every((type) => type.is(u));
31+
},
32+
(input, context) => {
33+
const errors: t.Errors = [];
34+
35+
const successes: unknown[] = [];
36+
37+
const results = types.map((type, index) =>
38+
type.validate(
39+
input,
40+
context.concat({
41+
key: String(index),
42+
type,
43+
actual: input,
44+
})
45+
)
46+
);
47+
48+
results.forEach((result) => {
49+
if (isLeft(result)) {
50+
errors.push(...result.left);
51+
} else {
52+
successes.push(result.right);
53+
}
54+
});
55+
56+
const mergedValues = lodashMerge({}, ...successes);
57+
58+
return errors.length > 0 ? t.failures(errors) : t.success(mergedValues);
59+
},
60+
(a) => types.reduce((val, type) => type.encode(val), a)
61+
);
62+
63+
return {
64+
...mergeType,
65+
_tag: 'MergeType',
66+
types,
67+
};
68+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import * as t from 'io-ts';
7+
import { isRight, isLeft } from 'fp-ts/lib/Either';
8+
import { strictKeysRt } from './';
9+
import { jsonRt } from '../json_rt';
10+
11+
describe('strictKeysRt', () => {
12+
it('correctly and deeply validates object keys', () => {
13+
const checks: Array<{ type: t.Type<any>; passes: any[]; fails: any[] }> = [
14+
{
15+
type: t.intersection([
16+
t.type({ foo: t.string }),
17+
t.partial({ bar: t.string }),
18+
]),
19+
passes: [{ foo: '' }, { foo: '', bar: '' }],
20+
fails: [
21+
{ foo: '', unknownKey: '' },
22+
{ foo: '', bar: '', unknownKey: '' },
23+
],
24+
},
25+
{
26+
type: t.type({
27+
path: t.union([
28+
t.type({ serviceName: t.string }),
29+
t.type({ transactionType: t.string }),
30+
]),
31+
}),
32+
passes: [
33+
{ path: { serviceName: '' } },
34+
{ path: { transactionType: '' } },
35+
],
36+
fails: [
37+
{ path: { serviceName: '', unknownKey: '' } },
38+
{ path: { transactionType: '', unknownKey: '' } },
39+
{ path: { serviceName: '', transactionType: '' } },
40+
{ path: { serviceName: '' }, unknownKey: '' },
41+
],
42+
},
43+
{
44+
type: t.intersection([
45+
t.type({ query: t.type({ bar: t.string }) }),
46+
t.partial({ query: t.partial({ _debug: t.boolean }) }),
47+
]),
48+
passes: [{ query: { bar: '', _debug: true } }],
49+
fails: [{ query: { _debug: true } }],
50+
},
51+
];
52+
53+
checks.forEach((check) => {
54+
const { type, passes, fails } = check;
55+
56+
const strictType = strictKeysRt(type);
57+
58+
passes.forEach((value) => {
59+
const result = strictType.decode(value);
60+
61+
if (!isRight(result)) {
62+
throw new Error(
63+
`Expected ${JSON.stringify(
64+
value
65+
)} to be allowed, but validation failed with ${
66+
result.left[0].message
67+
}`
68+
);
69+
}
70+
});
71+
72+
fails.forEach((value) => {
73+
const result = strictType.decode(value);
74+
75+
if (!isLeft(result)) {
76+
throw new Error(
77+
`Expected ${JSON.stringify(
78+
value
79+
)} to be disallowed, but validation succeeded`
80+
);
81+
}
82+
});
83+
});
84+
});
85+
86+
it('does not support piped types', () => {
87+
const typeA = t.type({
88+
query: t.type({ filterNames: jsonRt.pipe(t.array(t.string)) }),
89+
} as Record<string, any>);
90+
91+
const typeB = t.partial({
92+
query: t.partial({ _debug: jsonRt.pipe(t.boolean) }),
93+
});
94+
95+
const value = {
96+
query: {
97+
_debug: 'true',
98+
filterNames: JSON.stringify(['host', 'agentName']),
99+
},
100+
};
101+
102+
const pipedType = strictKeysRt(typeA.pipe(typeB));
103+
104+
expect(isLeft(pipedType.decode(value))).toBe(true);
105+
});
106+
});

0 commit comments

Comments
 (0)