Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/platform/packages/shared/kbn-workflows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './spec/lib/generate_yaml_schema_from_connectors';
export * from './spec/lib/get_workflow_json_schema';
export { getElasticsearchConnectors } from './spec/elasticsearch';
export { getKibanaConnectors } from './spec/kibana';
export { resolveKibanaStepTypeAlias } from './spec/kibana/aliases';
export * from './spec/schema';
export { builtInStepDefinitions, getBuiltInStepDefinition } from './spec/builtin_step_definitions';
export type { BuiltInStepDefinition } from './spec/builtin_step_definitions';
Expand All @@ -34,6 +35,7 @@ export {
export type { WorkflowExampleEntry } from './spec/examples';
export { StepCategory, StepCategories } from './spec/step_definition_types';
export type { BaseStepDefinition, StepDocumentation } from './spec/step_definition_types';
export * from './spec/deprecated_step_metadata';
export * from './types/latest';
export * from './types/utils';
export * from './common/constants';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export interface StepDeprecationInfo {
replacementStepType?: string;
message?: string;
}

export const DEPRECATED_STEP_METADATA: Record<string, StepDeprecationInfo> = {
'kibana.createCase': {
replacementStepType: 'cases.createCase',
},
'kibana.getCase': {
replacementStepType: 'cases.getCase',
},
'kibana.updateCase': {
replacementStepType: 'cases.updateCase',
},
'kibana.addCaseComment': {
replacementStepType: 'cases.addComment',
},
'kibana.createCaseDefaultSpace': {
replacementStepType: 'cases.createCase',
},
'kibana.getCaseDefaultSpace': {
replacementStepType: 'cases.getCase',
},
'kibana.updateCaseDefaultSpace': {
replacementStepType: 'cases.updateCase',
},
'kibana.addCaseCommentDefaultSpace': {
replacementStepType: 'cases.addComment',
},
};

export function getStepDeprecationInfo(stepType: string): StepDeprecationInfo | undefined {
return DEPRECATED_STEP_METADATA[stepType];
}

export function isDeprecatedStepType(stepType: string): boolean {
return getStepDeprecationInfo(stepType) !== undefined;
}

export function getDeprecatedStepMessage(
stepType: string,
deprecation: StepDeprecationInfo
): string {
if (deprecation.message) {
return deprecation.message;
}
if (deprecation.replacementStepType) {
return `Step type "${stepType}" is deprecated. Use "${deprecation.replacementStepType}" instead.`;
}
return `Step type "${stepType}" is deprecated.`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,7 @@ export const KIBANA_TYPE_ALIASES: Record<string, string> = Object.fromEntries(
.filter(([, v]) => v.backward)
.map(([oldOp, v]) => [`kibana.${oldOp}`, `kibana.${v.type}`])
);

export function resolveKibanaStepTypeAlias(stepType: string): string {
return KIBANA_TYPE_ALIASES[stepType] ?? stepType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type { InternalConnectorContract } from '../../types/latest';
import { getStepDeprecationInfo } from '../deprecated_step_metadata';

export function getKibanaConnectors(): InternalConnectorContract[] {
// TODO: bring the kibana connectors back, with the new approach to schemas generation
Expand All @@ -32,5 +33,8 @@ export function getKibanaConnectors(): InternalConnectorContract[] {
}
}

return mergedConnectors;
return mergedConnectors.map((connector) => ({
...connector,
deprecation: connector.deprecation ?? getStepDeprecationInfo(connector.type),
}));
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { z } from '@kbn/zod/v4';
import { convertLegacyFieldsToJsonSchema } from './field_conversion';
import { type ConnectorContractUnion } from '../..';
import { getDeprecatedStepMessage, getStepDeprecationInfo } from '../deprecated_step_metadata';
import { KIBANA_TYPE_ALIASES } from '../kibana/aliases';
import {
BaseConnectorStepSchema,
Expand Down Expand Up @@ -189,10 +190,14 @@ function generateAliasSchemas(
if (connector) {
// Create a schema with the old type name but same params/output
const newSchema = generateStepSchemaForConnector(connector, stepSchema, loose);
const deprecation = getStepDeprecationInfo(oldType);
const description = deprecation
? getDeprecatedStepMessage(oldType, deprecation)
: `Deprecated: Use ${newType} instead`;
aliasSchemas.push(
newSchema.extend({
// Mark as deprecated in description so it's clear this is a legacy alias
type: z.literal(oldType).describe(`Deprecated: Use ${newType} instead`),
type: z.literal(oldType).describe(description),
})
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ describe('getWorkflowJsonSchema / kibana connectors', () => {
validateWithYamlLsp = getValidateWithYamlLsp(jsonSchema);
});

it('uses shared deprecation messaging for deprecated alias step types', () => {
const jsonSchemaString = JSON.stringify(jsonSchema);

expect(jsonSchemaString).toContain(
'Step type \\"kibana.createCaseDefaultSpace\\" is deprecated. Use \\"cases.createCase\\" instead.'
);
});

it('should fix Monaco JSON schema generation to restore YAML validation', () => {
// This test verifies that the Monaco YAML validation works properly
// after fixing the broken $ref paths in the JSON schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type { z } from '@kbn/zod/v4';
import type { StepDeprecationInfo } from './deprecated_step_metadata';
import type { StepStabilityLevel } from '../types/v1';

export enum StepCategory {
Expand Down Expand Up @@ -113,4 +114,11 @@ export interface BaseStepDefinition<
* Omit for stable/GA steps.
*/
stability?: StepStabilityLevel;

/**
* Deprecation metadata for this step type.
* Deprecated steps remain valid for existing workflows, but should not be
* suggested for new workflows.
*/
deprecation?: StepDeprecationInfo;
}
3 changes: 3 additions & 0 deletions src/platform/packages/shared/kbn-workflows/types/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import type { JsonValue, RecursivePartial } from '@kbn/utility-types';
import { z } from '@kbn/zod/v4';
import type { StepDeprecationInfo } from '../spec/deprecated_step_metadata';
import type { SerializedError, WorkflowYaml } from '../spec/schema';
import { WorkflowSchema } from '../spec/schema';

Expand Down Expand Up @@ -454,6 +455,8 @@ export interface BaseConnectorContract {
documentation?: string | null;
/** API stability level derived from the OpenAPI `x-state` field */
stability?: StepStabilityLevel;
/** Deprecation metadata for this step type. */
deprecation?: StepDeprecationInfo;
examples?: ConnectorExamples;
// Rich property handlers for completions, validation and decorations
editorHandlers?: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ import type {
BaseConnectorContract,
ConnectorContractUnion,
ConnectorTypeInfo,
StepDeprecationInfo,
StepPropertyHandler,
} from '@kbn/workflows';
import {
builtInStepDefinitions,
DEPRECATED_STEP_METADATA,
generateYamlSchemaFromConnectors,
getElasticsearchConnectors,
getKibanaConnectors,
Expand Down Expand Up @@ -95,6 +98,7 @@ function getRegisteredStepDefinitions(): BaseConnectorContract[] {
paramsSchema: stepDefinition.inputSchema,
outputSchema: stepDefinition.outputSchema,
configSchema: stepDefinition.configSchema,
deprecation: stepDefinition.deprecation,
summary: null,
description: null,
};
Expand Down Expand Up @@ -340,6 +344,44 @@ export function getAllConnectors(): ConnectorContractUnion[] {
return getAllConnectorsInternal();
}

export function getDeprecatedStepMetadataMap(): Readonly<Record<string, StepDeprecationInfo>> {
const cached = stepSchemas.getDeprecatedStepMetadataCache();
if (cached !== null) {
return cached;
}

const deprecatedStepMetadata: Record<string, StepDeprecationInfo> = {
...DEPRECATED_STEP_METADATA,
};

for (const stepDefinition of builtInStepDefinitions) {
if (stepDefinition.deprecation) {
deprecatedStepMetadata[stepDefinition.id] = stepDefinition.deprecation;
}
}

for (const connector of getAllConnectorsInternal()) {
if (connector.deprecation) {
deprecatedStepMetadata[connector.type] = connector.deprecation;
}
}

const frozenDeprecatedStepMetadata = Object.freeze(deprecatedStepMetadata) as Readonly<
Record<string, StepDeprecationInfo>
>;

stepSchemas.setDeprecatedStepMetadataCache(frozenDeprecatedStepMetadata);
return frozenDeprecatedStepMetadata;
}

export function getDeprecatedStepMetadata(stepType: string): StepDeprecationInfo | undefined {
return getDeprecatedStepMetadataMap()[stepType];
}

export function isDeprecatedStepType(stepType: string): boolean {
return getDeprecatedStepMetadata(stepType) !== undefined;
}

export function getAllConnectorsWithDynamic(
dynamicConnectorTypes?: Record<string, ConnectorTypeInfo>
): ConnectorContractUnion[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getAllConnectorsWithDynamic,
getCachedAllConnectorsMap,
getCachedDynamicConnectorTypes,
getDeprecatedStepMetadataMap,
} from './schema';

describe('schema - additional coverage', () => {
Expand Down Expand Up @@ -48,6 +49,22 @@ describe('schema - additional coverage', () => {
});
});

describe('getDeprecatedStepMetadataMap', () => {
it('should freeze the cached metadata map', () => {
const metadata = getDeprecatedStepMetadataMap();

expect(Object.isFrozen(metadata)).toBe(true);
});

it('should return the same cached frozen object on subsequent calls', () => {
const first = getDeprecatedStepMetadataMap();
const second = getDeprecatedStepMetadataMap();

expect(first).toBe(second);
expect(Object.isFrozen(second)).toBe(true);
});
});

describe('getAllConnectorsWithDynamic', () => {
it('should return base connectors when no dynamic types provided', () => {
const result = getAllConnectorsWithDynamic();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { ConnectorContractUnion, ConnectorTypeInfo } from '@kbn/workflows';
import type {
ConnectorContractUnion,
ConnectorTypeInfo,
StepDeprecationInfo,
} from '@kbn/workflows';
import type {
PublicStepDefinition,
WorkflowsExtensionsPublicPluginStart,
Expand All @@ -31,6 +35,7 @@ class StepSchemas {
private workflowsExtensions: WorkflowsExtensions | null = null;
private allConnectorsCache: ConnectorContractUnion[] | null = null;
private allConnectorsMapCache: Map<string, ConnectorContractUnion> | null = null;
private deprecatedStepMetadataCache: Readonly<Record<string, StepDeprecationInfo>> | null = null;
private dynamicConnectorTypesCache: Record<string, ConnectorTypeInfo> | null = null;
private lastProcessedConnectorTypesHash: string | null = null;

Expand Down Expand Up @@ -72,6 +77,7 @@ class StepSchemas {

public setAllConnectorsCache(cache: ConnectorContractUnion[] | null): void {
this.allConnectorsCache = cache;
this.deprecatedStepMetadataCache = null;
}

public getAllConnectorsMapCache(): Map<string, ConnectorContractUnion> | null {
Expand All @@ -82,6 +88,16 @@ class StepSchemas {
this.allConnectorsMapCache = cache;
}

public getDeprecatedStepMetadataCache(): Readonly<Record<string, StepDeprecationInfo>> | null {
return this.deprecatedStepMetadataCache;
}

public setDeprecatedStepMetadataCache(
cache: Readonly<Record<string, StepDeprecationInfo>> | null
): void {
this.deprecatedStepMetadataCache = cache;
}

public getDynamicConnectorTypesCache(): Record<string, ConnectorTypeInfo> | null {
return this.dynamicConnectorTypesCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import type { WorkflowsExtensionsPublicPluginStart } from '@kbn/workflows-extens
import { workflowsExtensionsMock } from '@kbn/workflows-extensions/public/mocks';
import { z } from '@kbn/zod/v4';
import { flattenOptions, getActionOptions } from './get_action_options';
import { getAllConnectors } from '../../../../common/schema';
import { getAllConnectors, getDeprecatedStepMetadataMap } from '../../../../common/schema';
import { getStepIconType } from '../../../shared/ui/step_icons/get_step_icon_type';
import { triggerSchemas } from '../../../trigger_schemas';
import type { ActionOptionData } from '../types';
import { isActionGroup, isActionOption } from '../types';

jest.mock('../../../../common/schema', () => ({
getAllConnectors: jest.fn(),
getDeprecatedStepMetadataMap: jest.fn(() => ({})),
}));
jest.mock('../../../trigger_schemas', () => ({
triggerSchemas: { getTriggerDefinitions: jest.fn(() => []) },
Expand Down Expand Up @@ -59,6 +60,7 @@ describe('getActionOptions', () => {
mockWorkflowsExtensions = workflowsExtensionsMock.createStart();

(getAllConnectors as jest.Mock).mockReturnValue([]);
(getDeprecatedStepMetadataMap as jest.Mock).mockReturnValue({});
(isDynamicConnector as jest.MockedFunction<typeof isDynamicConnector>).mockImplementation(
() => false
);
Expand Down Expand Up @@ -336,6 +338,28 @@ describe('getActionOptions', () => {
}
});

it('should hide deprecated connectors from the actions menu', () => {
const mockConnector = {
type: 'kibana.createCase',
description: 'Create a case',
summary: 'Create a case',
};

(getAllConnectors as jest.Mock).mockReturnValue([mockConnector]);
(getDeprecatedStepMetadataMap as jest.Mock).mockReturnValue({
'kibana.createCase': { replacementStepType: 'cases.createCase' },
});
mockWorkflowsExtensions.getStepDefinition.mockReturnValue(undefined);

const result = getActionOptions(mockEuiTheme, mockWorkflowsExtensions);
const kibanaGroup = result.find((group) => group.id === 'kibana');

expect(kibanaGroup).toBeDefined();
if (kibanaGroup && isActionGroup(kibanaGroup)) {
expect(kibanaGroup.options).toHaveLength(0);
}
});

it('should pass tech_preview stability for kibana connectors with tech_preview stability', () => {
const mockConnector = {
type: 'kibana.streams.list',
Expand Down
Loading
Loading