[UsageCollection] Allow pass_through on Record<string, unknown> types (#144571) (#144587)

(cherry picked from commit f43da91786)

Co-authored-by: Alejandro Fernández Haro <alejandro.haro@elastic.co>
This commit is contained in:
Kibana Machine 2022-11-04 05:35:38 -04:00 committed by GitHub
parent 76c87beb0b
commit 6b28de8109
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 17 deletions

View file

@ -50,7 +50,11 @@
}
}
},
"my_str_array": { "type": "array", "items": { "type": "keyword" } }
"my_str_array": { "type": "array", "items": { "type": "keyword" } },
"my_pass_through": {
"type": "pass_through",
"_meta": { "description": "Don't know what goes here. Simply passing it through."}
}
}
}
}

View file

@ -56,6 +56,10 @@ export const parsedCollectorWithDescription: ParsedUsageCollection = [
},
},
my_str_array: { type: 'array', items: { type: 'keyword' } },
my_pass_through: {
type: 'pass_through',
_meta: { description: "Don't know what goes here. Simply passing it through." },
},
},
},
fetch: {
@ -103,6 +107,10 @@ export const parsedCollectorWithDescription: ParsedUsageCollection = [
type: 'StringKeyword',
},
},
my_pass_through: {
kind: SyntaxKind.UnknownKeyword,
type: 'UnknownKeyword',
},
},
},
},

View file

@ -10,7 +10,13 @@ import { ParsedUsageCollection } from './ts_parser';
export type AllowedSchemaNumberTypes = 'long' | 'integer' | 'short' | 'byte' | 'double' | 'float';
export type AllowedSchemaTypes = AllowedSchemaNumberTypes | 'keyword' | 'text' | 'boolean' | 'date';
export type AllowedSchemaTypes =
| AllowedSchemaNumberTypes
| 'keyword'
| 'text'
| 'boolean'
| 'date'
| 'pass_through';
export function compatibleSchemaTypes(type: AllowedSchemaTypes | 'array') {
switch (type) {
@ -29,6 +35,8 @@ export function compatibleSchemaTypes(type: AllowedSchemaTypes | 'array') {
return 'number';
case 'array':
return 'array';
case 'pass_through':
return 'unknown';
default:
throw new Error(`Unknown schema type ${type}`);
}

View file

@ -167,4 +167,13 @@ describe('getDescriptor', () => {
prop5: { kind: ts.SyntaxKind.FirstLiteralToken, type: 'FirstLiteralToken' },
});
});
it('serializes RecordStringUnknown', () => {
const usageInterface = usageInterfaces.get('RecordStringUnknown')!;
const descriptor = getDescriptor(usageInterface, tsProgram);
expect(descriptor).toEqual({
kind: ts.SyntaxKind.UnknownKeyword,
type: 'UnknownKeyword',
});
});
});

View file

@ -62,6 +62,8 @@ export function kindToDescriptorName(kind: number) {
case ts.SyntaxKind.NumberKeyword:
case ts.SyntaxKind.NumericLiteral:
return 'number';
case ts.SyntaxKind.UnknownKeyword:
return 'unknown';
default:
throw new Error(`Unknown kind ${kind}`);
}
@ -219,6 +221,15 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor |
// Support `Record<string, SOMETHING>`
if (symbolName === 'Record') {
// Special use case `Record<string, unknown>`
if (
node.typeArguments![0].kind === ts.SyntaxKind.StringKeyword &&
node.typeArguments![1].kind === ts.SyntaxKind.UnknownKeyword
) {
const kind = node.typeArguments![1].kind;
return { kind, type: ts.SyntaxKind[kind] as keyof typeof ts.SyntaxKind };
}
const descriptor = getDescriptor(node.typeArguments![1], program);
if (node.typeArguments![0].kind === ts.SyntaxKind.StringKeyword) {
return { '@@INDEX@@': descriptor };

View file

@ -63,6 +63,7 @@ export interface MappedTypes {
export type RecordWithKnownProps = Record<MappedTypeProps, number>;
export type RecordWithKnownAllProps = Record<MappedTypeAllProps, number>;
export type RecordStringUnknown = Record<string, unknown>;
export type IndexedAccessType = Pick<WithUnion, 'prop1' | 'prop2'>;
export type OmitIndexedAccessType = Omit<WithUnion, 'prop1' | 'prop2'>;

View file

@ -24,6 +24,7 @@ interface Usage {
my_index_signature_prop?: {
[key: string]: number;
};
my_pass_through?: Record<string, unknown>;
}
const SOME_NUMBER: number = 123;
@ -52,6 +53,9 @@ export const myCollector = makeUsageCollector<Usage>({
},
],
my_str_array: ['hello', 'world'],
my_pass_through: {
some: 'key',
},
};
} catch (err) {
return {
@ -94,5 +98,9 @@ export const myCollector = makeUsageCollector<Usage>({
max: { type: 'long' },
min: { type: 'long' },
},
my_pass_through: {
type: 'pass_through',
_meta: { description: "Don't know what goes here. Simply passing it through." },
},
},
});

View file

@ -8,7 +8,7 @@
import type { ElasticsearchClient, SavedObjectsClientContract, Logger } from '@kbn/core/server';
import type { PossibleSchemaTypes } from '@kbn/analytics-client';
import type { PossibleSchemaTypes, SchemaMetaOptional } from '@kbn/analytics-client';
export type {
AllowedSchemaTypes,
@ -22,7 +22,17 @@ export type {
* Helper to find out whether to keep recursively looking or if we are on an end value
*/
export type RecursiveMakeSchemaFrom<U> = U extends object
? MakeSchemaFrom<U>
? Record<string, unknown> extends U
?
| {
// pass_through should only be allowed for Record<string, unknown> for now
type: 'pass_through';
_meta: {
description: string; // Intentionally enforcing the descriptions here
} & SchemaMetaOptional<U>;
}
| MakeSchemaFrom<U> // But still allow being explicit in the definition if they want to.
: MakeSchemaFrom<U>
: { type: PossibleSchemaTypes<U>; _meta?: { description: string } };
/**
@ -60,7 +70,7 @@ export interface CollectorFetchContext {
/**
* The fetch method has the context of the Collector itself
* (this has access to all the properties of the collector like the logger)
* and the the first parameter is {@link CollectorFetchContext}.
* and the first parameter is {@link CollectorFetchContext}.
*/
export type CollectorFetchMethod<TReturn, ExtraOptions extends object = {}> = (
this: ICollector<TReturn> & ExtraOptions, // Specify the context of `this` for this.log and others to become available

View file

@ -10,7 +10,7 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
export interface Usage {
initialized: boolean;
flags: Record<string, string>;
flags: Record<string, unknown>;
flagNames: string[];
}
@ -37,10 +37,8 @@ export function registerUsageCollector(
},
// We'll likely map "flags" as `flattened`, so "flagNames" helps out to discover the key names
flags: {
DYNAMIC_KEY: {
type: 'keyword',
_meta: { description: 'Flags received by the client' },
},
type: 'pass_through',
_meta: { description: 'Flags received by the client' },
},
flagNames: {
type: 'array',

View file

@ -4979,13 +4979,9 @@
}
},
"flags": {
"properties": {
"DYNAMIC_KEY": {
"type": "keyword",
"_meta": {
"description": "Flags received by the client"
}
}
"type": "pass_through",
"_meta": {
"description": "Flags received by the client"
}
},
"flagNames": {