Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiTitle,
EuiToolTip,
useEuiTheme,
} from '@elastic/eui';
Expand Down Expand Up @@ -67,6 +68,7 @@ export function PackageCard({
onCardClick: onClickProp = undefined,
isCollectionCard = false,
titleLineClamp,
titleBadge,
descriptionLineClamp,
maxCardHeight,
minCardHeight,
Expand Down Expand Up @@ -225,6 +227,7 @@ export function PackageCard({
}

[class*='euiCard__titleButton'] {
width: 100%;
${getLineClampStyles(titleLineClamp)}
}

Expand All @@ -236,7 +239,7 @@ export function PackageCard({
data-test-subj={testid}
betaBadgeProps={quickstartBadge(isQuickstart)}
layout="horizontal"
title={title || ''}
title={<CardTitle title={title} titleBadge={titleBadge} />}
titleSize="xs"
description={showDescription ? description : ''}
hasBorder
Expand Down Expand Up @@ -272,6 +275,30 @@ export function PackageCard({
);
}

const CardTitle = React.memo<Pick<IntegrationCardItem, 'title' | 'titleBadge'>>(
({ title, titleBadge }) => {
if (!titleBadge) {
return title;
}
return (
<EuiFlexGroup
direction="row"
alignItems="flexStart"
justifyContent="spaceBetween"
gutterSize="s"
responsive={false}
>
<EuiFlexItem>
<EuiTitle size="xs">
<h3>{title}</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>{titleBadge}</EuiFlexItem>
</EuiFlexGroup>
);
}
);

function quickstartBadge(isQuickstart: boolean): { label: string; color: 'accent' } | undefined {
return isQuickstart
? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface IntegrationCardItem {
title: string;
// Security Solution uses this prop to determine how many lines the card title should be truncated
titleLineClamp?: number;
titleBadge?: React.ReactNode;
url: string;
version: string;
type?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ import type {
GetRuleMigrationRequestParamsInput,
GetRuleMigrationResponse,
GetRuleMigrationIntegrationsResponse,
GetRuleMigrationIntegrationsStatsResponse,
GetRuleMigrationPrebuiltRulesRequestParamsInput,
GetRuleMigrationPrebuiltRulesResponse,
GetRuleMigrationPrivilegesResponse,
Expand Down Expand Up @@ -1560,6 +1561,21 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Retrieves the stats of all the integrations for all the rule migrations, including the number of rules associated with the integration
*/
async getRuleMigrationIntegrationsStats() {
this.log.info(`${new Date().toISOString()} Calling API GetRuleMigrationIntegrationsStats`);
return this.kbnClient
.request<GetRuleMigrationIntegrationsStatsResponse>({
path: '/internal/siem_migrations/rules/integrations/stats',
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '1',
},
method: 'GET',
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Retrieves all available prebuilt rules (installed and installable)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const SIEM_RULE_MIGRATIONS_PATH = `${SIEM_MIGRATIONS_PATH}/rules` as cons
export const SIEM_RULE_MIGRATIONS_ALL_STATS_PATH = `${SIEM_RULE_MIGRATIONS_PATH}/stats` as const;
export const SIEM_RULE_MIGRATIONS_INTEGRATIONS_PATH =
`${SIEM_RULE_MIGRATIONS_PATH}/integrations` as const;
export const SIEM_RULE_MIGRATIONS_INTEGRATIONS_STATS_PATH =
`${SIEM_RULE_MIGRATIONS_PATH}/integrations/stats` as const;
export const SIEM_RULE_MIGRATION_PATH = `${SIEM_RULE_MIGRATIONS_PATH}/{migration_id}` as const;
export const SIEM_RULE_MIGRATION_RULES_PATH = `${SIEM_RULE_MIGRATION_PATH}/rules` as const;
export const SIEM_RULE_MIGRATION_START_PATH = `${SIEM_RULE_MIGRATION_PATH}/start` as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ArrayFromString, BooleanFromString } from '@kbn/zod-helpers';

import {
RuleMigrationTaskStats,
RuleMigrationAllIntegrationsStats,
RuleMigration,
OriginalRule,
RuleMigrationRule,
Expand Down Expand Up @@ -89,6 +90,11 @@ export type GetRuleMigrationIntegrationsResponse = z.infer<
>;
export const GetRuleMigrationIntegrationsResponse = z.object({}).catchall(RelatedIntegration);

export type GetRuleMigrationIntegrationsStatsResponse = z.infer<
typeof GetRuleMigrationIntegrationsStatsResponse
>;
export const GetRuleMigrationIntegrationsStatsResponse = RuleMigrationAllIntegrationsStats;

export type GetRuleMigrationPrebuiltRulesRequestParams = z.infer<
typeof GetRuleMigrationPrebuiltRulesRequestParams
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ paths:
additionalProperties:
$ref: '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml#/components/schemas/RelatedIntegration'

/internal/siem_migrations/rules/integrations/stats:
get:
summary: Retrieves the stats of all the integrations for all the rule migrations
operationId: GetRuleMigrationIntegrationsStats
x-codegen-enabled: true
x-internal: true
description: Retrieves the stats of all the integrations for all the rule migrations, including the number of rules associated with the integration
tags:
- SIEM Rule Migrations
responses:
200:
description: Indicates that related integrations stats have been retrieved correctly.
content:
application/json:
schema:
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationAllIntegrationsStats'

/internal/siem_migrations/rules:
put:
summary: Creates a new rule migration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,27 @@ export const RuleMigrationRetryFilter = z.enum(['failed', 'not_fully_translated'
export type RuleMigrationRetryFilterEnum = typeof RuleMigrationRetryFilter.enum;
export const RuleMigrationRetryFilterEnum = RuleMigrationRetryFilter.enum;

/**
* The migration rules integration stats object.
*/
export type RuleMigrationIntegrationStats = z.infer<typeof RuleMigrationIntegrationStats>;
export const RuleMigrationIntegrationStats = z.object({
/**
* The integration id
*/
id: NonEmptyString,
/**
* The number of rules that are associated with the integration.
*/
total_rules: z.number().int(),
});

/**
* The integrations stats objects of all the rule of all the migrations.
*/
export type RuleMigrationAllIntegrationsStats = z.infer<typeof RuleMigrationAllIntegrationsStats>;
export const RuleMigrationAllIntegrationsStats = z.array(RuleMigrationIntegrationStats);

/**
* The type of the rule migration resource.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,26 @@ components:
- failed
- not_fully_translated

RuleMigrationAllIntegrationsStats:
type: array
description: The integrations stats objects of all the rule of all the migrations.
items:
description: The migration rules integration stats object.
$ref: '#/components/schemas/RuleMigrationIntegrationStats'
RuleMigrationIntegrationStats:
type: object
description: The migration rules integration stats object.
required:
- id
- total_rules
properties:
id:
description: The integration id
$ref: '../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
total_rules:
type: integer
description: The number of rules that are associated with the integration.

## Rule migration resources

RuleMigrationResourceType:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
*/
import React, { useEffect, useState } from 'react';

/**
* HOC to wrap a component with a lazy-loaded hook.
* This allows the component to use a hook that is imported dynamically,
* which can be useful for reducing the initial bundle size.
*
* @param Component - The component to wrap, it have to accept the hook as a prop (e.g. { useSomeHook: UseSomeHook }).
* @param hookImport - A function that returns a promise resolving to an object with the hook's prop (e.g. { useSomeHook: () => {} }).
* @param fallback - A fallback React node to render while the hook is being loaded.
*/
export const withLazyHook = <P extends {}, PInjected extends keyof P>(
Component: React.ComponentType<P>,
hookImport: () => Promise<Pick<P, PInjected>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@

import React from 'react';

export const IntegrationsCardGridTabs = () => <div data-test-subj="integrationsCardGridTabs" />;
export const SecurityIntegrations = jest.fn(() => <div data-test-subj="securityIntegrations" />);
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
*/
import React from 'react';

export const SecurityIntegrations = () => <div data-test-subj="securityIntegrations" />;
export const SecurityIntegrationsGridTabs = jest.fn(() => (
<div data-test-subj="securityIntegrationsGridTabs" />
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 React from 'react';
import type { IntegrationCardItem } from '@kbn/fleet-plugin/public';

export const getDefaultAvailablePackages = () => ({
initialSelectedCategory: '',
selectedCategory: '',
setCategory: jest.fn(),
allCategories: [],
mainCategories: [],
availableSubCategories: [],
selectedSubCategory: '',
setSelectedSubCategory: jest.fn(),
searchTerm: '',
setSearchTerm: jest.fn(),
setUrlandPushHistory: jest.fn(),
setUrlandReplaceHistory: jest.fn(),
preference: '',
setPreference: jest.fn(),
isLoading: false,
isLoadingCategories: false,
isLoadingAllPackages: false,
isLoadingAppendCustomIntegrations: false,
eprPackageLoadingError: null,
eprCategoryLoadingError: null,
filteredCards: [] as IntegrationCardItem[],
});

export const mockAvailablePackages = jest.fn(() => getDefaultAvailablePackages());

export const withAvailablePackages = jest.fn(
(Component: React.ComponentType<{ availablePackages: unknown }>) =>
function WithAvailablePackages(props: object) {
return (
<div data-test-subj="withAvailablePackages">
<Component
{...{
...props,
availablePackages: mockAvailablePackages(),
}}
/>
</div>
);
}
);

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

export const PackageListGrid = () => <div data-test-subj="packageListGrid" />;
export { SecurityIntegrations } from './security_integrations';

This file was deleted.

Loading
Loading