-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Streams] Dashboard linking #204309
[Streams] Dashboard linking #204309
Changes from 146 commits
5fe1078
6f940eb
9b33808
c8cd0f7
7e8f2d7
999dfef
d99a3ff
0e89345
9dd5fde
7400a3b
5140687
797c93d
fcce628
9d37219
d622fa1
fbc6c2a
45787d0
e810f53
e93ac17
cf9f349
0d676ed
7b0a4e5
34f3c44
0b7de24
76ed81d
2427d2e
2ad08f4
c8d99ed
8c707d1
e6bb640
421ea5b
7992539
95ae9f8
7a88cc5
74f0678
dab77c1
a60a165
fc031f8
e9189e0
08aef83
2bef512
5c10198
5d40445
9ca2da2
8b0b51c
d655c0a
8e0eba8
eb61e23
5e90008
dd55c3a
b9896ab
377b365
b556194
aa4a2e0
f0276dc
adcd660
224ffeb
2edc694
bb4dc6e
2b74f6c
46ffca7
bd6f225
1ec3a20
922d1b4
d6ee23e
ad2cc5e
26f5554
8dc61f3
1fde5b4
3ffefbb
ab533ba
f032b51
ac426f5
97510ca
a950210
84e233e
d2ba657
1ec3ef9
c219042
1250723
9a487e4
7452c8b
5245055
37be7f5
90389fc
0d8e0eb
76d2042
e12aaec
1baac8f
3947668
244e1d8
ef89662
8ed5d23
1ce43ab
7065f3d
ddaabc1
a47ed43
d57be23
003c3e6
830fc5d
95440e2
c46d94c
cebd9d0
44d5b24
9d297e7
3967908
f1b5d0d
c66ed6e
1fc2da9
de087c0
2d76780
17b955d
a819748
1abbf61
4443da5
e5e2cc0
e9c9813
d20fff0
7452b90
092881e
ca13422
1a63b35
1d2c29c
32073ff
8faf3f2
c146287
76cb698
806d1fd
7cf986b
18d7788
746a068
0df97c1
82d94ca
ec11424
51beb26
f9b8345
90254e3
92487ba
0613385
7f2a70c
119c17c
1666de7
0dfc7a1
287528f
57699d9
7680759
1266ea0
f0739fb
068e60f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,15 +63,11 @@ export function isStream(subject: any): subject is StreamDefinition { | |
return isSchema(streamDefintionSchema, subject); | ||
} | ||
|
||
export function isIngestStream( | ||
subject: IngestStreamDefinition | WiredStreamDefinition | ||
): subject is IngestStreamDefinition { | ||
export function isIngestStream(subject: StreamDefinition): subject is IngestStreamDefinition { | ||
return isSchema(ingestStreamDefinitonSchema, subject); | ||
} | ||
|
||
export function isWiredStream( | ||
subject: IngestStreamDefinition | WiredStreamDefinition | ||
): subject is WiredStreamDefinition { | ||
export function isWiredStream(subject: StreamDefinition): subject is WiredStreamDefinition { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a simplification |
||
return isSchema(wiredStreamDefinitonSchema, subject); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,11 @@ | |
|
||
import { schema } from '@kbn/config-schema'; | ||
|
||
const savedObjectReferenceSchema = schema.object({ | ||
type: schema.string(), | ||
id: schema.string(), | ||
}); | ||
|
||
export const findRulesOptionsSchema = schema.object( | ||
{ | ||
perPage: schema.maybe(schema.number()), | ||
|
@@ -19,10 +24,7 @@ export const findRulesOptionsSchema = schema.object( | |
sortField: schema.maybe(schema.string()), | ||
sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), | ||
hasReference: schema.maybe( | ||
schema.object({ | ||
type: schema.string(), | ||
id: schema.string(), | ||
}) | ||
schema.oneOf([savedObjectReferenceSchema, schema.arrayOf(savedObjectReferenceSchema)]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is what the SO api allows, and what we need to find rules with one of n tags |
||
), | ||
fields: schema.maybe(schema.arrayOf(schema.string())), | ||
filter: schema.maybe( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Storage adapter | ||
|
||
Storage adapters are an abstraction for managing & writing data into Elasticsearch, from Kibana plugins. | ||
|
||
There are several ways one can use Elasticsearch in Kibana, for instance: | ||
|
||
- a simple id-based CRUD table | ||
- timeseries data with regular indices | ||
- timeseries data with data streams | ||
|
||
But then there are many choices to be made that make this a very complex problem: | ||
|
||
- Elasticsearch asset managmeent | ||
- Authentication | ||
- Schema changes | ||
- Kibana's distributed nature | ||
- Stateful versus serverless | ||
|
||
The intent of storage adapters is to come up with an abstraction that allows Kibana developers to have a common interface for writing to and reading data from Elasticsearch. For instance, for setting up your data store, it should not matter how you authenticate (internal user? current user? API keys?). | ||
|
||
## Saved objects | ||
|
||
Some of these problems are solved by Saved Objects. But Saved Objects come with a lot of baggage - Kibana RBAC, relationships, spaces, all of which might not be | ||
needed for your use case but are still restrictive. One could consider Saved Objects to be the target of an adapter, but Storage Adapters aim to address a wider set of use-cases. | ||
|
||
## Philosophy | ||
|
||
Storage adapters should largely adhere to the following principles: | ||
|
||
- Interfaces are as close to Elasticsearch as possible. Meaning, the `search` method is practically a pass-through for `_search`. | ||
- Strongly-typed. TypeScript types are inferred from the schema. This makes it easy to create fully-typed clients for any storage. | ||
- Lazy writes. No Elasticsearch assets (templates, indices, aliases) get installed unless necessary. Anything that gets persisted to Elasticsearch raises questions (in SDHs, UIs, APIs) and should be avoided when possible. This also helps avoidable upgrade issues (e.g. conflicting mappings for something that never got used). | ||
- Recoverable. If somehow Elasticsearch assets get borked, the adapters should make a best-effort attempt to recover, or log warnings with clear remediation steps. | ||
|
||
## Future goals | ||
|
||
Currently, we only have the StorageIndexAdapter which writes to plain indices. In the future, we'll want more: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you include a code example for how to consume There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done! |
||
|
||
- A StorageDataStreamAdapter or StorageSavedObjectAdapter | ||
- Federated search | ||
- Data/Index Lifecycle Management | ||
- Migration scripts | ||
- Runtime mappings for older versions | ||
|
||
## Usage | ||
|
||
### Storage index adapter | ||
|
||
To use the storage index adapter, instantiate it with an authenticated Elasticsearch client: | ||
|
||
```ts | ||
const storageSettings = { | ||
name: '.kibana_streams_assets', | ||
schema: { | ||
properties: { | ||
[ASSET_ASSET_ID]: types.keyword({ required: true }), | ||
[ASSET_TYPE]: types.enum(Object.values(ASSET_TYPES), { required: true }), | ||
}, | ||
}, | ||
} satisfies IndexStorageSettings; | ||
|
||
// create and configure the adapter | ||
const adapter = new StorageIndexAdapter( | ||
esClient: coreStart.elasticsearch.client.asInternalUser, | ||
this.logger.get('assets'), | ||
storageSettings | ||
); | ||
|
||
// get the client (which is shared across all adapters) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What does this mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its interface, will update the comment |
||
const client = adapter.getClient(); | ||
|
||
const response = await client.search('operation_name', { | ||
track_total_hits: true | ||
}); | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import stringify from 'json-stable-stringify'; | ||
import objectHash from 'object-hash'; | ||
import { IndexStorageSettings } from '.'; | ||
|
||
export function getSchemaVersion(storage: IndexStorageSettings): string { | ||
const version = objectHash(stringify(storage.schema.properties)); | ||
return version; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* 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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import type { | ||
BulkOperationContainer, | ||
BulkRequest, | ||
BulkResponse, | ||
DeleteRequest, | ||
DeleteResponse, | ||
IndexRequest, | ||
IndexResponse, | ||
SearchRequest, | ||
} from '@elastic/elasticsearch/lib/api/types'; | ||
import { InferSearchResponseOf } from '@kbn/es-types'; | ||
import { StorageFieldTypeOf, StorageMappingProperty } from './types'; | ||
|
||
interface StorageSchemaProperties { | ||
[x: string]: StorageMappingProperty; | ||
} | ||
|
||
export interface StorageSchema { | ||
properties: StorageSchemaProperties; | ||
} | ||
|
||
interface StorageSettingsBase { | ||
schema: StorageSchema; | ||
} | ||
|
||
export interface IndexStorageSettings extends StorageSettingsBase { | ||
name: string; | ||
} | ||
|
||
export type StorageSettings = IndexStorageSettings; | ||
|
||
export type StorageAdapterSearchRequest = Omit<SearchRequest, 'index'>; | ||
export type StorageAdapterSearchResponse< | ||
TDocument, | ||
TSearchRequest extends Omit<SearchRequest, 'index'> | ||
> = InferSearchResponseOf<TDocument, TSearchRequest>; | ||
|
||
export type StorageAdapterBulkOperation = Pick<BulkOperationContainer, 'delete' | 'index'>; | ||
|
||
export type StorageAdapterBulkRequest<TDocument extends Record<string, any>> = Omit< | ||
BulkRequest, | ||
'operations' | 'index' | ||
> & { | ||
operations: Array<StorageAdapterBulkOperation | TDocument>; | ||
}; | ||
export type StorageAdapterBulkResponse = BulkResponse; | ||
|
||
export type StorageAdapterDeleteRequest = DeleteRequest; | ||
export type StorageAdapterDeleteResponse = DeleteResponse; | ||
|
||
export type StorageAdapterIndexRequest<TDocument = unknown> = Omit< | ||
IndexRequest<TDocument>, | ||
'index' | ||
>; | ||
export type StorageAdapterIndexResponse = IndexResponse; | ||
|
||
export interface IStorageAdapter<TStorageSettings extends StorageSettings = never> { | ||
bulk<TDocument extends Record<string, any>>( | ||
request: StorageAdapterBulkRequest<TDocument> | ||
): Promise<StorageAdapterBulkResponse>; | ||
search<TDocument, TSearchRequest extends Omit<SearchRequest, 'index'>>( | ||
request: StorageAdapterSearchRequest | ||
): Promise<StorageAdapterSearchResponse<TDocument, TSearchRequest>>; | ||
index<TDocument>( | ||
request: StorageAdapterIndexRequest<TDocument> | ||
): Promise<StorageAdapterIndexResponse>; | ||
delete(request: StorageAdapterDeleteRequest): Promise<StorageAdapterDeleteResponse>; | ||
} | ||
|
||
export type StorageSettingsOf<TStorageAdapter extends IStorageAdapter<StorageSettings>> = | ||
TStorageAdapter extends IStorageAdapter<infer TStorageSettings> | ||
? TStorageSettings extends StorageSettings | ||
? TStorageSettings | ||
: never | ||
: never; | ||
|
||
export type StorageDocumentOf<TStorageSettings extends StorageSettings> = { | ||
[TKey in keyof TStorageSettings['schema']['properties']]: StorageFieldTypeOf< | ||
TStorageSettings['schema']['properties'][TKey] | ||
>; | ||
} & { _id: string }; | ||
|
||
export { StorageIndexAdapter } from './index_adapter'; | ||
export { StorageClient } from './storage_client'; | ||
export { types } from './types'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure we can refine errors without falling back to
never