mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Fleet] Remove dependency on public saved objects HTTP APIs (#158350)
## Summary Remove dependency on public saved objects HTTP APIs - [x] Removed usage of `SimpleSavedObject` type used for integration assets [accordion](d694a0d75f/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx
) - [x] Removed usage of `savedObjectsClient` and `ResolvedSimpleSavedObject` on integration assets page. Added a new Fleet API endpoint to retrieve the necessary assets information and related frontend hook `sendGetBulkAssets` to replace the savedObject client ``` POST kbn:/api/fleet/epm/bulk_assets { assetIds: [ { type: 'dashboard', id: 'sample_dashboard', }, { type: 'visualization', id: 'sample_visualization', }, ], } ``` - [x] Removed deprecated public saved objects typings in `EPM` and `Settings` types. These types are currently used in the following endpoints, they both make use of `savedObject` field: - GET `api/fleet/epm/packages/{pkgName}/{version}`: response schema uses [PackageInfo](ff8cebb407/x-pack/plugins/fleet/common/types/models/epm.ts (L466-L468)
) type. - GET `api/fleet/epm/packages`: response schema uses [PackageList](8d1afc5c97/x-pack/plugins/fleet/common/types/models/epm.ts (L438-L442)
) type My proposed solution is to remove the types imported from `core` and mark the use of `savedObject` field as a deprecation, since they are currently exposed through those endpoints (and used in lots of places in the code). ### Checklist Delete any items that are not applicable to this PR. - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
005108684b
commit
177914bcaf
51 changed files with 720 additions and 248 deletions
|
@ -4,6 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { AllowedAssetTypes } from '../types/models';
|
||||
import { ElasticsearchAssetType, KibanaAssetType } from '../types/models';
|
||||
|
||||
export const PACKAGES_SAVED_OBJECT_TYPE = 'epm-packages';
|
||||
export const ASSETS_SAVED_OBJECT_TYPE = 'epm-packages-assets';
|
||||
|
@ -79,3 +81,12 @@ export const installationStatuses = {
|
|||
InstallFailed: 'install_failed',
|
||||
NotInstalled: 'not_installed',
|
||||
} as const;
|
||||
|
||||
export const allowedAssetTypes: AllowedAssetTypes = [
|
||||
KibanaAssetType.dashboard,
|
||||
KibanaAssetType.search,
|
||||
KibanaAssetType.visualization,
|
||||
ElasticsearchAssetType.transform,
|
||||
];
|
||||
|
||||
export const allowedAssetTypesLookup = new Set<string>(allowedAssetTypes);
|
||||
|
|
|
@ -39,6 +39,7 @@ export const EPM_API_ROUTES = {
|
|||
CATEGORIES_PATTERN: `${EPM_API_ROOT}/categories`,
|
||||
VERIFICATION_KEY_ID: `${EPM_API_ROOT}/verification_key_id`,
|
||||
STATS_PATTERN: `${EPM_PACKAGES_MANY}/{pkgName}/stats`,
|
||||
BULK_ASSETS_PATTERN: `${EPM_API_ROOT}/bulk_assets`,
|
||||
|
||||
INFO_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
|
||||
INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED: EPM_PACKAGES_ONE_DEPRECATED,
|
||||
|
|
|
@ -115,6 +115,7 @@ export type {
|
|||
UpgradePackagePolicyDryRunResponseItem,
|
||||
BulkGetPackagePoliciesResponse,
|
||||
BulkGetAgentPoliciesResponse,
|
||||
GetBulkAssetsResponse,
|
||||
// Models
|
||||
Agent,
|
||||
AgentStatus,
|
||||
|
@ -191,6 +192,9 @@ export type {
|
|||
// Fleet server models
|
||||
FleetServerAgent,
|
||||
FleetServerAgentComponentStatus,
|
||||
AssetSOObject,
|
||||
SimpleSOAssetType,
|
||||
AllowedAssetTypes,
|
||||
} from './types';
|
||||
|
||||
export { ElasticsearchAssetType } from './types';
|
||||
|
|
|
@ -307,6 +307,59 @@
|
|||
},
|
||||
"parameters": []
|
||||
},
|
||||
"/epm/bulk_assets": {
|
||||
"post": {
|
||||
"summary": "Bulk get assets",
|
||||
"tags": [
|
||||
"Elastic Package Manager (EPM)"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/get_bulk_assets_response"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/error"
|
||||
}
|
||||
},
|
||||
"operationId": "bulk-get-assets",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetIds": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "list of items necessary to fetch assets"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetIds"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/epm/categories": {
|
||||
"get": {
|
||||
"summary": "List package categories",
|
||||
|
@ -5557,6 +5610,82 @@
|
|||
"item"
|
||||
]
|
||||
},
|
||||
"saved_object_type": {
|
||||
"title": "Saved Object type",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"dashboard",
|
||||
"visualization",
|
||||
"search",
|
||||
"index_pattern",
|
||||
"map",
|
||||
"lens",
|
||||
"security_rule",
|
||||
"csp_rule_template",
|
||||
"ml_module",
|
||||
"tag",
|
||||
"osquery_pack_asset",
|
||||
"osquery_saved_query"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"index",
|
||||
"component_template",
|
||||
"ingest_pipeline",
|
||||
"index_template",
|
||||
"ilm_policy",
|
||||
"transform",
|
||||
"data_stream_ilm_policy",
|
||||
"ml_model"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"get_bulk_assets_response": {
|
||||
"title": "Bulk get assets response",
|
||||
"type": "object",
|
||||
"deprecated": true,
|
||||
"properties": {
|
||||
"response": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/saved_object_type"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"items"
|
||||
]
|
||||
},
|
||||
"get_categories_response": {
|
||||
"title": "Get categories response",
|
||||
"type": "object",
|
||||
|
|
|
@ -191,6 +191,39 @@ paths:
|
|||
$ref: '#/components/responses/error'
|
||||
operationId: packages-get-verification-key-id
|
||||
parameters: []
|
||||
/epm/bulk_assets:
|
||||
post:
|
||||
summary: Bulk get assets
|
||||
tags:
|
||||
- Elastic Package Manager (EPM)
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/get_bulk_assets_response'
|
||||
'400':
|
||||
$ref: '#/components/responses/error'
|
||||
operationId: bulk-get-assets
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
assetIds:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
description: list of items necessary to fetch assets
|
||||
required:
|
||||
- assetIds
|
||||
/epm/categories:
|
||||
get:
|
||||
summary: List package categories
|
||||
|
@ -3478,6 +3511,60 @@ components:
|
|||
$ref: '#/components/schemas/settings'
|
||||
required:
|
||||
- item
|
||||
saved_object_type:
|
||||
title: Saved Object type
|
||||
oneOf:
|
||||
- type: string
|
||||
enum:
|
||||
- dashboard
|
||||
- visualization
|
||||
- search
|
||||
- index_pattern
|
||||
- map
|
||||
- lens
|
||||
- security_rule
|
||||
- csp_rule_template
|
||||
- ml_module
|
||||
- tag
|
||||
- osquery_pack_asset
|
||||
- osquery_saved_query
|
||||
- type: string
|
||||
enum:
|
||||
- index
|
||||
- component_template
|
||||
- ingest_pipeline
|
||||
- index_template
|
||||
- ilm_policy
|
||||
- transform
|
||||
- data_stream_ilm_policy
|
||||
- ml_model
|
||||
get_bulk_assets_response:
|
||||
title: Bulk get assets response
|
||||
type: object
|
||||
deprecated: true
|
||||
properties:
|
||||
response:
|
||||
type: array
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/components/schemas/saved_object_type'
|
||||
updatedAt:
|
||||
type: string
|
||||
attributes:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- items
|
||||
get_categories_response:
|
||||
title: Get categories response
|
||||
type: object
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
title: Bulk get assets response
|
||||
type: object
|
||||
deprecated: true
|
||||
properties:
|
||||
response:
|
||||
type: array
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
$ref: ./saved_object_type.yaml
|
||||
updatedAt:
|
||||
type: string
|
||||
attributes:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- items
|
|
@ -0,0 +1,26 @@
|
|||
title: Saved Object type
|
||||
oneOf:
|
||||
- type: string
|
||||
enum:
|
||||
- dashboard
|
||||
- visualization
|
||||
- search
|
||||
- index_pattern
|
||||
- map
|
||||
- lens
|
||||
- security_rule
|
||||
- csp_rule_template
|
||||
- ml_module
|
||||
- tag
|
||||
- osquery_pack_asset
|
||||
- osquery_saved_query
|
||||
- type: string
|
||||
enum:
|
||||
- index
|
||||
- component_template
|
||||
- ingest_pipeline
|
||||
- index_template
|
||||
- ilm_policy
|
||||
- transform
|
||||
- data_stream_ilm_policy
|
||||
- ml_model
|
|
@ -28,6 +28,8 @@ paths:
|
|||
# EPM / integrations endpoints
|
||||
/epm/verification_key_id:
|
||||
$ref: paths/epm@verification_key_id.yaml
|
||||
/epm/bulk_assets:
|
||||
$ref: paths/epm@bulk_assets.yaml
|
||||
/epm/categories:
|
||||
$ref: paths/epm@categories.yaml
|
||||
/epm/packages/limited:
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
post:
|
||||
summary: Bulk get assets
|
||||
tags:
|
||||
- Elastic Package Manager (EPM)
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: ../components/schemas/get_bulk_assets_response.yaml
|
||||
'400':
|
||||
$ref: ../components/responses/error.yaml
|
||||
operationId: bulk-get-assets
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
assetIds:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
description: list of items necessary to fetch assets
|
||||
required:
|
||||
- assetIds
|
|
@ -89,6 +89,9 @@ export const epmRouteService = {
|
|||
.replace('{pkgVersion}', pkgVersion)
|
||||
.replace(/\/$/, ''); // trim trailing slash
|
||||
},
|
||||
getBulkAssetsPath: () => {
|
||||
return EPM_API_ROUTES.BULK_ASSETS_PATTERN;
|
||||
},
|
||||
};
|
||||
|
||||
export const packagePolicyRouteService = {
|
||||
|
|
|
@ -14,6 +14,3 @@ export interface DownloadSourceBase {
|
|||
export type DownloadSource = DownloadSourceBase & {
|
||||
id: string;
|
||||
};
|
||||
export type DownloadSourceAttributes = DownloadSourceBase & {
|
||||
source_id?: string;
|
||||
};
|
||||
|
|
|
@ -6,11 +6,6 @@
|
|||
*/
|
||||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
// Follow pattern from https://github.com/elastic/kibana/pull/52447
|
||||
// TODO: Update when https://github.com/elastic/kibana/issues/53021 is closed
|
||||
import type { SavedObject, SavedObjectAttributes, SavedObjectReference } from '@kbn/core/public';
|
||||
|
||||
import type { CustomIntegrationIcon } from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
import type {
|
||||
ASSETS_SAVED_OBJECT_TYPE,
|
||||
|
@ -21,12 +16,7 @@ import type {
|
|||
} from '../../constants';
|
||||
import type { ValueOf } from '..';
|
||||
|
||||
import type {
|
||||
PackageSpecManifest,
|
||||
PackageSpecIcon,
|
||||
PackageSpecScreenshot,
|
||||
PackageSpecCategory,
|
||||
} from './package_spec';
|
||||
import type { PackageSpecManifest, PackageSpecIcon, PackageSpecCategory } from './package_spec';
|
||||
|
||||
export type InstallationStatus = typeof installationStatuses;
|
||||
|
||||
|
@ -48,13 +38,6 @@ export type InstallSource = 'registry' | 'upload' | 'bundled';
|
|||
|
||||
export type EpmPackageInstallStatus = 'installed' | 'installing' | 'install_failed';
|
||||
|
||||
export type DetailViewPanelName =
|
||||
| 'overview'
|
||||
| 'policies'
|
||||
| 'assets'
|
||||
| 'settings'
|
||||
| 'custom'
|
||||
| 'api-reference';
|
||||
export type ServiceName = 'kibana' | 'elasticsearch';
|
||||
export type AgentAssetType = typeof agentAssetTypes;
|
||||
export type DocAssetType = 'doc' | 'notice' | 'license';
|
||||
|
@ -115,6 +98,29 @@ export type FleetElasticsearchAssetType = Exclude<
|
|||
ElasticsearchAssetType.index
|
||||
>;
|
||||
|
||||
export type AllowedAssetTypes = [
|
||||
KibanaAssetType.dashboard,
|
||||
KibanaAssetType.search,
|
||||
KibanaAssetType.visualization,
|
||||
ElasticsearchAssetType.transform
|
||||
];
|
||||
|
||||
// Defined as part of the removing public references to saved object schemas
|
||||
export interface SimpleSOAssetType {
|
||||
id: string;
|
||||
type: ElasticsearchAssetType | KibanaSavedObjectType;
|
||||
updatedAt?: string;
|
||||
attributes: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssetSOObject {
|
||||
id: string;
|
||||
type: ElasticsearchAssetType | KibanaSavedObjectType;
|
||||
}
|
||||
|
||||
export type DataType = typeof dataTypes;
|
||||
export type MonitoringType = typeof monitoringTypes;
|
||||
export type InstallablePackage = RegistryPackage | ArchivePackage;
|
||||
|
@ -251,12 +257,6 @@ export interface RegistryStream {
|
|||
|
||||
export type RegistryStreamWithDataStream = RegistryStream & { data_stream: RegistryDataStream };
|
||||
|
||||
export type RequirementVersion = string;
|
||||
export type RequirementVersionRange = string;
|
||||
export interface ServiceRequirements {
|
||||
versions: RequirementVersionRange;
|
||||
}
|
||||
|
||||
// Registry's response types
|
||||
// from /search
|
||||
// https://github.com/elastic/package-registry/blob/master/docs/api/search.json
|
||||
|
@ -279,8 +279,6 @@ export type RegistrySearchResult = Pick<
|
|||
| 'categories'
|
||||
>;
|
||||
|
||||
export type ScreenshotItem = RegistryImage | PackageSpecScreenshot;
|
||||
|
||||
// from /categories
|
||||
// https://github.com/elastic/package-registry/blob/master/docs/api/categories.json
|
||||
export type CategorySummaryList = CategorySummaryItem[];
|
||||
|
@ -420,6 +418,34 @@ export interface RegistryVarsEntry {
|
|||
};
|
||||
}
|
||||
|
||||
// Deprecated as part of the removing public references to saved object schemas
|
||||
// See https://github.com/elastic/kibana/issues/149098
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export interface InstallableSavedObject {
|
||||
type: string;
|
||||
id: string;
|
||||
attributes: Installation;
|
||||
references?: SOReference[];
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
version?: string;
|
||||
coreMigrationVersion?: string;
|
||||
namespaces?: string[];
|
||||
}
|
||||
|
||||
// Deprecated as part of the removing public references to saved object schemas
|
||||
// See https://github.com/elastic/kibana/issues/149098
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
interface SOReference {
|
||||
name: string;
|
||||
type: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
// some properties are optional in Registry responses but required in EPM
|
||||
// internal until we need them
|
||||
export interface EpmPackageAdditions {
|
||||
|
@ -437,36 +463,19 @@ type Merge<FirstType, SecondType> = Omit<FirstType, Extract<keyof FirstType, key
|
|||
// Managers public HTTP response types
|
||||
export type PackageList = PackageListItem[];
|
||||
export type PackageListItem = Installable<RegistrySearchResult> & {
|
||||
id: string;
|
||||
integration?: string;
|
||||
id: string;
|
||||
savedObject?: InstallableSavedObject;
|
||||
};
|
||||
|
||||
export type IntegrationCardReleaseLabel = 'beta' | 'preview' | 'ga' | 'rc';
|
||||
|
||||
export interface IntegrationCardItem {
|
||||
url: string;
|
||||
release?: IntegrationCardReleaseLabel;
|
||||
description: string;
|
||||
name: string;
|
||||
title: string;
|
||||
version: string;
|
||||
icons: Array<PackageSpecIcon | CustomIntegrationIcon>;
|
||||
integration: string;
|
||||
id: string;
|
||||
categories: string[];
|
||||
fromIntegrations?: string;
|
||||
isReauthorizationRequired?: boolean;
|
||||
isUnverified?: boolean;
|
||||
isUpdateAvailable?: boolean;
|
||||
showLabels?: boolean;
|
||||
}
|
||||
|
||||
export type PackageVerificationStatus = 'verified' | 'unverified' | 'unknown';
|
||||
export type PackagesGroupedByStatus = Record<ValueOf<InstallationStatus>, PackageList>;
|
||||
export type PackageInfo =
|
||||
| Installable<Merge<RegistryPackage, EpmPackageAdditions>>
|
||||
| Installable<Merge<ArchivePackage, EpmPackageAdditions>>;
|
||||
|
||||
export type IntegrationCardReleaseLabel = 'beta' | 'preview' | 'ga' | 'rc';
|
||||
|
||||
export type PackageVerificationStatus = 'verified' | 'unverified' | 'unknown';
|
||||
|
||||
// TODO - Expand this with other experimental indexing types
|
||||
export type ExperimentalIndexingFeature =
|
||||
| 'synthetic_source'
|
||||
|
@ -479,7 +488,7 @@ export interface ExperimentalDataStreamFeature {
|
|||
features: Partial<Record<ExperimentalIndexingFeature, boolean>>;
|
||||
}
|
||||
|
||||
export interface Installation extends SavedObjectAttributes {
|
||||
export interface Installation {
|
||||
installed_kibana: KibanaAssetReference[];
|
||||
installed_es: EsAssetReference[];
|
||||
package_assets?: PackageAssetReference[];
|
||||
|
@ -500,8 +509,9 @@ export interface Installation extends SavedObjectAttributes {
|
|||
data_stream: string;
|
||||
features: Partial<Record<ExperimentalIndexingFeature, boolean>>;
|
||||
}>;
|
||||
internal?: boolean;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
export interface PackageUsageStats {
|
||||
agent_policy_count: number;
|
||||
}
|
||||
|
@ -519,12 +529,12 @@ export type InstallStatusExcluded<T = {}> = T & {
|
|||
|
||||
export type InstalledRegistry<T = {}> = T & {
|
||||
status: InstallationStatus['Installed'];
|
||||
savedObject: SavedObject<Installation>;
|
||||
savedObject?: InstallableSavedObject;
|
||||
};
|
||||
|
||||
export type Installing<T = {}> = T & {
|
||||
status: InstallationStatus['Installing'];
|
||||
savedObject: SavedObject<Installation>;
|
||||
savedObject?: InstallableSavedObject;
|
||||
};
|
||||
|
||||
export type NotInstalled<T = {}> = T & {
|
||||
|
@ -537,17 +547,20 @@ export type InstallFailed<T = {}> = T & {
|
|||
|
||||
export type AssetReference = KibanaAssetReference | EsAssetReference;
|
||||
|
||||
export type KibanaAssetReference = Pick<SavedObjectReference, 'id'> & {
|
||||
export interface KibanaAssetReference {
|
||||
id: string;
|
||||
type: KibanaSavedObjectType;
|
||||
};
|
||||
export type EsAssetReference = Pick<SavedObjectReference, 'id'> & {
|
||||
}
|
||||
export interface EsAssetReference {
|
||||
id: string;
|
||||
type: ElasticsearchAssetType;
|
||||
deferred?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type PackageAssetReference = Pick<SavedObjectReference, 'id'> & {
|
||||
export interface PackageAssetReference {
|
||||
id: string;
|
||||
type: typeof ASSETS_SAVED_OBJECT_TYPE;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IndexTemplateMappings {
|
||||
properties: any;
|
||||
|
|
|
@ -16,6 +16,8 @@ import type {
|
|||
InstallType,
|
||||
InstallSource,
|
||||
EpmPackageInstallStatus,
|
||||
SimpleSOAssetType,
|
||||
AssetSOObject,
|
||||
} from '../models/epm';
|
||||
|
||||
export interface GetCategoriesRequest {
|
||||
|
@ -197,3 +199,13 @@ export interface DeletePackageResponse {
|
|||
export interface GetVerificationKeyIdResponse {
|
||||
id: string | null;
|
||||
}
|
||||
|
||||
export interface GetBulkAssetsRequest {
|
||||
body: {
|
||||
assetIds: AssetSOObject[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetBulkAssetsResponse {
|
||||
items: SimpleSOAssetType[];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { Search as LocalSearch, PrefixIndexStrategy } from 'js-search';
|
||||
import { useRef } from 'react';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../common/types/models';
|
||||
import type { IntegrationCardItem } from '../sections/epm/screens/home';
|
||||
|
||||
export const searchIdField = 'id';
|
||||
export const fieldsToSearch = ['name', 'title', 'description'];
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from '../screens/detail/assets/deferred_assets_warning';
|
||||
|
||||
import { CardIcon } from '../../../../../components/package_icon';
|
||||
import type { IntegrationCardItem } from '../../../../../../common/types/models/epm';
|
||||
import type { IntegrationCardItem } from '../screens/home';
|
||||
|
||||
import { InlineReleaseBadge, WithGuidedOnboardingTour } from '../../../components';
|
||||
import { useStartServices, useIsGuidedOnboardingActive } from '../../../hooks';
|
||||
|
|
|
@ -23,7 +23,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import { Loading } from '../../../../components';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types/models';
|
||||
import type { IntegrationCardItem } from '../../screens/home';
|
||||
|
||||
import type { ExtendedIntegrationCategory } from '../../screens/home/category_facets';
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import { useLocalSearch, searchIdField } from '../../../../hooks';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types/models';
|
||||
import type { IntegrationCardItem } from '../../screens/home';
|
||||
|
||||
import type {
|
||||
ExtendedIntegrationCategory,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types';
|
||||
import type { IntegrationCardItem } from '../../screens/home';
|
||||
|
||||
import { _promoteFeaturedIntegrations } from './promote_featured_integrations';
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import type { RequirementVersion } from '../../../types';
|
||||
export type RequirementVersion = string;
|
||||
export type RequirementVersionRange = string;
|
||||
|
||||
const CodeText = styled.span`
|
||||
font-family: ${(props) => props.theme.eui.euiCodeFontFamily};
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { groupBy } from 'lodash';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiCallOut } from '@elastic/eui';
|
||||
|
||||
import type { ResolvedSimpleSavedObject } from '@kbn/core/public';
|
||||
|
||||
import type { EsAssetReference } from '../../../../../../../../common';
|
||||
import type {
|
||||
EsAssetReference,
|
||||
AssetSOObject,
|
||||
SimpleSOAssetType,
|
||||
} from '../../../../../../../../common';
|
||||
import { allowedAssetTypes } from '../../../../../../../../common/constants';
|
||||
|
||||
import { Error, ExtensionWrapper, Loading } from '../../../../../components';
|
||||
|
||||
|
@ -25,16 +27,15 @@ import {
|
|||
useLink,
|
||||
useStartServices,
|
||||
useUIExtension,
|
||||
useAuthz,
|
||||
} from '../../../../../hooks';
|
||||
|
||||
import { sendGetBulkAssets } from '../../../../../hooks';
|
||||
|
||||
import { DeferredAssetsSection } from './deferred_assets_accordion';
|
||||
|
||||
import type { AssetSavedObject } from './types';
|
||||
import { allowedAssetTypes } from './constants';
|
||||
import { AssetsAccordion } from './assets_accordion';
|
||||
|
||||
const allowedAssetTypesLookup = new Set<string>(allowedAssetTypes);
|
||||
|
||||
interface AssetsPanelProps {
|
||||
packageInfo: PackageInfo;
|
||||
}
|
||||
|
@ -42,31 +43,30 @@ interface AssetsPanelProps {
|
|||
export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
|
||||
const { name, version } = packageInfo;
|
||||
const pkgkey = `${name}-${version}`;
|
||||
const {
|
||||
spaces,
|
||||
savedObjects: { client: savedObjectsClient },
|
||||
} = useStartServices();
|
||||
const { spaces } = useStartServices();
|
||||
const customAssetsExtension = useUIExtension(packageInfo.name, 'package-detail-assets');
|
||||
|
||||
const canReadPackageSettings = useAuthz().integrations.readPackageSettings;
|
||||
|
||||
const { getPath } = useLink();
|
||||
const getPackageInstallStatus = useGetPackageInstallStatus();
|
||||
const packageInstallStatus = getPackageInstallStatus(packageInfo.name);
|
||||
|
||||
// assume assets are installed in this space until we find otherwise
|
||||
const [assetsInstalledInCurrentSpace, setAssetsInstalledInCurrentSpace] = useState<boolean>(true);
|
||||
const [assetSavedObjects, setAssetsSavedObjects] = useState<undefined | AssetSavedObject[]>();
|
||||
const [assetSavedObjects, setAssetsSavedObjects] = useState<undefined | SimpleSOAssetType[]>();
|
||||
const [deferredInstallations, setDeferredInstallations] = useState<EsAssetReference[]>();
|
||||
|
||||
const [fetchError, setFetchError] = useState<undefined | Error>();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [hasPermissionError, setHasPermissionError] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAssetSavedObjects = async () => {
|
||||
if ('savedObject' in packageInfo) {
|
||||
if (spaces) {
|
||||
const { id: spaceId } = await spaces.getActiveSpace();
|
||||
const assetInstallSpaceId = packageInfo.savedObject.attributes.installed_kibana_space_id;
|
||||
const assetInstallSpaceId = packageInfo.savedObject?.attributes.installed_kibana_space_id;
|
||||
|
||||
// if assets are installed in a different space no need to attempt to load them.
|
||||
if (assetInstallSpaceId && assetInstallSpaceId !== spaceId) {
|
||||
setAssetsInstalledInCurrentSpace(false);
|
||||
|
@ -75,71 +75,40 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
|
|||
}
|
||||
}
|
||||
|
||||
const {
|
||||
savedObject: { attributes: packageAttributes },
|
||||
} = packageInfo;
|
||||
const packageAttributes = packageInfo.savedObject?.attributes;
|
||||
|
||||
if (
|
||||
packageAttributes?.installed_es &&
|
||||
Array.isArray(packageAttributes.installed_es) &&
|
||||
packageAttributes.installed_es?.length > 0
|
||||
packageAttributes.installed_es.length > 0
|
||||
) {
|
||||
const deferredAssets = packageAttributes.installed_es.filter(
|
||||
(asset) => asset.deferred === true
|
||||
);
|
||||
setDeferredInstallations(deferredAssets);
|
||||
}
|
||||
|
||||
const authorizedTransforms = packageAttributes.installed_es.filter(
|
||||
const authorizedTransforms = (packageAttributes?.installed_es || []).filter(
|
||||
(asset) => asset.type === ElasticsearchAssetType.transform && !asset.deferred
|
||||
);
|
||||
|
||||
if (
|
||||
authorizedTransforms.length === 0 &&
|
||||
(!packageAttributes.installed_kibana || packageAttributes.installed_kibana.length === 0)
|
||||
authorizedTransforms?.length === 0 &&
|
||||
(!packageAttributes?.installed_kibana || packageAttributes.installed_kibana.length === 0)
|
||||
) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const objectsToGet = [...authorizedTransforms, ...packageAttributes.installed_kibana].map(
|
||||
({ id, type }) => ({
|
||||
id,
|
||||
type,
|
||||
})
|
||||
);
|
||||
// We don't have an API to know which SO types a user has access to, so instead we make a request for each
|
||||
// SO type and ignore the 403 errors
|
||||
const objectsByType = await Promise.all(
|
||||
Object.entries(groupBy(objectsToGet, 'type')).map(([type, objects]) =>
|
||||
savedObjectsClient
|
||||
.bulkResolve(objects)
|
||||
// Ignore privilege errors
|
||||
.catch((e: any) => {
|
||||
if (e?.body?.statusCode === 403) {
|
||||
setHasPermissionError(true);
|
||||
return { resolved_objects: [] };
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.then(
|
||||
({
|
||||
resolved_objects: resolvedObjects,
|
||||
}: {
|
||||
resolved_objects: ResolvedSimpleSavedObject[];
|
||||
}) => {
|
||||
return resolvedObjects
|
||||
.map(({ saved_object: savedObject }) => savedObject)
|
||||
.filter(
|
||||
(savedObject) =>
|
||||
savedObject?.error?.statusCode !== 404 &&
|
||||
allowedAssetTypesLookup.has(savedObject.type)
|
||||
) as AssetSavedObject[];
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
setAssetsSavedObjects([...objectsByType.flat()]);
|
||||
const assetIds: AssetSOObject[] = [
|
||||
...authorizedTransforms,
|
||||
...(packageAttributes?.installed_kibana || []),
|
||||
].map(({ id, type }) => ({
|
||||
id,
|
||||
type,
|
||||
}));
|
||||
|
||||
const response = await sendGetBulkAssets({ assetIds });
|
||||
setAssetsSavedObjects(response.data?.items);
|
||||
} catch (e) {
|
||||
setFetchError(e);
|
||||
} finally {
|
||||
|
@ -150,7 +119,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
|
|||
}
|
||||
};
|
||||
fetchAssetSavedObjects();
|
||||
}, [savedObjectsClient, packageInfo, spaces]);
|
||||
}, [packageInfo, spaces]);
|
||||
|
||||
// if they arrive at this page and the package is not installed, send them to overview
|
||||
// this happens if they arrive with a direct url or they uninstall while on this tab
|
||||
|
@ -160,10 +129,26 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
|
|||
|
||||
const showDeferredInstallations =
|
||||
Array.isArray(deferredInstallations) && deferredInstallations.length > 0;
|
||||
|
||||
let content: JSX.Element | Array<JSX.Element | null> | null;
|
||||
if (isLoading) {
|
||||
content = <Loading />;
|
||||
} else if (!canReadPackageSettings) {
|
||||
content = (
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epm.packageDetails.assets.assetsPermissionErrorTitle"
|
||||
defaultMessage="Permission error"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epm.packageDetails.assets.assetsPermissionError"
|
||||
defaultMessage="You do not have permission to retrieve the Kibana saved object for that integration. Contact your administrator."
|
||||
/>
|
||||
</EuiCallOut>
|
||||
);
|
||||
} else if (fetchError) {
|
||||
content = (
|
||||
<Error
|
||||
|
@ -187,23 +172,6 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
|
|||
</h2>
|
||||
</EuiTitle>
|
||||
);
|
||||
} else if (hasPermissionError) {
|
||||
content = (
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epm.packageDetails.assets.assetsPermissionErrorTitle"
|
||||
defaultMessage="Permission errors"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epm.packageDetails.assets.assetsPermissionError"
|
||||
defaultMessage="You do not have permission to retrieve the Kibana saved object for that integration. Contact your administrator."
|
||||
/>
|
||||
</EuiCallOut>
|
||||
);
|
||||
} else if (assetSavedObjects === undefined || assetSavedObjects.length === 0) {
|
||||
if (customAssetsExtension) {
|
||||
// If a UI extension for custom asset entries is defined, render the custom component here despite
|
||||
|
|
|
@ -23,15 +23,16 @@ import {
|
|||
|
||||
import { AssetTitleMap } from '../../../constants';
|
||||
|
||||
import type { SimpleSOAssetType, AllowedAssetTypes } from '../../../../../../../../common';
|
||||
|
||||
import { getHrefToObjectInKibanaApp, useStartServices } from '../../../../../hooks';
|
||||
|
||||
import { ElasticsearchAssetType, KibanaAssetType } from '../../../../../types';
|
||||
|
||||
import type { AllowedAssetType, AssetSavedObject } from './types';
|
||||
|
||||
export type AllowedAssetType = AllowedAssetTypes[number] | 'view';
|
||||
interface Props {
|
||||
type: AllowedAssetType;
|
||||
savedObjects: AssetSavedObject[];
|
||||
savedObjects: SimpleSOAssetType[];
|
||||
}
|
||||
|
||||
export const AssetsAccordion: FunctionComponent<Props> = ({ savedObjects, type }) => {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* 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 { ElasticsearchAssetType, KibanaAssetType } from '../../../../../types';
|
||||
|
||||
import type { AllowedAssetTypes } from './types';
|
||||
|
||||
export const allowedAssetTypes: AllowedAssetTypes = [
|
||||
KibanaAssetType.dashboard,
|
||||
KibanaAssetType.search,
|
||||
KibanaAssetType.visualization,
|
||||
ElasticsearchAssetType.transform,
|
||||
];
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* 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 { SimpleSavedObject } from '@kbn/core/public';
|
||||
|
||||
import type { KibanaAssetType } from '../../../../../types';
|
||||
import type { ElasticsearchAssetType } from '../../../../../types';
|
||||
|
||||
export type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>;
|
||||
|
||||
export type AllowedAssetTypes = [
|
||||
KibanaAssetType.dashboard,
|
||||
KibanaAssetType.search,
|
||||
KibanaAssetType.visualization,
|
||||
ElasticsearchAssetType.transform
|
||||
];
|
||||
|
||||
export type AllowedAssetType = AllowedAssetTypes[number] | 'view';
|
|
@ -20,10 +20,7 @@ import type {
|
|||
GetSettingsResponse,
|
||||
GetVerificationKeyIdResponse,
|
||||
} from '../../../../../../../common/types/rest_spec';
|
||||
import type {
|
||||
DetailViewPanelName,
|
||||
KibanaAssetType,
|
||||
} from '../../../../../../../common/types/models';
|
||||
import type { KibanaAssetType } from '../../../../../../../common/types/models';
|
||||
import {
|
||||
agentPolicyRouteService,
|
||||
appRoutesService,
|
||||
|
@ -36,9 +33,11 @@ import { createIntegrationsTestRendererMock } from '../../../../../../mock';
|
|||
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
import type { DetailViewPanelName } from '.';
|
||||
import { Detail } from '.';
|
||||
|
||||
// @ts-ignore this saves us having to define all experimental features
|
||||
ExperimentalFeaturesService.init({});
|
||||
import { Detail } from '.';
|
||||
|
||||
describe('when on integration detail', () => {
|
||||
const pkgkey = 'nginx-0.3.7';
|
||||
|
|
|
@ -56,7 +56,7 @@ import {
|
|||
useIsGuidedOnboardingActive,
|
||||
} from '../../../../hooks';
|
||||
import { pkgKeyFromPackageInfo } from '../../../../services';
|
||||
import type { DetailViewPanelName, PackageInfo } from '../../../../types';
|
||||
import type { PackageInfo } from '../../../../types';
|
||||
import { InstallStatus } from '../../../../types';
|
||||
import {
|
||||
Error,
|
||||
|
@ -86,6 +86,14 @@ import { DocumentationPage } from './documentation';
|
|||
|
||||
import './index.scss';
|
||||
|
||||
export type DetailViewPanelName =
|
||||
| 'overview'
|
||||
| 'policies'
|
||||
| 'assets'
|
||||
| 'settings'
|
||||
| 'custom'
|
||||
| 'api-reference';
|
||||
|
||||
export interface DetailParams {
|
||||
pkgkey: string;
|
||||
panel?: DetailViewPanelName;
|
||||
|
@ -249,7 +257,7 @@ export function Detail() {
|
|||
let installedVersion;
|
||||
const { name } = packageInfoData.item;
|
||||
if ('savedObject' in packageInfoResponse) {
|
||||
installedVersion = packageInfoResponse.savedObject.attributes.version;
|
||||
installedVersion = packageInfoResponse.savedObject?.attributes.version;
|
||||
}
|
||||
const status: InstallStatus = packageInfoResponse?.status as any;
|
||||
if (name) {
|
||||
|
|
|
@ -10,9 +10,10 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiText, EuiPagination } from '@elastic/eui';
|
||||
|
||||
import type { ScreenshotItem } from '../../../../../types';
|
||||
import type { RegistryImage, PackageSpecScreenshot } from '../../../../../../../../common/types';
|
||||
import { useLinks } from '../../../../../hooks';
|
||||
|
||||
type ScreenshotItem = RegistryImage | PackageSpecScreenshot;
|
||||
interface ScreenshotProps {
|
||||
images: ScreenshotItem[];
|
||||
packageName: string;
|
||||
|
|
|
@ -455,7 +455,8 @@ export const SettingsPage: React.FC<Props> = memo(({ packageInfo, theme$ }: Prop
|
|||
<ReinstallButton
|
||||
{...packageInfo}
|
||||
installSource={
|
||||
'savedObject' in packageInfo
|
||||
'savedObject' in packageInfo &&
|
||||
packageInfo.savedObject?.attributes.install_source
|
||||
? packageInfo.savedObject.attributes.install_source
|
||||
: ''
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
isIntegrationPolicyTemplate,
|
||||
} from '../../../../../../../../common/services';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../../common/types/models';
|
||||
import type { IntegrationCardItem } from '..';
|
||||
|
||||
import { ALL_CATEGORY } from '../category_facets';
|
||||
import type { CategoryFacet } from '../category_facets';
|
||||
|
|
|
@ -9,12 +9,19 @@ import React, { useState } from 'react';
|
|||
import { Switch } from 'react-router-dom';
|
||||
import { Route } from '@kbn/shared-ux-router';
|
||||
|
||||
import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common';
|
||||
import type {
|
||||
CustomIntegration,
|
||||
CustomIntegrationIcon,
|
||||
} from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
import { hasDeferredInstallations } from '../../../../../../services/has_deferred_installations';
|
||||
import { getPackageReleaseLabel } from '../../../../../../../common/services';
|
||||
|
||||
import { installationStatuses } from '../../../../../../../common/constants';
|
||||
import type {
|
||||
PackageSpecIcon,
|
||||
IntegrationCardReleaseLabel,
|
||||
} from '../../../../../../../common/types';
|
||||
|
||||
import type { DynamicPage, DynamicPagePathValues, StaticPage } from '../../../../constants';
|
||||
import { INTEGRATIONS_ROUTING_PATHS, INTEGRATIONS_SEARCH_QUERYPARAM } from '../../../../constants';
|
||||
|
@ -23,11 +30,6 @@ import { isPackageUnverified, isPackageUpdatable } from '../../../../services';
|
|||
|
||||
import type { PackageListItem } from '../../../../types';
|
||||
|
||||
import type {
|
||||
IntegrationCardItem,
|
||||
IntegrationCardReleaseLabel,
|
||||
} from '../../../../../../../common/types/models';
|
||||
|
||||
import { useGetPackagesQuery } from '../../../../hooks';
|
||||
|
||||
import type { CategoryFacet, ExtendedIntegrationCategory } from './category_facets';
|
||||
|
@ -40,6 +42,24 @@ export interface CategoryParams {
|
|||
subcategory?: string;
|
||||
}
|
||||
|
||||
export interface IntegrationCardItem {
|
||||
url: string;
|
||||
release?: IntegrationCardReleaseLabel;
|
||||
description: string;
|
||||
name: string;
|
||||
title: string;
|
||||
version: string;
|
||||
icons: Array<PackageSpecIcon | CustomIntegrationIcon>;
|
||||
integration: string;
|
||||
id: string;
|
||||
categories: string[];
|
||||
fromIntegrations?: string;
|
||||
isReauthorizationRequired?: boolean;
|
||||
isUnverified?: boolean;
|
||||
isUpdateAvailable?: boolean;
|
||||
showLabels?: boolean;
|
||||
}
|
||||
|
||||
export const getParams = (params: CategoryParams, search: string) => {
|
||||
const { category, subcategory } = params;
|
||||
const selectedCategory: ExtendedIntegrationCategory = category || '';
|
||||
|
@ -81,7 +101,7 @@ export const mapToCard = ({
|
|||
: item.uiExternalLink || getAbsolutePath(item.uiInternalPath);
|
||||
} else {
|
||||
let urlVersion = item.version;
|
||||
if ('savedObject' in item) {
|
||||
if ('savedObject' in item && item?.savedObject?.attributes.version) {
|
||||
urlVersion = item.savedObject.attributes.version || item.version;
|
||||
isUnverified = isPackageUnverified(item, packageVerificationKeyId);
|
||||
isUpdateAvailable = isPackageUpdatable(item);
|
||||
|
@ -130,7 +150,10 @@ export const EPMHomePage: React.FC = () => {
|
|||
);
|
||||
|
||||
const unverifiedPackageCount = installedPackages.filter(
|
||||
(pkg) => 'savedObject' in pkg && pkg.savedObject.attributes.verification_status === 'unverified'
|
||||
(pkg) =>
|
||||
'savedObject' in pkg &&
|
||||
pkg.savedObject?.attributes.verification_status &&
|
||||
pkg.savedObject.attributes.verification_status === 'unverified'
|
||||
).length;
|
||||
|
||||
const upgradeablePackageCount = installedPackages.filter(isPackageUpdatable).length;
|
||||
|
|
|
@ -171,7 +171,8 @@ export const InstalledPackages: React.FC<{
|
|||
() =>
|
||||
installedPackages.filter(
|
||||
(item) =>
|
||||
'savedObject' in item && semverLt(item.savedObject.attributes.version, item.version)
|
||||
item.savedObject?.attributes.version &&
|
||||
semverLt(item.savedObject.attributes.version, item.version)
|
||||
),
|
||||
[installedPackages]
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { IntegrationCategory } from '@kbn/custom-integrations-plugin/common';
|
||||
import { INTEGRATION_CATEGORY_DISPLAY } from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types/models';
|
||||
import type { IntegrationCardItem } from '.';
|
||||
|
||||
import type { CategoryFacet } from './category_facets';
|
||||
|
||||
|
|
|
@ -31,11 +31,10 @@ export const useIsPackagePolicyUpgradable = () => {
|
|||
const { name, version } = pkgPolicy.package;
|
||||
const installedPackage = allInstalledPackages.find(
|
||||
(installedPkg) =>
|
||||
'savedObject' in installedPkg && installedPkg.savedObject.attributes.name === name
|
||||
'savedObject' in installedPkg && installedPkg.savedObject?.attributes.name === name
|
||||
);
|
||||
if (
|
||||
installedPackage &&
|
||||
'savedObject' in installedPackage &&
|
||||
installedPackage?.savedObject?.attributes.version &&
|
||||
semverLt(version, installedPackage.savedObject.attributes.version)
|
||||
) {
|
||||
return true;
|
||||
|
|
|
@ -44,7 +44,9 @@ export const usePackageInstallationsQuery = () => {
|
|||
() =>
|
||||
allInstalledPackages.filter(
|
||||
(item) =>
|
||||
'savedObject' in item && semverLt(item.savedObject.attributes.version, item.version)
|
||||
'savedObject' in item &&
|
||||
item.savedObject?.attributes.version &&
|
||||
semverLt(item.savedObject.attributes.version, item.version)
|
||||
),
|
||||
[allInstalledPackages]
|
||||
);
|
||||
|
@ -57,11 +59,12 @@ export const usePackageInstallationsQuery = () => {
|
|||
const { name, version } = pkgPolicy.package;
|
||||
const installedPackage = allInstalledPackages.find(
|
||||
(installedPkg) =>
|
||||
'savedObject' in installedPkg && installedPkg.savedObject.attributes.name === name
|
||||
'savedObject' in installedPkg && installedPkg?.savedObject?.attributes.name === name
|
||||
);
|
||||
if (
|
||||
installedPackage &&
|
||||
'savedObject' in installedPackage &&
|
||||
installedPackage?.savedObject?.attributes?.version &&
|
||||
semverLt(version, installedPackage.savedObject.attributes.version)
|
||||
) {
|
||||
const packageData = result.get(name) ?? {
|
||||
|
|
|
@ -24,6 +24,8 @@ import type {
|
|||
DeletePackageResponse,
|
||||
UpdatePackageRequest,
|
||||
UpdatePackageResponse,
|
||||
GetBulkAssetsRequest,
|
||||
GetBulkAssetsResponse,
|
||||
GetVerificationKeyIdResponse,
|
||||
} from '../../types';
|
||||
import type { FleetErrorResponse, GetStatsResponse } from '../../../common/types';
|
||||
|
@ -266,3 +268,11 @@ export const sendUpdatePackage = (
|
|||
body,
|
||||
});
|
||||
};
|
||||
|
||||
export const sendGetBulkAssets = (body: GetBulkAssetsRequest['body']) => {
|
||||
return sendRequest<GetBulkAssetsResponse>({
|
||||
path: epmRouteService.getBulkAssetsPath(),
|
||||
method: 'post',
|
||||
body,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import type { PackageInfo, PackageListItem } from '../../common';
|
||||
|
||||
export const getDeferredInstallationsCnt = (pkg?: PackageInfo | PackageListItem | null): number => {
|
||||
if (!pkg) return 0;
|
||||
|
||||
return pkg && 'savedObject' in pkg && pkg.savedObject
|
||||
? pkg.savedObject.attributes?.installed_es?.filter((d) => d.deferred).length
|
||||
: 0;
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
import semverLt from 'semver/functions/lt';
|
||||
|
||||
import type { PackageInfo, PackageListItem } from '../types';
|
||||
import type { PackageListItem } from '../types';
|
||||
|
||||
export const isPackageUpdatable = (pkg: PackageInfo | PackageListItem): boolean =>
|
||||
'savedObject' in pkg &&
|
||||
pkg.savedObject &&
|
||||
semverLt(pkg.savedObject.attributes.version, pkg.version);
|
||||
export const isPackageUpdatable = (pkg: PackageListItem): boolean =>
|
||||
'savedObject' in pkg && pkg.savedObject?.attributes.version
|
||||
? semverLt(pkg.savedObject.attributes.version, pkg.version)
|
||||
: false;
|
||||
|
|
|
@ -17,10 +17,10 @@ export function isPackageUnverified(
|
|||
pkg: PackageInfo | PackageListItem,
|
||||
packageVerificationKeyId?: string
|
||||
) {
|
||||
if (!('savedObject' in pkg)) return false;
|
||||
if (!('savedObject' in pkg) || !pkg.savedObject?.attributes) return false;
|
||||
|
||||
const { verification_status: verificationStatus, verification_key_id: verificationKeyId } =
|
||||
pkg.savedObject.attributes;
|
||||
pkg?.savedObject?.attributes;
|
||||
|
||||
const { packageVerification: isPackageVerificationEnabled } = ExperimentalFeaturesService.get();
|
||||
const isKeyOutdated = !!verificationKeyId && verificationKeyId !== packageVerificationKeyId;
|
||||
|
|
|
@ -108,8 +108,6 @@ export type {
|
|||
PackageListItem,
|
||||
PackagesGroupedByStatus,
|
||||
RequirementsByServiceName,
|
||||
RequirementVersion,
|
||||
ScreenshotItem,
|
||||
ServiceName,
|
||||
GetCategoriesRequest,
|
||||
GetCategoriesResponse,
|
||||
|
@ -120,7 +118,6 @@ export type {
|
|||
GetInfoResponse,
|
||||
InstallPackageResponse,
|
||||
DeletePackageResponse,
|
||||
DetailViewPanelName,
|
||||
InstallationStatus,
|
||||
Installable,
|
||||
RegistryRelease,
|
||||
|
@ -135,6 +132,8 @@ export type {
|
|||
PostHealthCheckResponse,
|
||||
PostRetrieveAgentsByActionsRequest,
|
||||
PostRetrieveAgentsByActionsResponse,
|
||||
GetBulkAssetsRequest,
|
||||
GetBulkAssetsResponse,
|
||||
} from '../../common/types';
|
||||
export {
|
||||
entries,
|
||||
|
|
|
@ -13,7 +13,6 @@ import semverValid from 'semver/functions/valid';
|
|||
import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server';
|
||||
|
||||
import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header';
|
||||
|
||||
import { generateTransformSecondaryAuthHeaders } from '../../services/api_keys/transform_api_keys';
|
||||
import { handleTransformReauthorizeAndStart } from '../../services/epm/elasticsearch/transform/reauthorize';
|
||||
|
||||
|
@ -30,6 +29,8 @@ import type {
|
|||
GetStatsResponse,
|
||||
UpdatePackageResponse,
|
||||
GetVerificationKeyIdResponse,
|
||||
GetBulkAssetsResponse,
|
||||
SimpleSOAssetType,
|
||||
GetInstalledPackagesResponse,
|
||||
GetEpmDataStreamsResponse,
|
||||
} from '../../../common/types';
|
||||
|
@ -48,6 +49,7 @@ import type {
|
|||
FleetRequestHandler,
|
||||
UpdatePackageRequestSchema,
|
||||
GetLimitedPackagesRequestSchema,
|
||||
GetBulkAssetsRequestSchema,
|
||||
} from '../../types';
|
||||
import {
|
||||
bulkInstallPackages,
|
||||
|
@ -70,8 +72,10 @@ import { getAsset } from '../../services/epm/archive/storage';
|
|||
import { getPackageUsageStats } from '../../services/epm/packages/get';
|
||||
import { updatePackage } from '../../services/epm/packages/update';
|
||||
import { getGpgKeyIdOrUndefined } from '../../services/epm/packages/package_verification';
|
||||
import type { ReauthorizeTransformRequestSchema } from '../../types';
|
||||
import type { ReauthorizeTransformRequestSchema, SimpleSOAssetAttributes } from '../../types';
|
||||
import type { KibanaSavedObjectType, ElasticsearchAssetType } from '../../../common/types/models';
|
||||
import { getDataStreams } from '../../services/epm/data_streams';
|
||||
import { allowedAssetTypesLookup } from '../../../common/constants';
|
||||
|
||||
const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = {
|
||||
'cache-control': 'max-age=600',
|
||||
|
@ -287,6 +291,7 @@ export const getInfoHandler: FleetRequestHandler<
|
|||
ignoreUnverified,
|
||||
prerelease,
|
||||
});
|
||||
|
||||
const body: GetInfoResponse = {
|
||||
item: res,
|
||||
};
|
||||
|
@ -296,6 +301,43 @@ export const getInfoHandler: FleetRequestHandler<
|
|||
}
|
||||
};
|
||||
|
||||
export const getBulkAssetsHandler: FleetRequestHandler<
|
||||
undefined,
|
||||
undefined,
|
||||
TypeOf<typeof GetBulkAssetsRequestSchema.body>
|
||||
> = async (context, request, response) => {
|
||||
try {
|
||||
const savedObjectsClient = (await context.fleet).internalSoClient;
|
||||
const { assetIds } = request.body;
|
||||
|
||||
const { resolved_objects: resolvedObjects } =
|
||||
await savedObjectsClient.bulkResolve<SimpleSOAssetAttributes>(assetIds);
|
||||
const res: SimpleSOAssetType[] = resolvedObjects
|
||||
.map(({ saved_object: savedObject }) => savedObject)
|
||||
.filter(
|
||||
(savedObject) =>
|
||||
savedObject?.error?.statusCode !== 404 && allowedAssetTypesLookup.has(savedObject.type)
|
||||
)
|
||||
.map((obj) => {
|
||||
return {
|
||||
id: obj.id,
|
||||
type: obj.type as unknown as ElasticsearchAssetType | KibanaSavedObjectType,
|
||||
updatedAt: obj.updated_at,
|
||||
attributes: {
|
||||
title: obj.attributes.title,
|
||||
description: obj.attributes.description,
|
||||
},
|
||||
};
|
||||
});
|
||||
const body: GetBulkAssetsResponse = {
|
||||
items: res,
|
||||
};
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
return defaultFleetErrorHandler({ error, response });
|
||||
}
|
||||
};
|
||||
|
||||
export const updatePackageHandler: FleetRequestHandler<
|
||||
TypeOf<typeof UpdatePackageRequestSchema.params>,
|
||||
unknown,
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
GetFileRequestSchema,
|
||||
GetInfoRequestSchema,
|
||||
GetInfoRequestSchemaDeprecated,
|
||||
GetBulkAssetsRequestSchema,
|
||||
InstallPackageFromRegistryRequestSchema,
|
||||
InstallPackageFromRegistryRequestSchemaDeprecated,
|
||||
InstallPackageByUploadRequestSchema,
|
||||
|
@ -51,6 +52,7 @@ import {
|
|||
getLimitedListHandler,
|
||||
getFileHandler,
|
||||
getInfoHandler,
|
||||
getBulkAssetsHandler,
|
||||
installPackageFromRegistryHandler,
|
||||
installPackageByUploadHandler,
|
||||
deletePackageHandler,
|
||||
|
@ -227,6 +229,17 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
|
|||
getDataStreamsHandler
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: EPM_API_ROUTES.BULK_ASSETS_PATTERN,
|
||||
validate: GetBulkAssetsRequestSchema,
|
||||
fleetAuthz: {
|
||||
integrations: { readPackageInfo: true },
|
||||
},
|
||||
},
|
||||
getBulkAssetsHandler
|
||||
);
|
||||
|
||||
// deprecated since 8.0
|
||||
router.get(
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ import { savedObjectsClientMock } from '@kbn/core/server/mocks';
|
|||
|
||||
import { securityMock } from '@kbn/security-plugin/server/mocks';
|
||||
|
||||
import type { DownloadSourceAttributes } from '../types';
|
||||
import type { DownloadSourceSOAttributes } from '../types';
|
||||
|
||||
import { DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE } from '../constants';
|
||||
|
||||
|
@ -158,7 +158,7 @@ describe('Download Service', () => {
|
|||
|
||||
// ID should always be the same for a predefined id
|
||||
expect(soClient.create.mock.calls[0][2]?.id).toEqual('download-source-test');
|
||||
expect((soClient.create.mock.calls[0][1] as DownloadSourceAttributes).source_id).toEqual(
|
||||
expect((soClient.create.mock.calls[0][1] as DownloadSourceSOAttributes).source_id).toEqual(
|
||||
'download-source-test'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
DEFAULT_DOWNLOAD_SOURCE_ID,
|
||||
} from '../constants';
|
||||
|
||||
import type { DownloadSource, DownloadSourceAttributes, DownloadSourceBase } from '../types';
|
||||
import type { DownloadSource, DownloadSourceSOAttributes, DownloadSourceBase } from '../types';
|
||||
import { DownloadSourceError, FleetError } from '../errors';
|
||||
import { SO_SEARCH_LIMIT } from '../../common';
|
||||
|
||||
|
@ -20,7 +20,7 @@ import { agentPolicyService } from './agent_policy';
|
|||
import { appContextService } from './app_context';
|
||||
import { escapeSearchQueryPhrase } from './saved_object';
|
||||
|
||||
function savedObjectToDownloadSource(so: SavedObject<DownloadSourceAttributes>) {
|
||||
function savedObjectToDownloadSource(so: SavedObject<DownloadSourceSOAttributes>) {
|
||||
const { source_id: sourceId, ...attributes } = so.attributes;
|
||||
|
||||
return {
|
||||
|
@ -31,7 +31,7 @@ function savedObjectToDownloadSource(so: SavedObject<DownloadSourceAttributes>)
|
|||
|
||||
class DownloadSourceService {
|
||||
public async get(soClient: SavedObjectsClientContract, id: string): Promise<DownloadSource> {
|
||||
const soResponse = await soClient.get<DownloadSourceAttributes>(
|
||||
const soResponse = await soClient.get<DownloadSourceSOAttributes>(
|
||||
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
id
|
||||
);
|
||||
|
@ -44,7 +44,7 @@ class DownloadSourceService {
|
|||
}
|
||||
|
||||
public async list(soClient: SavedObjectsClientContract) {
|
||||
const downloadSources = await soClient.find<DownloadSourceAttributes>({
|
||||
const downloadSources = await soClient.find<DownloadSourceSOAttributes>({
|
||||
type: DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
page: 1,
|
||||
perPage: SO_SEARCH_LIMIT,
|
||||
|
@ -65,7 +65,7 @@ class DownloadSourceService {
|
|||
downloadSource: DownloadSourceBase,
|
||||
options?: { id?: string; overwrite?: boolean }
|
||||
): Promise<DownloadSource> {
|
||||
const data: DownloadSourceAttributes = downloadSource;
|
||||
const data: DownloadSourceSOAttributes = downloadSource;
|
||||
|
||||
await this.requireUniqueName(soClient, {
|
||||
name: downloadSource.name,
|
||||
|
@ -84,7 +84,7 @@ class DownloadSourceService {
|
|||
data.source_id = options?.id;
|
||||
}
|
||||
|
||||
const newSo = await soClient.create<DownloadSourceAttributes>(
|
||||
const newSo = await soClient.create<DownloadSourceSOAttributes>(
|
||||
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
data,
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ class DownloadSourceService {
|
|||
id: string,
|
||||
newData: Partial<DownloadSource>
|
||||
) {
|
||||
const updateData: Partial<DownloadSourceAttributes> = newData;
|
||||
const updateData: Partial<DownloadSourceSOAttributes> = newData;
|
||||
|
||||
if (newData.name) {
|
||||
await this.requireUniqueName(soClient, {
|
||||
|
@ -117,7 +117,7 @@ class DownloadSourceService {
|
|||
await this.update(soClient, defaultDownloadSourceId, { is_default: false });
|
||||
}
|
||||
}
|
||||
const soResponse = await soClient.update<DownloadSourceAttributes>(
|
||||
const soResponse = await soClient.update<DownloadSourceSOAttributes>(
|
||||
DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
id,
|
||||
updateData
|
||||
|
@ -183,7 +183,7 @@ class DownloadSourceService {
|
|||
soClient: SavedObjectsClientContract,
|
||||
downloadSource: { name: string; id?: string }
|
||||
) {
|
||||
const results = await soClient.find<DownloadSourceAttributes>({
|
||||
const results = await soClient.find<DownloadSourceSOAttributes>({
|
||||
type: DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
searchFields: ['name'],
|
||||
search: escapeSearchQueryPhrase(downloadSource.name),
|
||||
|
@ -205,7 +205,7 @@ class DownloadSourceService {
|
|||
}
|
||||
|
||||
private async _getDefaultDownloadSourceSO(soClient: SavedObjectsClientContract) {
|
||||
return await soClient.find<DownloadSourceAttributes>({
|
||||
return await soClient.find<DownloadSourceSOAttributes>({
|
||||
type: DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE,
|
||||
searchFields: ['is_default'],
|
||||
search: 'true',
|
||||
|
|
|
@ -27,6 +27,7 @@ import type {
|
|||
PackageUsageStats,
|
||||
Installable,
|
||||
PackageDataStreamTypes,
|
||||
PackageList,
|
||||
} from '../../../../common/types';
|
||||
import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants';
|
||||
import type {
|
||||
|
@ -146,7 +147,7 @@ export async function getPackages(
|
|||
return newPkg;
|
||||
});
|
||||
|
||||
return packageListWithoutStatus;
|
||||
return packageListWithoutStatus as PackageList;
|
||||
}
|
||||
|
||||
interface GetInstalledPackagesOptions {
|
||||
|
|
|
@ -17,7 +17,6 @@ const mockInstallation: SavedObject<Installation> = {
|
|||
references: [],
|
||||
type: 'epm-packages',
|
||||
attributes: {
|
||||
id: 'test-pkg',
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
|
||||
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
|
||||
|
@ -38,7 +37,6 @@ const mockInstallationUpdateFail: SavedObject<Installation> = {
|
|||
references: [],
|
||||
type: 'epm-packages',
|
||||
attributes: {
|
||||
id: 'test-pkg',
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
|
||||
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
|
||||
|
|
|
@ -78,7 +78,6 @@ export type {
|
|||
FullAgentPolicyInputStream,
|
||||
DownloadSourceBase,
|
||||
DownloadSource,
|
||||
DownloadSourceAttributes,
|
||||
PackageVerificationStatus,
|
||||
BulkInstallPackageInfo,
|
||||
PackageAssetReference,
|
||||
|
|
|
@ -89,6 +89,12 @@ export const GetInfoRequestSchema = {
|
|||
}),
|
||||
};
|
||||
|
||||
export const GetBulkAssetsRequestSchema = {
|
||||
body: schema.object({
|
||||
assetIds: schema.arrayOf(schema.object({ id: schema.string(), type: schema.string() })),
|
||||
}),
|
||||
};
|
||||
|
||||
export const GetInfoRequestSchemaDeprecated = {
|
||||
params: schema.object({
|
||||
pkgkey: schema.string(),
|
||||
|
|
|
@ -137,7 +137,18 @@ export interface OutputSOAttributes {
|
|||
}
|
||||
|
||||
export interface SettingsSOAttributes {
|
||||
prerelease_integrations_enabled: boolean;
|
||||
has_seen_add_data_notice?: boolean;
|
||||
fleet_server_hosts?: string[];
|
||||
prerelease_integrations_enabled: boolean;
|
||||
}
|
||||
|
||||
export interface DownloadSourceSOAttributes {
|
||||
name: string;
|
||||
host: string;
|
||||
is_default: boolean;
|
||||
source_id?: string;
|
||||
}
|
||||
export interface SimpleSOAssetAttributes {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ export const createInstalledIntegrationSet = (): IInstalledIntegrationSet => {
|
|||
}
|
||||
|
||||
// Actual `installed_version` is buried in SO, root `version` is latest package version available
|
||||
const installedPackageVersion = fleetPackage.savedObject.attributes.install_version;
|
||||
const installedPackageVersion = fleetPackage.savedObject?.attributes.install_version || '';
|
||||
|
||||
// Policy templates correspond to package's integrations.
|
||||
const packagePolicyTemplates = fleetPackage.policy_templates ?? [];
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { GetBulkAssetsResponse } from '@kbn/fleet-plugin/common';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
import { setupFleetAndAgents } from '../agents/services';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
const supertest = getService('supertest');
|
||||
const dockerServers = getService('dockerServers');
|
||||
const server = dockerServers.get('registry');
|
||||
const pkgName = 'all_assets';
|
||||
const pkgVersion = '0.1.0';
|
||||
|
||||
const uninstallPackage = async (pkg: string, version: string) => {
|
||||
await supertest.delete(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx');
|
||||
};
|
||||
const installPackage = async (pkg: string, version: string) => {
|
||||
await supertest
|
||||
.post(`/api/fleet/epm/packages/${pkg}/${version}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ force: true });
|
||||
};
|
||||
|
||||
describe('Bulk get assets', async () => {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
setupFleetAndAgents(providerContext);
|
||||
|
||||
describe('installs all assets when installing a package for the first time', async () => {
|
||||
before(async () => {
|
||||
if (!server.enabled) return;
|
||||
await installPackage(pkgName, pkgVersion);
|
||||
});
|
||||
after(async () => {
|
||||
if (!server.enabled) return;
|
||||
await uninstallPackage(pkgName, pkgVersion);
|
||||
});
|
||||
|
||||
it('should get the assets based on the required objects', async () => {
|
||||
const { body }: { body: GetBulkAssetsResponse } = await supertest
|
||||
.post(`/api/fleet/epm/bulk_assets`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
assetIds: [
|
||||
{
|
||||
type: 'dashboard',
|
||||
id: 'sample_dashboard',
|
||||
},
|
||||
{
|
||||
id: 'sample_visualization',
|
||||
type: 'visualization',
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
const asset1 = body.items[0];
|
||||
expect(asset1.id).to.equal('sample_dashboard');
|
||||
expect(asset1.type).to.equal('dashboard');
|
||||
expect(asset1.attributes).to.eql({
|
||||
title: '[Logs Sample] Overview ECS',
|
||||
description: 'Sample dashboard',
|
||||
});
|
||||
|
||||
const asset2 = body.items[1];
|
||||
expect(asset2.id).to.equal('sample_visualization');
|
||||
expect(asset2.type).to.equal('visualization');
|
||||
expect(asset2.attributes).to.eql({
|
||||
title: 'sample vis title',
|
||||
description: 'sample visualization update',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -41,5 +41,6 @@ export default function loadTests({ loadTestFile, getService }) {
|
|||
loadTestFile(require.resolve('./verification_key_id'));
|
||||
loadTestFile(require.resolve('./install_integration_in_multiple_spaces.ts'));
|
||||
loadTestFile(require.resolve('./install_hidden_datastreams'));
|
||||
loadTestFile(require.resolve('./bulk_get_assets'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
.get(`/api/fleet/epm/packages/endpoint/${latestEndpointVersion}`)
|
||||
.expect(200));
|
||||
expect(body.item).to.have.property('savedObject');
|
||||
expect((body.item as InstalledRegistry).savedObject.attributes.install_version).to.eql(
|
||||
expect((body.item as InstalledRegistry).savedObject?.attributes.install_version).to.eql(
|
||||
latestEndpointVersion
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue