diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 71190c26c4894..ccea77d906970 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -451,8 +451,7 @@ The plugin exposes the static DefaultEditorController class to consume. |{kib-repo}blob/{branch}/x-pack/plugins/asset_manager/README.md[assetManager] -|This plugin provides access to the asset data stored in assets-* indices, primarily -for inventory and topology purposes. +|This plugin provides access to observed asset data, such as information about hosts, pods, containers, services, and more. |{kib-repo}blob/{branch}/x-pack/plugins/banners/README.md[banners] diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ec34d257eadc2..9b12708a506fc 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -4,6 +4,7 @@ pageLoadAssetSize: aiops: 10000 alerting: 106936 apm: 64385 + assetManager: 25000 banners: 17946 bfetch: 22837 canvas: 1066647 diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 94af20ff4f86b..ca639ed3272fd 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -197,6 +197,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.apm.featureFlags.sourcemapApiAvailable (any)', 'xpack.apm.featureFlags.storageExplorerAvailable (any)', 'xpack.apm.serverless.enabled (any)', // It's a boolean (any because schema.conditional) + 'xpack.assetManager.alphaEnabled (boolean)', 'xpack.observability_onboarding.serverless.enabled (any)', // It's a boolean (any because schema.conditional) 'xpack.cases.files.allowedMimeTypes (array)', 'xpack.cases.files.maxSize (number)', diff --git a/x-pack/plugins/asset_manager/README.md b/x-pack/plugins/asset_manager/README.md index f82f174af471c..d73bfbb53b087 100644 --- a/x-pack/plugins/asset_manager/README.md +++ b/x-pack/plugins/asset_manager/README.md @@ -1,39 +1,13 @@ # Asset Manager Plugin -This plugin provides access to the asset data stored in assets-\* indices, primarily -for inventory and topology purposes. +This plugin provides access to observed asset data, such as information about hosts, pods, containers, services, and more. ## Documentation -See [docs for the provided APIs in the docs folder](./docs/index.md). +### User Docs -## Running Tests +For those interested in making use of the APIs provided by this plugin, see [our API docs](./docs/api.md). -There are integration tests for the endpoints implemented thus far as well as for -the sample data tests. There is also a small set of tests meant to ensure that the -plugin is not doing anything without the proper config value in place to enable -the plugin fully. For more on enabling the plugin, see [the docs page](./docs/index.md). +### Developer Docs -The "not enabled" tests are run by default in CI. To run them manually, do the following: - -```shell -$ node scripts/functional_tests_server --config x-pack/test/api_integration/apis/asset_manager/config_when_disabled.ts -$ node scripts/functional_test_runner --config=x-pack/test/api_integration/apis/asset_manager/config_when_disabled.ts -``` - -The "enabled" tests are NOT run by CI yet, to prevent blocking Kibana development for a -test failure in this alpha, tech preview plugin. They will be moved into the right place -to make them run for CI before the plugin is enabled by default. To run them manually: - -```shell -$ node scripts/functional_tests_server --config x-pack/test/api_integration/apis/asset_manager/config.ts -$ node scripts/functional_test_runner --config=x-pack/test/api_integration/apis/asset_manager/config.ts -``` - -## Using Sample Data - -This plugin comes with a full "working set" of sample asset documents, meant -to provide enough data in the correct schema format so that all of the API -endpoints return expected values. - -To create the sample data, follow [the instructions in the REST API docs](./docs/index.md#sample-data). +For those working on this plugin directly and developing it, please see [our development docs](./docs/development.md). diff --git a/x-pack/plugins/asset_manager/common/config.ts b/x-pack/plugins/asset_manager/common/config.ts new file mode 100644 index 0000000000000..0a57e37d497bb --- /dev/null +++ b/x-pack/plugins/asset_manager/common/config.ts @@ -0,0 +1,56 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; + +export const INDEX_DEFAULTS = { + logs: 'filebeat-*,logs-*', +}; + +export const configSchema = schema.object({ + alphaEnabled: schema.maybe(schema.boolean()), + // Designate where various types of data live. + // NOTE: this should be handled in a centralized way for observability, so + // that when a user configures these differently from the known defaults, + // that value is propagated everywhere. For now, we duplicate the value here. + sourceIndices: schema.object( + { + logs: schema.string({ defaultValue: INDEX_DEFAULTS.logs }), + }, + { defaultValue: INDEX_DEFAULTS } + ), + // Choose an explicit source for asset queries. + // NOTE: This will eventually need to be able to cleverly switch + // between these values based on the availability of data in the + // indices, and possibly for each asset kind/type value. + // For now, we set this explicitly. + lockedSource: schema.oneOf([schema.literal('assets'), schema.literal('signals')], { + defaultValue: 'signals', + }), +}); + +export type AssetManagerConfig = TypeOf; + +/** + * The following map is passed to the server plugin setup under the + * exposeToBrowser: option, and controls which of the above config + * keys are allow-listed to be available in the browser config. + * + * NOTE: anything exposed here will be visible in the UI dev tools, + * and therefore MUST NOT be anything that is sensitive information! + */ +export const exposeToBrowserConfig = { + alphaEnabled: true, +} as const; + +type ValidKeys = keyof { + [K in keyof typeof exposeToBrowserConfig as typeof exposeToBrowserConfig[K] extends true + ? K + : never]: true; +}; + +export type AssetManagerPublicConfig = Pick; diff --git a/x-pack/plugins/asset_manager/common/constants_routes.ts b/x-pack/plugins/asset_manager/common/constants_routes.ts new file mode 100644 index 0000000000000..1aef43f7383bd --- /dev/null +++ b/x-pack/plugins/asset_manager/common/constants_routes.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export const ASSET_MANAGER_API_BASE = '/api/asset-manager'; + +function base(path: string) { + return `${ASSET_MANAGER_API_BASE}${path}`; +} + +export const GET_ASSETS = base('/assets'); +export const GET_RELATED_ASSETS = base('/assets/related'); +export const GET_ASSETS_DIFF = base('/assets/diff'); + +export const GET_HOSTS = base('/assets/hosts'); diff --git a/x-pack/plugins/asset_manager/common/types_api.ts b/x-pack/plugins/asset_manager/common/types_api.ts index 8e9e9181a29e4..11b5ea4bda3a4 100644 --- a/x-pack/plugins/asset_manager/common/types_api.ts +++ b/x-pack/plugins/asset_manager/common/types_api.ts @@ -6,77 +6,111 @@ */ import * as rt from 'io-ts'; - -export const assetTypeRT = rt.union([ - rt.literal('k8s.pod'), - rt.literal('k8s.cluster'), - rt.literal('k8s.node'), -]); +import { + dateRt, + inRangeFromStringRt, + datemathStringRt, + createLiteralValueFromUndefinedRT, +} from '@kbn/io-ts-utils'; + +export const assetTypeRT = rt.keyof({ + 'k8s.pod': null, + 'k8s.cluster': null, + 'k8s.node': null, +}); export type AssetType = rt.TypeOf; -export const assetKindRT = rt.union([ - rt.literal('cluster'), - rt.literal('host'), - rt.literal('pod'), - rt.literal('container'), - rt.literal('service'), - rt.literal('alert'), -]); +export const assetKindRT = rt.keyof({ + cluster: null, + host: null, + pod: null, + container: null, + service: null, + alert: null, +}); export type AssetKind = rt.TypeOf; -export type AssetStatus = - | 'CREATING' - | 'ACTIVE' - | 'DELETING' - | 'FAILED' - | 'UPDATING' - | 'PENDING' - | 'UNKNOWN'; -export type CloudProviderName = 'aws' | 'gcp' | 'azure' | 'other' | 'unknown' | 'none'; - -interface WithTimestamp { - '@timestamp': string; -} -export interface ECSDocument extends WithTimestamp { - 'kubernetes.namespace'?: string; - 'kubernetes.pod.name'?: string; - 'kubernetes.pod.uid'?: string; - 'kubernetes.pod.start_time'?: Date; - 'kubernetes.node.name'?: string; - 'kubernetes.node.start_time'?: Date; - - 'orchestrator.api_version'?: string; - 'orchestrator.namespace'?: string; - 'orchestrator.organization'?: string; - 'orchestrator.type'?: string; - 'orchestrator.cluster.id'?: string; - 'orchestrator.cluster.name'?: string; - 'orchestrator.cluster.url'?: string; - 'orchestrator.cluster.version'?: string; - - 'cloud.provider'?: CloudProviderName; - 'cloud.instance.id'?: string; - 'cloud.region'?: string; - 'cloud.service.name'?: string; - - 'service.environment'?: string; -} +export const assetStatusRT = rt.keyof({ + CREATING: null, + ACTIVE: null, + DELETING: null, + FAILED: null, + UPDATING: null, + PENDING: null, + UNKNOWN: null, +}); + +export type AssetStatus = rt.TypeOf; + +// https://github.com/gcanti/io-ts/blob/master/index.md#union-of-string-literals +export const cloudProviderNameRT = rt.keyof({ + aws: null, + gcp: null, + azure: null, + other: null, + unknown: null, + none: null, +}); + +export type CloudProviderName = rt.TypeOf; + +const withTimestampRT = rt.type({ + '@timestamp': rt.string, +}); + +export type WithTimestamp = rt.TypeOf; + +export const ECSDocumentRT = rt.intersection([ + withTimestampRT, + rt.partial({ + 'kubernetes.namespace': rt.string, + 'kubernetes.pod.name': rt.string, + 'kubernetes.pod.uid': rt.string, + 'kubernetes.pod.start_time': rt.string, + 'kubernetes.node.name': rt.string, + 'kubernetes.node.start_time': rt.string, + 'orchestrator.api_version': rt.string, + 'orchestrator.namespace': rt.string, + 'orchestrator.organization': rt.string, + 'orchestrator.type': rt.string, + 'orchestrator.cluster.id': rt.string, + 'orchestrator.cluster.name': rt.string, + 'orchestrator.cluster.url': rt.string, + 'orchestrator.cluster.version': rt.string, + 'cloud.provider': cloudProviderNameRT, + 'cloud.instance.id': rt.string, + 'cloud.region': rt.string, + 'cloud.service.name': rt.string, + 'service.environment': rt.string, + }), +]); -export interface Asset extends ECSDocument { - 'asset.collection_version'?: string; - 'asset.ean': string; - 'asset.id': string; - 'asset.kind': AssetKind; - 'asset.name'?: string; - 'asset.type'?: AssetType; - 'asset.status'?: AssetStatus; - 'asset.parents'?: string | string[]; - 'asset.children'?: string | string[]; - 'asset.references'?: string | string[]; - 'asset.namespace'?: string; -} +export type ECSDocument = rt.TypeOf; + +export const assetRT = rt.intersection([ + ECSDocumentRT, + rt.type({ + 'asset.ean': rt.string, + 'asset.id': rt.string, + 'asset.kind': assetKindRT, + }), + // mixed required and optional require separate hashes combined via intersection + // https://github.com/gcanti/io-ts/blob/master/index.md#mixing-required-and-optional-props + rt.partial({ + 'asset.collection_version': rt.string, + 'asset.name': rt.string, + 'asset.type': assetTypeRT, + 'asset.status': assetStatusRT, + 'asset.parents': rt.union([rt.string, rt.array(rt.string)]), + 'asset.children': rt.union([rt.string, rt.array(rt.string)]), + 'asset.references': rt.union([rt.string, rt.array(rt.string)]), + 'asset.namespace': rt.string, + }), +]); + +export type Asset = rt.TypeOf; export type AssetWithoutTimestamp = Omit; @@ -156,3 +190,22 @@ export type RelationField = keyof Pick< Asset, 'asset.children' | 'asset.parents' | 'asset.references' >; + +export const sizeRT = rt.union([ + inRangeFromStringRt(1, 100), + createLiteralValueFromUndefinedRT(10), +]); +export const assetDateRT = rt.union([dateRt, datemathStringRt]); +export const getHostAssetsQueryOptionsRT = rt.exact( + rt.partial({ + from: assetDateRT, + to: assetDateRT, + size: sizeRT, + }) +); +export type GetHostAssetsQueryOptions = rt.TypeOf; + +export const getHostAssetsResponseRT = rt.type({ + hosts: rt.array(assetRT), +}); +export type GetHostAssetsResponse = rt.TypeOf; diff --git a/x-pack/plugins/asset_manager/common/types_client.ts b/x-pack/plugins/asset_manager/common/types_client.ts new file mode 100644 index 0000000000000..350a168da8965 --- /dev/null +++ b/x-pack/plugins/asset_manager/common/types_client.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export interface GetHostsOptionsPublic { + from: string; + to: string; +} + +export interface GetServicesOptionsPublic { + from: string; + to: string; + parent?: string; +} diff --git a/x-pack/plugins/asset_manager/docs/api.md b/x-pack/plugins/asset_manager/docs/api.md new file mode 100644 index 0000000000000..755abfe4be373 --- /dev/null +++ b/x-pack/plugins/asset_manager/docs/api.md @@ -0,0 +1,187 @@ +# Asset Manager API Documentation + +## Plugin configuration + +This plugin is NOT fully enabled by default, even though it's always enabled +by Kibana's definition of "enabled". However, without the following configuration, +it will bail before it sets up any routes or returns anything from its +start, setup, or stop hooks. + +To fully enable the plugin, set the following config values in your kibana.yml file: + +```yaml +xpack.assetManager: + alphaEnabled: true +``` + +## Depending on an asset client in your packages + +If you're creating a shared UI component or tool that needs to access asset data, you +can create that code in a stateless Kibana package that can itself be imported into +any Kibana plugin without any dependency restrictions. To gain access to the asset data, +this component or tool can require the appropriate asset client to be passed in. + +TODO: need to move main client types to a package so that they can be depended on by +other packages that require an injected asset client. Then we can list that package name +here and explain how to use those types in a package. + +## Client APIs + +This plugin provides asset clients for Kibana server and public usage. The differences between these +two clients are described below in their sections, while the methods for both APIs are described +in the Client Methods section. + +These clients are set up in the following way. For a given "methodA": + +``` +publicMethodA(...options: MethodAPublicOptions) + -> browser client calls corresponding REST API method with MethodAPublicOptions + -> REST API handler calls corresponding serverMethodA + -> serverMethodA requires MethodAPublicOptions & AssetClientDependencies, and it also + injects some internal dependencies from the plugin's config on your behalf +``` + +The public and server clientss are both accessible to plugin dependants, but the REST API is NOT. + +### Required dependency setup + +To use either client, you must first add "assetManager" to your `"requiredDependencies"` array +in your plugin's kibana.jsonc file. + +TECH PREVIEW NOTE: While this plugin is in "tech preview", in both the server and public clients, +the provided plugin dependencies can be undefined for this plugin if the proper configuration +has not been set (see above). For that reason, the types will force you to guard against this +undefined scenario. Once the tech preview gating is removed, this will no longer be the case. + +### Server client usage + +In your plugin's `setup` method, you can gain access to the client from the injected `plugins` map. +Make sure you import the `AssetManagerServerPluginSetup` type from the plugin's server +directory and add it to your own SetupPlugins type, as seen below. + +```ts +import { AssetManagerServerPluginSetup } from '@kbn/assetManager-plugin/server'; + +interface MyPluginSetupDeps { + assetManager: AssetManagerServerPluginSetup; +} + +class MyPlugin { + setup(core: CoreSetup, plugins: MyPluginSetupDeps) { + // assetClient is found on plugins.assetManager.assetClient + setupRoutes(router, plugins); + } +} +``` + +To use the server client in your server routes, you can use something like this: + +```ts +export function setupRoutes(router: IRouter, plugins: MyPluginDeps) { + router.get( + { + path: '/my/path', + validate: {}, + }, + async (context, req, res) => { + // handle route + // optionally, use asset client + // NOTE: see below for important info on required server client args + const hosts = await plugins.assetManager.assetClient.getHosts(); + } + ); +} +``` + +#### Required parameters for server client methods + +All methods called via the server client require some core Kibana clients to be passed in, +so that they are pulled from the request context and properly scoped. If the asset manager +plugin provided these clients internally, they would not be scoped to the user that made +the API request, so they are required arguments for every server client method. + +_Note: These required arguments are referred to as `AssetClientDependencies`, which can be +seen in the [the server types file](../server/types.ts)._ + +For example: + +```ts +router.get( + { + path: '/my/path', + validate: {}, + }, + async (context, req, res) => { + // to use server asset client, you must get the following clients + // from the request context and pass them to the client method + // alongside whatever "public" arguments that method defines + const coreContext = await context.core; + const hostsOptions: PublicGetHostsOptions = {}; // these will be different for each method + + const hosts = await plugins.assetManager.assetClient.getHosts({ + ...hostsOptions, + elasticsearchClient: coreContext.elasticsearch.client.asCurrentUser, + savedObjectsClient: coreContext.savedObjects.client, + }); + } +); +``` + +### Public client usage + +You should grab the public client in the same way as the server one, via the plugin dependencies +in your `setup` lifecycle. + +```ts +import { AssetManagerPublicPluginStart } from '@kbn/assetManager-plugin/public'; + +interface MyPluginStartDeps { + assetManager: AssetManagerPublicPluginStart; +} + +class MyPlugin { + setup(core: CoreSetup) { + core.application.register({ + id: 'my-other-plugin', + title: '', + appRoute: '/app/my-other-plugin', + mount: async (params: AppMountParameters) => { + // mount callback should not use setup dependencies, get start dependencies instead + // so the pluginStart map passed to your renderApp method will be the start deps, + // not the setup deps -- the same asset client is provided to both setup and start in public + const [coreStart, , pluginStart] = await core.getStartServices(); + // assetClient is found on pluginStart.assetManager.assetClient + return renderApp(coreStart, pluginStart, params); + }, + }); + } +} +``` + +All methods in the public client only require their public options (seen below), and don't require +the "AssetClientDependencies" that are required for the server client versions of the same methods. +This is because the public client will use the asset manager's internal REST API under the hood, where +it will be able to pull the properly-scoped client dependencies off of that request context for you. + +### Client methods + +#### getHosts + +Get a group of host assets found within a specified time range. + +| Parameter | Type | Required? | Description | +| :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | +| from | datetime string | yes | ISO date string representing the START of the time range being queried | +| to | datetime string | yes | ISO date string representing the END of the time range being queried | + +**Response** + +```json +{ + "hosts": [ + ...found host assets + ] +} +``` + +TODO: Link to a centralized asset document example that each response can reference? diff --git a/x-pack/plugins/asset_manager/docs/development.md b/x-pack/plugins/asset_manager/docs/development.md new file mode 100644 index 0000000000000..a98e8e46a8ce4 --- /dev/null +++ b/x-pack/plugins/asset_manager/docs/development.md @@ -0,0 +1,34 @@ +# Asset Manager Plugin Development + +These docs contain information you might need if you are developing this plugin in Kibana. If you are interested in the APIs this plugin exposes, please see [./api.md](our API docs) instead. + +## Running Tests + +There are integration tests for the endpoints implemented thus far as well as for +the sample data tests. There is also a small set of tests meant to ensure that the +plugin is not doing anything without the proper config value in place to enable +the plugin fully. For more on enabling the plugin, see [the docs page](./docs/index.md). + +The "not enabled" tests are run by default in CI. To run them manually, do the following: + +```shell +$ node scripts/functional_tests_server --config x-pack/test/api_integration/apis/asset_manager/config_when_disabled.ts +$ node scripts/functional_test_runner --config=x-pack/test/api_integration/apis/asset_manager/config_when_disabled.ts +``` + +The "enabled" tests are NOT run by CI yet, to prevent blocking Kibana development for a +test failure in this alpha, tech preview plugin. They will be moved into the right place +to make them run for CI before the plugin is enabled by default. To run them manually: + +```shell +$ node scripts/functional_tests_server --config x-pack/test/api_integration/apis/asset_manager/config.ts +$ node scripts/functional_test_runner --config=x-pack/test/api_integration/apis/asset_manager/config.ts +``` + +## Using Sample Data + +This plugin comes with a full "working set" of sample asset documents, meant +to provide enough data in the correct schema format so that all of the API +endpoints return expected values. + +To create the sample data, follow [the instructions in the REST API docs](./docs/index.md#sample-data). diff --git a/x-pack/plugins/asset_manager/docs/index.md b/x-pack/plugins/asset_manager/docs/rest_deprecated.md similarity index 93% rename from x-pack/plugins/asset_manager/docs/index.md rename to x-pack/plugins/asset_manager/docs/rest_deprecated.md index 790beb87b4f7e..43a5f74a0d058 100644 --- a/x-pack/plugins/asset_manager/docs/index.md +++ b/x-pack/plugins/asset_manager/docs/rest_deprecated.md @@ -1,24 +1,6 @@ -# Asset Manager Documentation +## Deprecated REST API docs -_Note:_ To read about development guidance around testing, sample data, etc., see the -[plugin's main README file](../README.md) - -## Alpha Configuration - -This plugin is NOT fully enabled by default, even though it's always enabled -by Kibana's definition of "enabled". However, without the following configuration, -it will bail before it sets up any routes or returns anything from its -start, setup, or stop hooks. - -To fully enable the plugin, set the following config value in your kibana.yml file: - -```yaml -xpack.assetManager.alphaEnabled: true -``` - -## APIs - -This plugin provides the following APIs. +These docs are not being currently maintained because they pertain to an internal REST API. Please see [our docs for our API clients](./api.md) instead. ### Shared Types @@ -58,16 +40,16 @@ Returns a list of assets present within a given time range. Can be limited by as ##### Request -| Option | Type | Required? | Default | Description | -| :------ | :------------ | :-------- | :------ | :--------------------------------------------------------------------------------- | -| from | RangeDate | No | "now-24h" | Starting point for date range to search for assets within | -| to | RangeDate | No | "now" | End point for date range to search for assets | -| type | AssetType[] | No | all | Specify one or more types to restrict the query | -| ean | AssetEan[] | No | all | Specify one or more EANs (specific assets) to restrict the query | -| size | number | No | all | Limit the amount of assets returned | - +| Option | Type | Required? | Default | Description | +| :----- | :---------- | :-------- | :-------- | :--------------------------------------------------------------- | +| from | RangeDate | No | "now-24h" | Starting point for date range to search for assets within | +| to | RangeDate | No | "now" | End point for date range to search for assets | +| type | AssetType[] | No | all | Specify one or more types to restrict the query | +| ean | AssetEan[] | No | all | Specify one or more EANs (specific assets) to restrict the query | +| size | number | No | all | Limit the amount of assets returned | _Notes:_ + - User cannot specify both type and ean at the same time. - For array types such as `type` and `ean`, user should specify the query parameter multiple times, e.g. `type=k8s.pod&type=k8s.node` @@ -410,15 +392,15 @@ GET kbn:/api/asset-manager/assets?from=2023-03-25T17:44:44.000Z&to=2023-03-25T18 Returns assets found in the two time ranges, split by what occurs in only either or in both. -#### Request +#### Request -| Option | Type | Required? | Default | Description | -| :--- | :--- | :--- | :--- | :--- | -| aFrom | RangeDate | Yes | N/A | Starting point for baseline date range to search for assets within | -| aTo | RangeDate | Yes | N/A | End point for baseline date range to search for assets within | -| bFrom | RangeDate | Yes | N/A | Starting point for comparison date range | -| bTo | RangeDate | Yes | N/A | End point for comparison date range | -| type | AssetType[] | No | all | Restrict results to one or more asset.type value | +| Option | Type | Required? | Default | Description | +| :----- | :---------- | :-------- | :------ | :----------------------------------------------------------------- | +| aFrom | RangeDate | Yes | N/A | Starting point for baseline date range to search for assets within | +| aTo | RangeDate | Yes | N/A | End point for baseline date range to search for assets within | +| bFrom | RangeDate | Yes | N/A | Starting point for comparison date range | +| bTo | RangeDate | Yes | N/A | End point for comparison date range | +| type | AssetType[] | No | all | Restrict results to one or more asset.type value | #### Responses @@ -1044,14 +1026,14 @@ Returns assets related to the provided ean. The relation can be one of ancestors #### Request -| Option | Type | Required? | Default | Description | -| :--- | :--- | :--- | :--- | :--- | -| relation | string | Yes | N/A | The type of related assets we're looking for. One of (ancestors|descendants|references) | -| from | RangeDate | Yes | N/A | Starting point for date range to search for assets within | -| to | RangeDate | No | "now" | End point for date range to search for assets | -| ean | AssetEan | Yes | N/A | Single Elastic Asset Name representing the asset for which the related assets are being requested | -| type | AssetType[] | No | all | Restrict results to one or more asset.type value | -| maxDistance | number (1-5) | No | 1 | Maximum number of "hops" to search away from specified asset | +| Option | Type | Required? | Default | Description | +| :---------- | :----------- | :-------- | :------ | :------------------------------------------------------------------------------------------------ | ----------- | ----------- | +| relation | string | Yes | N/A | The type of related assets we're looking for. One of (ancestors | descendants | references) | +| from | RangeDate | Yes | N/A | Starting point for date range to search for assets within | +| to | RangeDate | No | "now" | End point for date range to search for assets | +| ean | AssetEan | Yes | N/A | Single Elastic Asset Name representing the asset for which the related assets are being requested | +| type | AssetType[] | No | all | Restrict results to one or more asset.type value | +| maxDistance | number (1-5) | No | 1 | Maximum number of "hops" to search away from specified asset | #### Responses diff --git a/x-pack/plugins/asset_manager/kibana.jsonc b/x-pack/plugins/asset_manager/kibana.jsonc index 49b1b59838d9c..b3fcd1b3a4fa1 100644 --- a/x-pack/plugins/asset_manager/kibana.jsonc +++ b/x-pack/plugins/asset_manager/kibana.jsonc @@ -15,7 +15,7 @@ "apmDataAccess", "metricsDataAccess" ], - "browser": false, + "browser": true, "server": true, "requiredBundles": [ ] diff --git a/x-pack/plugins/asset_manager/public/index.ts b/x-pack/plugins/asset_manager/public/index.ts new file mode 100644 index 0000000000000..7837c00909430 --- /dev/null +++ b/x-pack/plugins/asset_manager/public/index.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializer, PluginInitializerContext } from '@kbn/core/public'; +import { Plugin } from './plugin'; +import { AssetManagerPublicPluginSetup, AssetManagerPublicPluginStart } from './types'; + +export const plugin: PluginInitializer< + AssetManagerPublicPluginSetup | undefined, + AssetManagerPublicPluginStart | undefined +> = (context: PluginInitializerContext) => { + return new Plugin(context); +}; + +export type { AssetManagerPublicPluginSetup, AssetManagerPublicPluginStart }; +export type AssetManagerAppId = 'assetManager'; diff --git a/x-pack/plugins/asset_manager/public/lib/public_assets_client.test.ts b/x-pack/plugins/asset_manager/public/lib/public_assets_client.test.ts new file mode 100644 index 0000000000000..93cc541a34af4 --- /dev/null +++ b/x-pack/plugins/asset_manager/public/lib/public_assets_client.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { HttpSetupMock } from '@kbn/core-http-browser-mocks'; +import { coreMock } from '@kbn/core/public/mocks'; +import { PublicAssetsClient } from './public_assets_client'; +import * as routePaths from '../../common/constants_routes'; + +describe('Public assets client', () => { + let http: HttpSetupMock = coreMock.createSetup().http; + + beforeEach(() => { + http = coreMock.createSetup().http; + }); + + describe('class instantiation', () => { + it('should successfully instantiate', () => { + new PublicAssetsClient(http); + }); + }); + + describe('getHosts', () => { + it('should call the REST API', async () => { + const client = new PublicAssetsClient(http); + await client.getHosts({ from: 'x', to: 'y' }); + expect(http.get).toBeCalledTimes(1); + }); + + it('should include specified "from" and "to" parameters in http.get query', async () => { + const client = new PublicAssetsClient(http); + await client.getHosts({ from: 'x', to: 'y' }); + expect(http.get).toBeCalledWith(routePaths.GET_HOSTS, { + query: { from: 'x', to: 'y' }, + }); + }); + + it('should return the direct results of http.get', async () => { + const client = new PublicAssetsClient(http); + http.get.mockResolvedValueOnce('my result'); + const result = await client.getHosts({ from: 'x', to: 'y' }); + expect(result).toBe('my result'); + }); + }); +}); diff --git a/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts b/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts new file mode 100644 index 0000000000000..dd18386868f94 --- /dev/null +++ b/x-pack/plugins/asset_manager/public/lib/public_assets_client.ts @@ -0,0 +1,26 @@ +/* + * 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 { HttpStart } from '@kbn/core/public'; +import { GetHostsOptionsPublic } from '../../common/types_client'; +import { GetHostAssetsResponse } from '../../common/types_api'; +import { GET_HOSTS } from '../../common/constants_routes'; +import { IPublicAssetsClient } from '../types'; + +export class PublicAssetsClient implements IPublicAssetsClient { + constructor(private readonly http: HttpStart) {} + + async getHosts(options: GetHostsOptionsPublic) { + const results = await this.http.get(GET_HOSTS, { + query: { + ...options, + }, + }); + + return results; + } +} diff --git a/x-pack/plugins/asset_manager/public/plugin.ts b/x-pack/plugins/asset_manager/public/plugin.ts new file mode 100644 index 0000000000000..4b2d91f3a60f1 --- /dev/null +++ b/x-pack/plugins/asset_manager/public/plugin.ts @@ -0,0 +1,51 @@ +/* + * 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 { CoreSetup, CoreStart, PluginInitializerContext } from '@kbn/core/public'; +import { Logger } from '@kbn/logging'; +import { AssetManagerPluginClass } from './types'; +import { PublicAssetsClient } from './lib/public_assets_client'; +import type { AssetManagerPublicConfig } from '../common/config'; + +export class Plugin implements AssetManagerPluginClass { + public config: AssetManagerPublicConfig; + public logger: Logger; + + constructor(context: PluginInitializerContext<{}>) { + this.config = context.config.get(); + this.logger = context.logger.get(); + } + + setup(core: CoreSetup) { + // Check for config value and bail out if not "alpha-enabled" + if (!this.config.alphaEnabled) { + this.logger.debug('Public is NOT enabled'); + return; + } + + this.logger.debug('Public is enabled'); + + const publicAssetsClient = new PublicAssetsClient(core.http); + return { + publicAssetsClient, + }; + } + + start(core: CoreStart) { + // Check for config value and bail out if not "alpha-enabled" + if (!this.config.alphaEnabled) { + return; + } + + const publicAssetsClient = new PublicAssetsClient(core.http); + return { + publicAssetsClient, + }; + } + + stop() {} +} diff --git a/x-pack/plugins/asset_manager/public/types.ts b/x-pack/plugins/asset_manager/public/types.ts new file mode 100644 index 0000000000000..67f0053cfdd56 --- /dev/null +++ b/x-pack/plugins/asset_manager/public/types.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Plugin as PluginClass } from '@kbn/core/public'; +import { GetHostsOptionsPublic } from '../common/types_client'; +import { GetHostAssetsResponse } from '../common/types_api'; +export interface AssetManagerPublicPluginSetup { + publicAssetsClient: IPublicAssetsClient; +} + +export interface AssetManagerPublicPluginStart { + publicAssetsClient: IPublicAssetsClient; +} + +export type AssetManagerPluginClass = PluginClass< + AssetManagerPublicPluginSetup | undefined, + AssetManagerPublicPluginStart | undefined +>; + +export interface IPublicAssetsClient { + getHosts: (options: GetHostsOptionsPublic) => Promise; +} diff --git a/x-pack/plugins/asset_manager/server/constants.ts b/x-pack/plugins/asset_manager/server/constants.ts index 0aa1cb467df48..4630365e47875 100644 --- a/x-pack/plugins/asset_manager/server/constants.ts +++ b/x-pack/plugins/asset_manager/server/constants.ts @@ -6,4 +6,3 @@ */ export const ASSETS_INDEX_PREFIX = 'assets'; -export const ASSET_MANAGER_API_BASE = '/api/asset-manager'; diff --git a/x-pack/plugins/asset_manager/server/index.ts b/x-pack/plugins/asset_manager/server/index.ts index d6eafa380b857..5dbecbee5f9da 100644 --- a/x-pack/plugins/asset_manager/server/index.ts +++ b/x-pack/plugins/asset_manager/server/index.ts @@ -6,11 +6,21 @@ */ import { PluginInitializerContext } from '@kbn/core-plugins-server'; -import { AssetManagerServerPlugin, config } from './plugin'; +import { AssetManagerConfig } from '../common/config'; +import { + AssetManagerServerPlugin, + AssetManagerServerPluginSetup, + AssetManagerServerPluginStart, + config, +} from './plugin'; import type { WriteSamplesPostBody } from './routes/sample_assets'; -import { AssetManagerConfig } from './types'; -export type { AssetManagerConfig, WriteSamplesPostBody }; +export type { + AssetManagerConfig, + WriteSamplesPostBody, + AssetManagerServerPluginSetup, + AssetManagerServerPluginStart, +}; export { config }; export const plugin = (context: PluginInitializerContext) => diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_assets.ts b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_assets.ts index 13e2d00a82083..f975df1cd82f4 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_assets.ts @@ -13,7 +13,7 @@ export async function getHostsByAssets( options: GetHostsOptionsInjected ): Promise<{ hosts: Asset[] }> { const hosts = await getAssets({ - esClient: options.esClient, + elasticsearchClient: options.elasticsearchClient, filters: { kind: 'host', from: options.from, diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_signals.ts b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_signals.ts index 4fad9e301a89d..93e601ae00f9c 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_signals.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/get_hosts_by_signals.ts @@ -13,11 +13,11 @@ export async function getHostsBySignals( options: GetHostsOptionsInjected ): Promise<{ hosts: Asset[] }> { const metricsIndices = await options.metricsClient.getMetricIndices({ - savedObjectsClient: options.soClient, + savedObjectsClient: options.savedObjectsClient, }); const { assets } = await collectHosts({ - client: options.esClient, + client: options.elasticsearchClient, from: options.from, to: options.to, sourceIndices: { diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/index.ts b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/index.ts index 9becb15ebdc0a..1b60268d85389 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/hosts/index.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/hosts/index.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { AccessorOptions, OptionsWithInjectedValues } from '..'; +import type { AssetClientDependencies } from '../../../types'; +import type { GetHostsOptionsPublic } from '../../../../common/types_client'; +import type { OptionsWithInjectedValues } from '..'; -export interface GetHostsOptions extends AccessorOptions { - from: string; - to: string; -} +export type GetHostsOptions = GetHostsOptionsPublic & AssetClientDependencies; export type GetHostsOptionsInjected = OptionsWithInjectedValues; export interface HostIdentifier { diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/index.ts b/x-pack/plugins/asset_manager/server/lib/accessors/index.ts index f5cf4d38fadc8..6fd9254a2182e 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/index.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/index.ts @@ -5,11 +5,10 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { APMDataAccessConfig } from '@kbn/apm-data-access-plugin/server'; import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server'; import { SavedObjectsClientContract } from '@kbn/core/server'; -import { AssetManagerConfig } from '../../types'; +import { AssetManagerConfig } from '../../../common/config'; export interface InjectedValues { sourceIndices: AssetManagerConfig['sourceIndices']; @@ -18,8 +17,3 @@ export interface InjectedValues { } export type OptionsWithInjectedValues = T & InjectedValues; - -export interface AccessorOptions { - esClient: ElasticsearchClient; - soClient: SavedObjectsClientContract; -} diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_assets.ts b/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_assets.ts index 8bdd6283d6559..8e69bcbff4625 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_assets.ts @@ -18,7 +18,7 @@ export async function getServicesByAssets( } const services = await getAssets({ - esClient: options.esClient, + elasticsearchClient: options.elasticsearchClient, filters: { kind: 'service', from: options.from, @@ -32,7 +32,7 @@ export async function getServicesByAssets( async function getServicesByParent( options: GetServicesOptionsInjected ): Promise<{ services: Asset[] }> { - const { descendants } = await getAllRelatedAssets(options.esClient, { + const { descendants } = await getAllRelatedAssets(options.elasticsearchClient, { from: options.from, to: options.to, maxDistance: 5, diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_signals.ts b/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_signals.ts index ab8de39adb301..720d6b3e30531 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_signals.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/services/get_services_by_signals.ts @@ -26,9 +26,9 @@ export async function getServicesBySignals( }); } - const apmIndices = await options.getApmIndices(options.soClient); + const apmIndices = await options.getApmIndices(options.savedObjectsClient); const { assets } = await collectServices({ - client: options.esClient, + client: options.elasticsearchClient, from: options.from, to: options.to, sourceIndices: { diff --git a/x-pack/plugins/asset_manager/server/lib/accessors/services/index.ts b/x-pack/plugins/asset_manager/server/lib/accessors/services/index.ts index 3fed1047eacba..e8b52e4924c4d 100644 --- a/x-pack/plugins/asset_manager/server/lib/accessors/services/index.ts +++ b/x-pack/plugins/asset_manager/server/lib/accessors/services/index.ts @@ -5,13 +5,11 @@ * 2.0. */ -import { AccessorOptions, OptionsWithInjectedValues } from '..'; +import { AssetClientDependencies } from '../../../types'; +import { GetServicesOptionsPublic } from '../../../../common/types_client'; +import { OptionsWithInjectedValues } from '..'; -export interface GetServicesOptions extends AccessorOptions { - from: string; - to: string; - parent?: string; -} +export type GetServicesOptions = GetServicesOptionsPublic & AssetClientDependencies; export type GetServicesOptionsInjected = OptionsWithInjectedValues; export interface ServiceIdentifier { diff --git a/x-pack/plugins/asset_manager/server/lib/asset_accessor.ts b/x-pack/plugins/asset_manager/server/lib/asset_client.ts similarity index 81% rename from x-pack/plugins/asset_manager/server/lib/asset_accessor.ts rename to x-pack/plugins/asset_manager/server/lib/asset_client.ts index 73c2064e48311..8bf23313c663e 100644 --- a/x-pack/plugins/asset_manager/server/lib/asset_accessor.ts +++ b/x-pack/plugins/asset_manager/server/lib/asset_client.ts @@ -8,8 +8,8 @@ import { APMDataAccessConfig } from '@kbn/apm-data-access-plugin/server'; import { MetricsDataClient } from '@kbn/metrics-data-access-plugin/server'; import { SavedObjectsClientContract } from '@kbn/core/server'; +import { AssetManagerConfig } from '../../common/config'; import { Asset } from '../../common/types_api'; -import { AssetManagerConfig } from '../types'; import { OptionsWithInjectedValues } from './accessors'; import { GetHostsOptions } from './accessors/hosts'; import { GetServicesOptions } from './accessors/services'; @@ -18,28 +18,28 @@ import { getHostsBySignals } from './accessors/hosts/get_hosts_by_signals'; import { getServicesByAssets } from './accessors/services/get_services_by_assets'; import { getServicesBySignals } from './accessors/services/get_services_by_signals'; -interface AssetAccessorClassOptions { +interface AssetClientClassOptions { sourceIndices: AssetManagerConfig['sourceIndices']; source: AssetManagerConfig['lockedSource']; getApmIndices: (soClient: SavedObjectsClientContract) => Promise; metricsClient: MetricsDataClient; } -export class AssetAccessor { - constructor(private options: AssetAccessorClassOptions) {} +export class AssetClient { + constructor(private baseOptions: AssetClientClassOptions) {} injectOptions(options: T): OptionsWithInjectedValues { return { ...options, - sourceIndices: this.options.sourceIndices, - getApmIndices: this.options.getApmIndices, - metricsClient: this.options.metricsClient, + sourceIndices: this.baseOptions.sourceIndices, + getApmIndices: this.baseOptions.getApmIndices, + metricsClient: this.baseOptions.metricsClient, }; } async getHosts(options: GetHostsOptions): Promise<{ hosts: Asset[] }> { const withInjected = this.injectOptions(options); - if (this.options.source === 'assets') { + if (this.baseOptions.source === 'assets') { return await getHostsByAssets(withInjected); } else { return await getHostsBySignals(withInjected); @@ -48,7 +48,7 @@ export class AssetAccessor { async getServices(options: GetServicesOptions): Promise<{ services: Asset[] }> { const withInjected = this.injectOptions(options); - if (this.options.source === 'assets') { + if (this.baseOptions.source === 'assets') { return await getServicesByAssets(withInjected); } else { return await getServicesBySignals(withInjected); diff --git a/x-pack/plugins/asset_manager/server/lib/get_all_related_assets.ts b/x-pack/plugins/asset_manager/server/lib/get_all_related_assets.ts index ad8aff78cbb18..dddbb792b0979 100644 --- a/x-pack/plugins/asset_manager/server/lib/get_all_related_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/get_all_related_assets.ts @@ -26,13 +26,13 @@ interface GetAllRelatedAssetsOptions { } export async function getAllRelatedAssets( - esClient: ElasticsearchClient, + elasticsearchClient: ElasticsearchClient, options: GetAllRelatedAssetsOptions ) { // How to put size into this? const { ean, from, to, relation, maxDistance, kind = [] } = options; - const primary = await findPrimary(esClient, { ean, from, to }); + const primary = await findPrimary(elasticsearchClient, { ean, from, to }); let assetsToFetch = [primary]; let currentDistance = 1; @@ -52,7 +52,7 @@ export async function getAllRelatedAssets( const results = flatten( await Promise.all( - assetsToFetch.map((asset) => findRelatedAssets(esClient, asset, queryOptions)) + assetsToFetch.map((asset) => findRelatedAssets(elasticsearchClient, asset, queryOptions)) ) ); @@ -75,11 +75,11 @@ export async function getAllRelatedAssets( } async function findPrimary( - esClient: ElasticsearchClient, + elasticsearchClient: ElasticsearchClient, { ean, from, to }: Pick ): Promise { const primaryResults = await getAssets({ - esClient, + elasticsearchClient, size: 1, filters: { ean, from, to }, }); @@ -101,7 +101,7 @@ type FindRelatedAssetsOptions = Pick< > & { visitedEans: string[] }; async function findRelatedAssets( - esClient: ElasticsearchClient, + elasticsearchClient: ElasticsearchClient, primary: Asset, { relation, from, to, kind, visitedEans }: FindRelatedAssetsOptions ): Promise { @@ -116,7 +116,7 @@ async function findRelatedAssets( const remainingEansToFind = without(directlyRelatedEans, ...visitedEans); if (remainingEansToFind.length > 0) { directlyRelatedAssets = await getAssets({ - esClient, + elasticsearchClient, filters: { ean: remainingEansToFind, from, to, kind }, }); } @@ -124,7 +124,7 @@ async function findRelatedAssets( debug('Directly related assets found:', JSON.stringify(directlyRelatedAssets)); const indirectlyRelatedAssets = await getIndirectlyRelatedAssets({ - esClient, + elasticsearchClient, ean: primary['asset.ean'], excludeEans: visitedEans.concat(directlyRelatedEans), relation, diff --git a/x-pack/plugins/asset_manager/server/lib/get_assets.ts b/x-pack/plugins/asset_manager/server/lib/get_assets.ts index 12f87e4b398fc..e3630f92f26e9 100644 --- a/x-pack/plugins/asset_manager/server/lib/get_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/get_assets.ts @@ -19,7 +19,7 @@ interface GetAssetsOptions extends ElasticsearchAccessorOptions { } export async function getAssets({ - esClient, + elasticsearchClient, size = 100, filters = {}, }: GetAssetsOptions): Promise { @@ -125,6 +125,6 @@ export async function getAssets({ debug('Performing Get Assets Query', '\n\n', JSON.stringify(dsl, null, 2)); - const response = await esClient.search(dsl); + const response = await elasticsearchClient.search(dsl); return response.hits.hits.map((hit) => hit._source).filter((asset): asset is Asset => !!asset); } diff --git a/x-pack/plugins/asset_manager/server/lib/get_indirectly_related_assets.ts b/x-pack/plugins/asset_manager/server/lib/get_indirectly_related_assets.ts index fa9f3279ec497..b91242f4aba1b 100644 --- a/x-pack/plugins/asset_manager/server/lib/get_indirectly_related_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/get_indirectly_related_assets.ts @@ -23,7 +23,7 @@ interface GetRelatedAssetsOptions extends ElasticsearchAccessorOptions { } export async function getIndirectlyRelatedAssets({ - esClient, + elasticsearchClient, size = 100, from = 'now-24h', to = 'now', @@ -91,7 +91,7 @@ export async function getIndirectlyRelatedAssets({ debug('Performing Indirectly Related Asset Query', '\n\n', JSON.stringify(dsl, null, 2)); - const response = await esClient.search(dsl); + const response = await elasticsearchClient.search(dsl); return response.hits.hits.map((hit) => hit._source).filter((asset): asset is Asset => !!asset); } diff --git a/x-pack/plugins/asset_manager/server/lib/write_assets.ts b/x-pack/plugins/asset_manager/server/lib/write_assets.ts index 55c5397645725..72b79bc366b6d 100644 --- a/x-pack/plugins/asset_manager/server/lib/write_assets.ts +++ b/x-pack/plugins/asset_manager/server/lib/write_assets.ts @@ -18,7 +18,7 @@ interface WriteAssetsOptions extends ElasticsearchAccessorOptions { } export async function writeAssets({ - esClient, + elasticsearchClient, assetDocs, namespace = 'default', refresh = false, @@ -33,5 +33,5 @@ export async function writeAssets({ debug('Performing Write Asset Query', '\n\n', JSON.stringify(dsl, null, 2)); - return await esClient.bulk<{}>(dsl); + return await elasticsearchClient.bulk<{}>(dsl); } diff --git a/x-pack/plugins/asset_manager/server/plugin.ts b/x-pack/plugins/asset_manager/server/plugin.ts index 6693e6037a836..24563b5e0fbc1 100644 --- a/x-pack/plugins/asset_manager/server/plugin.ts +++ b/x-pack/plugins/asset_manager/server/plugin.ts @@ -18,15 +18,16 @@ import { import { upsertTemplate } from './lib/manage_index_templates'; import { setupRoutes } from './routes'; import { assetsIndexTemplateConfig } from './templates/assets_template'; -import { AssetManagerConfig, configSchema } from './types'; -import { AssetAccessor } from './lib/asset_accessor'; +import { AssetClient } from './lib/asset_client'; import { AssetManagerPluginSetupDependencies, AssetManagerPluginStartDependencies } from './types'; +import { AssetManagerConfig, configSchema, exposeToBrowserConfig } from '../common/config'; export type AssetManagerServerPluginSetup = ReturnType; export type AssetManagerServerPluginStart = ReturnType; export const config: PluginConfigDescriptor = { schema: configSchema, + exposeToBrowser: exposeToBrowserConfig, }; export class AssetManagerServerPlugin @@ -49,13 +50,13 @@ export class AssetManagerServerPlugin public setup(core: CoreSetup, plugins: AssetManagerPluginSetupDependencies) { // Check for config value and bail out if not "alpha-enabled" if (!this.config.alphaEnabled) { - this.logger.info('Asset manager plugin [tech preview] is NOT enabled'); + this.logger.info('Server is NOT enabled'); return; } - this.logger.info('Asset manager plugin [tech preview] is enabled'); + this.logger.info('Server is enabled'); - const assetAccessor = new AssetAccessor({ + const assetClient = new AssetClient({ source: this.config.lockedSource, sourceIndices: this.config.sourceIndices, getApmIndices: plugins.apmDataAccess.getApmIndices, @@ -63,10 +64,10 @@ export class AssetManagerServerPlugin }); const router = core.http.createRouter(); - setupRoutes({ router, assetAccessor }); + setupRoutes({ router, assetClient }); return { - assetAccessor, + assetClient, }; } diff --git a/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts b/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts index e17ad95f81a24..f7780f2ef4a6c 100644 --- a/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts +++ b/x-pack/plugins/asset_manager/server/routes/assets/hosts.ts @@ -5,56 +5,37 @@ * 2.0. */ -import * as rt from 'io-ts'; import datemath from '@kbn/datemath'; -import { - dateRt, - inRangeFromStringRt, - datemathStringRt, - createRouteValidationFunction, - createLiteralValueFromUndefinedRT, -} from '@kbn/io-ts-utils'; +import { createRouteValidationFunction } from '@kbn/io-ts-utils'; import { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; +import { GetHostAssetsQueryOptions, getHostAssetsQueryOptionsRT } from '../../../common/types_api'; import { debug } from '../../../common/debug_log'; import { SetupRouteOptions } from '../types'; -import { ASSET_MANAGER_API_BASE } from '../../constants'; -import { getEsClientFromContext } from '../utils'; - -const sizeRT = rt.union([inRangeFromStringRt(1, 100), createLiteralValueFromUndefinedRT(10)]); -const assetDateRT = rt.union([dateRt, datemathStringRt]); -const getHostAssetsQueryOptionsRT = rt.exact( - rt.partial({ - from: assetDateRT, - to: assetDateRT, - size: sizeRT, - }) -); - -export type GetHostAssetsQueryOptions = rt.TypeOf; +import * as routePaths from '../../../common/constants_routes'; +import { getClientsFromContext } from '../utils'; export function hostsRoutes({ router, - assetAccessor, + assetClient, }: SetupRouteOptions) { router.get( { - path: `${ASSET_MANAGER_API_BASE}/assets/hosts`, + path: routePaths.GET_HOSTS, validate: { query: createRouteValidationFunction(getHostAssetsQueryOptionsRT), }, }, async (context, req, res) => { const { from = 'now-24h', to = 'now' } = req.query || {}; - const esClient = await getEsClientFromContext(context); - const coreContext = await context.core; - const soClient = coreContext.savedObjects.client; + + const { elasticsearchClient, savedObjectsClient } = await getClientsFromContext(context); try { - const response = await assetAccessor.getHosts({ + const response = await assetClient.getHosts({ from: datemath.parse(from)!.toISOString(), to: datemath.parse(to)!.toISOString(), - esClient, - soClient, + elasticsearchClient, + savedObjectsClient, }); return res.ok({ body: response }); diff --git a/x-pack/plugins/asset_manager/server/routes/assets/index.ts b/x-pack/plugins/asset_manager/server/routes/assets/index.ts index b8b6d7ab0fa3a..8d9eaff170d30 100644 --- a/x-pack/plugins/asset_manager/server/routes/assets/index.ts +++ b/x-pack/plugins/asset_manager/server/routes/assets/index.ts @@ -17,11 +17,11 @@ import { } from '@kbn/io-ts-utils'; import { debug } from '../../../common/debug_log'; import { assetTypeRT, assetKindRT, relationRT } from '../../../common/types_api'; -import { ASSET_MANAGER_API_BASE } from '../../constants'; +import { GET_ASSETS, GET_RELATED_ASSETS, GET_ASSETS_DIFF } from '../../../common/constants_routes'; import { getAssets } from '../../lib/get_assets'; import { getAllRelatedAssets } from '../../lib/get_all_related_assets'; import { SetupRouteOptions } from '../types'; -import { getEsClientFromContext } from '../utils'; +import { getClientsFromContext } from '../utils'; import { AssetNotFoundError } from '../../lib/errors'; import { isValidRange } from '../../lib/utils'; @@ -82,7 +82,7 @@ export function assetsRoutes({ router }: SetupR // GET /assets router.get( { - path: `${ASSET_MANAGER_API_BASE}/assets`, + path: GET_ASSETS, validate: { query: createRouteValidationFunction(getAssetsQueryOptionsRT), }, @@ -102,10 +102,10 @@ export function assetsRoutes({ router }: SetupR }); } - const esClient = await getEsClientFromContext(context); + const { elasticsearchClient } = await getClientsFromContext(context); try { - const results = await getAssets({ esClient, size, filters }); + const results = await getAssets({ elasticsearchClient, size, filters }); return res.ok({ body: { results } }); } catch (error: unknown) { debug('error looking up asset records', error); @@ -120,7 +120,7 @@ export function assetsRoutes({ router }: SetupR // GET assets/related router.get( { - path: `${ASSET_MANAGER_API_BASE}/assets/related`, + path: GET_RELATED_ASSETS, validate: { query: createRouteValidationFunction(getRelatedAssetsQueryOptionsRT), }, @@ -129,7 +129,7 @@ export function assetsRoutes({ router }: SetupR // Add references into sample data and write integration tests const { from, to, ean, relation, maxDistance, size, type, kind } = req.query || {}; - const esClient = await getEsClientFromContext(context); + const { elasticsearchClient } = await getClientsFromContext(context); if (to && !isValidRange(from, to)) { return res.badRequest({ @@ -140,7 +140,7 @@ export function assetsRoutes({ router }: SetupR try { return res.ok({ body: { - results: await getAllRelatedAssets(esClient, { + results: await getAllRelatedAssets(elasticsearchClient, { ean, from, to, @@ -165,7 +165,7 @@ export function assetsRoutes({ router }: SetupR // GET /assets/diff router.get( { - path: `${ASSET_MANAGER_API_BASE}/assets/diff`, + path: GET_ASSETS_DIFF, validate: { query: createRouteValidationFunction(getAssetsDiffQueryOptionsRT), }, @@ -187,11 +187,11 @@ export function assetsRoutes({ router }: SetupR }); } - const esClient = await getEsClientFromContext(context); + const { elasticsearchClient } = await getClientsFromContext(context); try { const resultsForA = await getAssets({ - esClient, + elasticsearchClient, filters: { from: aFrom, to: aTo, @@ -201,7 +201,7 @@ export function assetsRoutes({ router }: SetupR }); const resultsForB = await getAssets({ - esClient, + elasticsearchClient, filters: { from: bFrom, to: bTo, diff --git a/x-pack/plugins/asset_manager/server/routes/assets/services.ts b/x-pack/plugins/asset_manager/server/routes/assets/services.ts index d7edf3b6f7f3c..3852a0bb60d11 100644 --- a/x-pack/plugins/asset_manager/server/routes/assets/services.ts +++ b/x-pack/plugins/asset_manager/server/routes/assets/services.ts @@ -17,8 +17,8 @@ import { import { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; import { debug } from '../../../common/debug_log'; import { SetupRouteOptions } from '../types'; -import { ASSET_MANAGER_API_BASE } from '../../constants'; -import { getEsClientFromContext } from '../utils'; +import { ASSET_MANAGER_API_BASE } from '../../../common/constants_routes'; +import { getClientsFromContext } from '../utils'; const sizeRT = rt.union([inRangeFromStringRt(1, 100), createLiteralValueFromUndefinedRT(10)]); const assetDateRT = rt.union([dateRt, datemathStringRt]); @@ -35,7 +35,7 @@ export type GetServiceAssetsQueryOptions = rt.TypeOf({ router, - assetAccessor, + assetClient, }: SetupRouteOptions) { // GET /assets/services router.get( @@ -47,16 +47,14 @@ export function servicesRoutes({ }, async (context, req, res) => { const { from = 'now-24h', to = 'now', parent } = req.query || {}; - const esClient = await getEsClientFromContext(context); - const coreContext = await context.core; - const soClient = coreContext.savedObjects.client; + const { elasticsearchClient, savedObjectsClient } = await getClientsFromContext(context); try { - const response = await assetAccessor.getServices({ + const response = await assetClient.getServices({ from: datemath.parse(from)!.toISOString(), to: datemath.parse(to)!.toISOString(), parent, - esClient, - soClient, + elasticsearchClient, + savedObjectsClient, }); return res.ok({ body: response }); diff --git a/x-pack/plugins/asset_manager/server/routes/index.ts b/x-pack/plugins/asset_manager/server/routes/index.ts index cab0b1558fa00..30064a8562b6f 100644 --- a/x-pack/plugins/asset_manager/server/routes/index.ts +++ b/x-pack/plugins/asset_manager/server/routes/index.ts @@ -15,11 +15,11 @@ import { servicesRoutes } from './assets/services'; export function setupRoutes({ router, - assetAccessor, + assetClient, }: SetupRouteOptions) { - pingRoute({ router, assetAccessor }); - assetsRoutes({ router, assetAccessor }); - sampleAssetsRoutes({ router, assetAccessor }); - hostsRoutes({ router, assetAccessor }); - servicesRoutes({ router, assetAccessor }); + pingRoute({ router, assetClient }); + assetsRoutes({ router, assetClient }); + sampleAssetsRoutes({ router, assetClient }); + hostsRoutes({ router, assetClient }); + servicesRoutes({ router, assetClient }); } diff --git a/x-pack/plugins/asset_manager/server/routes/ping.ts b/x-pack/plugins/asset_manager/server/routes/ping.ts index 3f7b1bb679b98..3d7a20b5fd476 100644 --- a/x-pack/plugins/asset_manager/server/routes/ping.ts +++ b/x-pack/plugins/asset_manager/server/routes/ping.ts @@ -6,7 +6,7 @@ */ import { RequestHandlerContextBase } from '@kbn/core-http-server'; -import { ASSET_MANAGER_API_BASE } from '../constants'; +import { ASSET_MANAGER_API_BASE } from '../../common/constants_routes'; import { SetupRouteOptions } from './types'; export function pingRoute({ router }: SetupRouteOptions) { diff --git a/x-pack/plugins/asset_manager/server/routes/sample_assets.ts b/x-pack/plugins/asset_manager/server/routes/sample_assets.ts index 98f7f32051f3f..447051bbb2730 100644 --- a/x-pack/plugins/asset_manager/server/routes/sample_assets.ts +++ b/x-pack/plugins/asset_manager/server/routes/sample_assets.ts @@ -7,11 +7,11 @@ import { schema } from '@kbn/config-schema'; import { RequestHandlerContext } from '@kbn/core/server'; -import { ASSET_MANAGER_API_BASE } from '../constants'; +import { ASSET_MANAGER_API_BASE } from '../../common/constants_routes'; import { getSampleAssetDocs, sampleAssets } from '../lib/sample_assets'; import { writeAssets } from '../lib/write_assets'; import { SetupRouteOptions } from './types'; -import { getEsClientFromContext } from './utils'; +import { getClientsFromContext } from './utils'; export type WriteSamplesPostBody = { baseDateTime?: string | number; @@ -62,12 +62,12 @@ export function sampleAssetsRoutes({ }, }); } - const esClient = await getEsClientFromContext(context); + const { elasticsearchClient } = await getClientsFromContext(context); const assetDocs = getSampleAssetDocs({ baseDateTime: parsed, excludeEans }); try { const response = await writeAssets({ - esClient, + elasticsearchClient, assetDocs, namespace: 'sample_data', refresh, @@ -101,9 +101,9 @@ export function sampleAssetsRoutes({ validate: {}, }, async (context, req, res) => { - const esClient = await getEsClientFromContext(context); + const { elasticsearchClient } = await getClientsFromContext(context); - const sampleDataStreams = await esClient.indices.getDataStream({ + const sampleDataStreams = await elasticsearchClient.indices.getDataStream({ name: 'assets-*-sample_data', expand_wildcards: 'all', }); @@ -115,7 +115,7 @@ export function sampleAssetsRoutes({ for (let i = 0; i < dataStreamsToDelete.length; i++) { const dsName = dataStreamsToDelete[i]; try { - await esClient.indices.deleteDataStream({ name: dsName }); + await elasticsearchClient.indices.deleteDataStream({ name: dsName }); deletedDataStreams.push(dsName); } catch (error: any) { errorWhileDeleting = diff --git a/x-pack/plugins/asset_manager/server/routes/types.ts b/x-pack/plugins/asset_manager/server/routes/types.ts index 2a0cf91f47df7..ae1b967a5b596 100644 --- a/x-pack/plugins/asset_manager/server/routes/types.ts +++ b/x-pack/plugins/asset_manager/server/routes/types.ts @@ -6,9 +6,9 @@ */ import { IRouter, RequestHandlerContextBase } from '@kbn/core-http-server'; -import { AssetAccessor } from '../lib/asset_accessor'; +import { AssetClient } from '../lib/asset_client'; export interface SetupRouteOptions { router: IRouter; - assetAccessor: AssetAccessor; + assetClient: AssetClient; } diff --git a/x-pack/plugins/asset_manager/server/routes/utils.ts b/x-pack/plugins/asset_manager/server/routes/utils.ts index 378ed0b48fc87..665adc0917fa0 100644 --- a/x-pack/plugins/asset_manager/server/routes/utils.ts +++ b/x-pack/plugins/asset_manager/server/routes/utils.ts @@ -7,6 +7,12 @@ import { RequestHandlerContext } from '@kbn/core/server'; -export async function getEsClientFromContext(context: T) { - return (await context.core).elasticsearch.client.asCurrentUser; +export async function getClientsFromContext(context: T) { + const coreContext = await context.core; + + return { + coreContext, + elasticsearchClient: coreContext.elasticsearch.client.asCurrentUser, + savedObjectsClient: coreContext.savedObjects.client, + }; } diff --git a/x-pack/plugins/asset_manager/server/types.ts b/x-pack/plugins/asset_manager/server/types.ts index 380d48aa0c7fe..431378c7c9a9f 100644 --- a/x-pack/plugins/asset_manager/server/types.ts +++ b/x-pack/plugins/asset_manager/server/types.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { ElasticsearchClient } from '@kbn/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import { ApmDataAccessPluginSetup, ApmDataAccessPluginStart, @@ -14,37 +13,9 @@ import { import { MetricsDataPluginSetup } from '@kbn/metrics-data-access-plugin/server'; export interface ElasticsearchAccessorOptions { - esClient: ElasticsearchClient; + elasticsearchClient: ElasticsearchClient; } -export const INDEX_DEFAULTS = { - logs: 'filebeat-*,logs-*', -}; - -export const configSchema = schema.object({ - alphaEnabled: schema.maybe(schema.boolean()), - // Designate where various types of data live. - // NOTE: this should be handled in a centralized way for observability, so - // that when a user configures these differently from the known defaults, - // that value is propagated everywhere. For now, we duplicate the value here. - sourceIndices: schema.object( - { - logs: schema.string({ defaultValue: INDEX_DEFAULTS.logs }), - }, - { defaultValue: INDEX_DEFAULTS } - ), - // Choose an explicit source for asset queries. - // NOTE: This will eventually need to be able to cleverly switch - // between these values based on the availability of data in the - // indices, and possibly for each asset kind/type value. - // For now, we set this explicitly. - lockedSource: schema.oneOf([schema.literal('assets'), schema.literal('signals')], { - defaultValue: 'signals', - }), -}); - -export type AssetManagerConfig = TypeOf; - export interface AssetManagerPluginSetupDependencies { apmDataAccess: ApmDataAccessPluginSetup; metricsDataAccess: MetricsDataPluginSetup; @@ -52,3 +23,8 @@ export interface AssetManagerPluginSetupDependencies { export interface AssetManagerPluginStartDependencies { apmDataAccess: ApmDataAccessPluginStart; } + +export interface AssetClientDependencies { + elasticsearchClient: ElasticsearchClient; + savedObjectsClient: SavedObjectsClientContract; +} diff --git a/x-pack/plugins/asset_manager/tsconfig.json b/x-pack/plugins/asset_manager/tsconfig.json index d7663856f0513..35972189e5287 100644 --- a/x-pack/plugins/asset_manager/tsconfig.json +++ b/x-pack/plugins/asset_manager/tsconfig.json @@ -7,6 +7,7 @@ "../../../typings/**/*", "common/**/*", "server/**/*", + "public/**/*", "types/**/*" ], "exclude": ["target/**/*"], @@ -17,10 +18,11 @@ "@kbn/core-http-server", "@kbn/core-elasticsearch-client-server-mocks", "@kbn/io-ts-utils", - "@kbn/core-elasticsearch-server", "@kbn/core-http-request-handler-context-server", "@kbn/datemath", "@kbn/apm-data-access-plugin", + "@kbn/core-http-browser-mocks", + "@kbn/logging", "@kbn/metrics-data-access-plugin" ] }