Skip to content

Commit

Permalink
add feature flag for new service map
Browse files Browse the repository at this point in the history
  • Loading branch information
crespocarlos committed Feb 28, 2025
1 parent b08056c commit 5fa4ec0
Show file tree
Hide file tree
Showing 24 changed files with 848 additions and 679 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export const OBSERVABILITY_APM_ENABLE_CONTINUOUS_ROLLUPS_ID =
export const OBSERVABILITY_APM_ENABLE_PROFILING_INTEGRATION_ID =
'observability:apmEnableProfilingIntegration';
export const OBSERVABILITY_APM_ENABLE_TABLE_SEARCH_BAR = 'observability:apmEnableTableSearchBar';
export const OBSERVABILITY_APM_ENABLE_SERVICE_MAP_V2 = 'observability:apmEnableServiceMapV2';
export const OBSERVABILITY_APM_ENABLE_SERVICE_INVENTORY_TABLE_SEARCH_BAR =
'observability:apmEnableServiceInventoryTableSearchBar';
export const OBSERVABILITY_LOGS_SHARED_NEW_LOGS_OVERVIEW_ID = 'observability:newLogsOverview';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [
settings.OBSERVABILITY_APM_ENABLE_CRITICAL_PATH_ID,
settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID,
settings.OBSERVABILITY_APM_ENABLE_TABLE_SEARCH_BAR,
settings.OBSERVABILITY_APM_ENABLE_SERVICE_MAP_V2,
settings.OBSERVABILITY_APM_ENABLE_SERVICE_INVENTORY_TABLE_SEARCH_BAR,
settings.OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE,
settings.OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'observability:apmEnableServiceMapV2': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'observability:entityCentricExperience': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface UsageStats {
'observability:enableInfrastructureAssetCustomDashboards': boolean;
'observability:apmAgentExplorerView': boolean;
'observability:apmEnableTableSearchBar': boolean;
'observability:apmEnableServiceMapV2': boolean;
'observability:apmEnableServiceInventoryTableSearchBar': boolean;
'observability:logSources': string[];
'observability:newLogsOverview': boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { ServiceHealthStatus } from '../service_health_status';
import { SERVICE_NAME, SPAN_SUBTYPE, SPAN_TYPE } from '../es_fields/apm';
import type { ServiceMapExitSpan, ServiceMapService, ServiceMapWithConnections } from './types';
import type { ServiceMapExitSpan, ServiceMapService, ServiceMapConnections } from './types';
import { getServiceMapNodes } from './get_service_map_nodes';
import { getExternalConnectionNode, getServiceConnectionNode } from './utils';

Expand Down Expand Up @@ -75,7 +75,7 @@ const anomalies = {

describe('getServiceMapNodes', () => {
it('maps external destinations to internal services', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [
getServiceConnectionNode(nodejsService),
getServiceConnectionNode(javaService),
Expand Down Expand Up @@ -107,7 +107,7 @@ describe('getServiceMapNodes', () => {
});

it('adds connection for messaging-based external destinations', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [
getServiceConnectionNode(nodejsService),
getServiceConnectionNode(javaService),
Expand Down Expand Up @@ -162,7 +162,7 @@ describe('getServiceMapNodes', () => {
});

it('collapses external destinations based on span.destination.resource.name', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [
getServiceConnectionNode(nodejsService),
getServiceConnectionNode(javaService),
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('getServiceMapNodes', () => {
});

it('picks the first span.type/subtype in an alphabetically sorted list', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [getServiceConnectionNode(javaService)],
exitSpanDestinations: [],
connections: [
Expand Down Expand Up @@ -235,7 +235,7 @@ describe('getServiceMapNodes', () => {
});

it('processes connections without a matching "service" aggregation', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [getServiceConnectionNode(javaService)],
exitSpanDestinations: [],
connections: [
Expand All @@ -252,7 +252,7 @@ describe('getServiceMapNodes', () => {
});

it('maps routing services child transasctions to their corresponding upstream service', () => {
const response: ServiceMapWithConnections = {
const response: ServiceMapConnections = {
servicesData: [getServiceConnectionNode(javaService)],
exitSpanDestinations: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ import {
SPAN_TYPE,
SPAN_SUBTYPE,
} from '../es_fields/apm';
import type { ExitSpanDestination } from './types';
import type { ExitSpanDestination, ServicesResponse } from './types';
import type {
Connection,
ConnectionNode,
ServiceConnectionNode,
ExternalConnectionNode,
ConnectionElement,
ConnectionEdge,
ServiceMapWithConnections,
ServiceMapConnections,
GroupResourceNodesResponse,
} from './types';
import type { GroupResourceNodesResponse } from './group_resource_nodes';

import { groupResourceNodes } from './group_resource_nodes';
import { getConnectionId, isExitSpan } from './utils';

Expand Down Expand Up @@ -55,10 +56,7 @@ function addMessagingConnections(
return [...connections, ...messagingConnections];
}

function getAllNodes(
services: ServiceMapWithConnections['servicesData'],
connections: ServiceMapWithConnections['connections']
) {
function getAllNodes(services: ServicesResponse[], connections: Connection[]) {
const allNodesMap = new Map<string, ConnectionNode>();

// Process connections in one pass
Expand Down Expand Up @@ -257,7 +255,7 @@ export function getServiceMapNodes({
exitSpanDestinations,
servicesData,
anomalies,
}: ServiceMapWithConnections): GroupResourceNodesResponse {
}: ServiceMapConnections): GroupResourceNodesResponse {
const allConnections = addMessagingConnections(connections, exitSpanDestinations);
const allNodes = getAllNodes(servicesData, allConnections);
const allServices = getAllServices(allNodes, exitSpanDestinations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
*/

import type { ConnectionElement, ServiceMapExitSpan, ServiceMapService } from './types';
import type { GroupedNode } from './group_resource_nodes';
import type { GroupedNode } from './types';
import { groupResourceNodes } from './group_resource_nodes';
import expectedGroupedData from '../../server/routes/service_map/mock_responses/group_resource_nodes_grouped.json';
import preGroupedData from '../../server/routes/service_map/mock_responses/group_resource_nodes_pregrouped.json';
import { getEdgeId, getExternalConnectionNode, getServiceConnectionNode } from './utils';

describe('groupResourceNodes', () => {
Expand Down Expand Up @@ -88,19 +86,6 @@ describe('groupResourceNodes', () => {

describe('basic grouping', () => {
it('should group external nodes', () => {
const responseWithGroups = groupResourceNodes(
preGroupedData as { elements: ConnectionElement[] }
);
expect(responseWithGroups.elements).toHaveLength(expectedGroupedData.elements.length);
for (const element of responseWithGroups.elements) {
const expectedElement = expectedGroupedData.elements.find(
({ data: { id } }: { data: { id: string } }) => id === element.data.id
)!;
expect(element).toMatchObject(expectedElement);
}
});

it('should group nodes when they meet minimum group size', () => {
const elements: ConnectionElement[] = [
nodeJsServiceNode,
nodeJsExitSpanQuora,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,22 @@
import { i18n } from '@kbn/i18n';
import { compact, groupBy } from 'lodash';
import { SPAN_TYPE, SPAN_SUBTYPE } from '../es_fields/apm';
import type { ConnectionEdge, ConnectionElement, ConnectionNode } from './types';
import type {
ConnectionEdge,
ConnectionElement,
ConnectionNode,
GroupResourceNodesResponse,
GroupedEdge,
GroupedNode,
} from './types';
import { getEdgeId, isSpanGroupingSupported } from './utils';

const MINIMUM_GROUP_SIZE = 4;

type GroupedConnection = ConnectionNode | ConnectionEdge;

export interface GroupedNode {
data: {
id: string;
'span.type': string;
label: string;
groupedConnections: GroupedConnection[];
};
}

export interface GroupedEdge {
data: {
id: string;
source: string;
target: string;
};
}

export interface GroupResourceNodesResponse {
elements: Array<GroupedNode | GroupedEdge | ConnectionElement>;
nodesCount: number;
}

const isEdge = (el: ConnectionElement): el is { data: ConnectionEdge } =>
Boolean(el.data.source && el.data.target);
const isNode = (el: ConnectionElement): el is { data: ConnectionNode } => !isEdge(el);
const isElligibleGroupNode = (el: ConnectionElement) => {
const isElligibleGroupNode = (el: ConnectionElement): el is { data: ConnectionNode } => {
if (isNode(el) && SPAN_TYPE in el.data) {
return isSpanGroupingSupported(el.data[SPAN_TYPE], el.data[SPAN_SUBTYPE]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import type {
ExitSpanDestination,
ConnectionElement,
ExternalConnectionNode,
GroupResourceNodesResponse,
ServiceConnectionNode,
ServicesResponse,
ServiceMapResponse,
ServiceMapWithConnections,
ServiceMapConnections,
ServiceMapTelemetry,
NodeStats,
NodeItem,
} from './types';
import type { GroupResourceNodesResponse } from './group_resource_nodes';

export * from './utils';
export { getServiceMapNodes } from './get_service_map_nodes';
Expand All @@ -35,7 +35,7 @@ export {
ExternalConnectionNode,
ServiceConnectionNode,
ServicesResponse,
ServiceMapWithConnections,
ServiceMapConnections as ServiceMapWithConnections,
ServiceMapResponse,
ServiceMapTelemetry,
NodeStats,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,83 @@

import type cytoscape from 'cytoscape';
import type { AgentName } from '@kbn/apm-types/src/es_schemas/ui/fields';
import type { AGENT_NAME, SERVICE_ENVIRONMENT, SERVICE_NAME } from '@kbn/apm-types';
import type { SPAN_DESTINATION_SERVICE_RESOURCE, SPAN_SUBTYPE, SPAN_TYPE } from '@kbn/apm-types';
import type { ServiceAnomaliesResponse } from '../../server/routes/service_map/get_service_anomalies';
import type { Coordinate } from '../../typings/timeseries';
import type { ServiceAnomalyStats } from '../anomaly_detection';

export interface ServiceMapTelemetry {
tracesCount: number;
nodesCount?: number;
}

export interface ServiceMapWithConnections
extends Pick<ServiceMapResponse, 'servicesData' | 'anomalies'> {
type GroupedConnection = ConnectionNode | ConnectionEdge;

export interface GroupedNode {
data: {
id: string;
'span.type': string;
label: string;
groupedConnections: GroupedConnection[];
};
}

export interface GroupedEdge {
data: {
id: string;
source: string;
target: string;
};
}

export interface GroupResourceNodesResponse {
elements: Array<GroupedNode | GroupedEdge | ConnectionElement>;
nodesCount: number;
}

export type ConnectionType = Connection | ConnectionLegacy;
export type DestinationType = ExitSpanDestination | ExitSpanDestinationLegacy;

export interface ServiceMapConnections {
servicesData: ServicesResponse[];
anomalies: ServiceAnomaliesResponse;
connections: Connection[];
exitSpanDestinations: ExitSpanDestination[];
}

export type ServiceMapResponse = {
spans: ServiceMapNode[];
export interface ServiceMapRawResponse {
spans: ServiceMapSpan[];
servicesData: ServicesResponse[];
anomalies: ServiceAnomaliesResponse;
} & ServiceMapTelemetry;
}
export type ServiceMapResponse = ServiceMapTelemetry &
(ServiceMapRawResponse | GroupResourceNodesResponse);

export interface ServicesResponse {
'service.name': string;
'agent.name': string;
'service.environment': string | null;
[SERVICE_NAME]: string;
[AGENT_NAME]: string;
[SERVICE_ENVIRONMENT]: string | null;
}

export interface ServiceConnectionNode extends cytoscape.NodeDataDefinition {
id: string;
'service.name': string;
'service.environment': string | null;
'agent.name': string;
'service.node.name'?: string;
serviceAnomalyStats?: ServiceAnomalyStats;
label?: string;
}
export type ServiceConnectionNode = cytoscape.NodeDataDefinition &
ServicesResponse & {
id: string;
serviceAnomalyStats?: ServiceAnomalyStats;
label?: string;
};
export interface ExternalConnectionNode extends cytoscape.NodeDataDefinition {
id: string;
'span.destination.service.resource': string;
'span.type': string;
'span.subtype': string;
[SPAN_DESTINATION_SERVICE_RESOURCE]: string;
[SPAN_TYPE]: string;
[SPAN_SUBTYPE]: string;
label?: string;
}

export type ConnectionNode = ServiceConnectionNode | ExternalConnectionNode;
export type ConnectionNodeLegacy =
| Omit<ServiceConnectionNode, 'id'>
| Omit<ExternalConnectionNode, 'id'>;

export interface ConnectionEdge {
id: string;
Expand All @@ -73,6 +106,10 @@ export interface Connection {
source: ConnectionNode;
destination: ConnectionNode;
}
export interface ConnectionLegacy {
source: ConnectionNodeLegacy;
destination: ConnectionNodeLegacy;
}

export interface NodeStats {
transactionStats?: {
Expand All @@ -99,11 +136,17 @@ export interface NodeStats {
};
}

export type ExitSpanDestinationType = ExitSpanDestination | ExitSpanDestinationLegacy;
export interface ExitSpanDestination {
from: ExternalConnectionNode;
to: ServiceConnectionNode;
}

export interface ExitSpanDestinationLegacy {
from: Omit<ExternalConnectionNode, 'id'>;
to: Omit<ServiceConnectionNode, 'id'>;
}

export interface ServiceMapService {
serviceName: string;
agentName: AgentName;
Expand All @@ -117,6 +160,6 @@ export interface ServiceMapExitSpan extends ServiceMapService {
spanSubtype: string;
spanDestinationServiceResource: string;
}
export type ServiceMapNode = ServiceMapExitSpan & {
export type ServiceMapSpan = ServiceMapExitSpan & {
destinationService?: ServiceMapService;
};
Loading

0 comments on commit 5fa4ec0

Please sign in to comment.