diff --git a/plugins/splunk-on-call/src/api/client.ts b/plugins/splunk-on-call/src/api/client.ts index 437c6365ed..6c0ff9bdc4 100644 --- a/plugins/splunk-on-call/src/api/client.ts +++ b/plugins/splunk-on-call/src/api/client.ts @@ -19,6 +19,7 @@ import { OnCall, User, EscalationPolicyInfo, + RoutingKey, Team, } from '../components/types'; import { @@ -30,6 +31,7 @@ import { RequestOptions, ListUserResponse, EscalationPolicyResponse, + ListRoutingKeyResponse, } from './types'; import { createApiRef, @@ -82,6 +84,15 @@ export class SplunkOnCallClient implements SplunkOnCallApi { return teams; } + async getRoutingKeys(): Promise { + const url = `${await this.config.discoveryApi.getBaseUrl( + 'proxy', + )}/splunk-on-call/v1/org/routing-keys`; + const { routingKeys } = await this.getByUrl(url); + + return routingKeys; + } + async getUsers(): Promise { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', diff --git a/plugins/splunk-on-call/src/api/types.ts b/plugins/splunk-on-call/src/api/types.ts index 0bdd093ad1..6a414d18bd 100644 --- a/plugins/splunk-on-call/src/api/types.ts +++ b/plugins/splunk-on-call/src/api/types.ts @@ -18,6 +18,7 @@ import { EscalationPolicyInfo, Incident, OnCall, + RoutingKey, Team, User, } from '../components/types'; @@ -65,6 +66,11 @@ export interface SplunkOnCallApi { */ getTeams(): Promise; + /** + * Get a list of routing keys for your organization. + */ + getRoutingKeys(): Promise; + /** * Get a list of escalation policies for your organization. */ @@ -80,6 +86,11 @@ export type ListUserResponse = { _selfUrl?: string; }; +export type ListRoutingKeyResponse = { + routingKeys: RoutingKey[]; + _selfUrl?: string; +}; + export type IncidentsResponse = { incidents: Incident[]; }; diff --git a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.tsx b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.tsx index bca53a3b0a..ef4ad0388b 100644 --- a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.tsx +++ b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.tsx @@ -45,9 +45,18 @@ import { } from '@backstage/core-components'; export const SPLUNK_ON_CALL_TEAM = 'splunk.com/on-call-team'; +export const SPLUNK_ON_CALL_ROUTING_KEY = 'splunk.com/on-call-routing-key'; -export const MissingTeamAnnotation = () => ( - +export const MissingAnnotation = () => ( +
+ + The Splunk On Call plugin requires setting either the{' '} + {SPLUNK_ON_CALL_TEAM} or the{' '} + {SPLUNK_ON_CALL_ROUTING_KEY} annotation. + + + +
); export const InvalidTeamAnnotation = ({ teamName }: { teamName: string }) => ( @@ -71,7 +80,8 @@ export const MissingEventsRestEndpoint = () => ( ); export const isSplunkOnCallAvailable = (entity: Entity) => - Boolean(entity.metadata.annotations?.[SPLUNK_ON_CALL_TEAM]); + Boolean(entity.metadata.annotations?.[SPLUNK_ON_CALL_TEAM]) || + Boolean(entity.metadata.annotations?.[SPLUNK_ON_CALL_ROUTING_KEY]); export const EntitySplunkOnCallCard = () => { const config = useApi(configApiRef); @@ -79,7 +89,12 @@ export const EntitySplunkOnCallCard = () => { const { entity } = useEntity(); const [showDialog, setShowDialog] = useState(false); const [refreshIncidents, setRefreshIncidents] = useState(false); - const team = entity.metadata.annotations![SPLUNK_ON_CALL_TEAM]; + const teamAnnotation = entity + ? entity.metadata.annotations![SPLUNK_ON_CALL_TEAM] + : undefined; + const routingKeyAnnotation = entity + ? entity.metadata.annotations![SPLUNK_ON_CALL_ROUTING_KEY] + : undefined; const eventsRestEndpoint = config.getOptionalString('splunkOnCall.eventsRestEndpoint') || null; @@ -108,7 +123,20 @@ export const EntitySplunkOnCallCard = () => { {}, ); const teams = await api.getTeams(); - const foundTeam = teams.find(teamValue => teamValue.name === team); + let foundTeam = teams.find(teamValue => teamValue.name === teamAnnotation); + + if (!foundTeam && routingKeyAnnotation) { + const routingKeys = await api.getRoutingKeys(); + const foundRoutingKey = routingKeys.find( + key => key.routingKey === routingKeyAnnotation, + ); + const teamUrlParts = foundRoutingKey + ? foundRoutingKey.targets[0]._teamUrl.split('/') + : []; + const teamSlug = teamUrlParts[teamUrlParts.length - 1]; + foundTeam = teams.find(teamValue => teamValue.slug === teamSlug); + } + return { usersHashMap, foundTeam }; }); @@ -128,13 +156,22 @@ export const EntitySplunkOnCallCard = () => { return ; } + const team = + usersAndTeam?.foundTeam && usersAndTeam?.foundTeam.name + ? usersAndTeam?.foundTeam.name + : ''; + const Content = () => { - if (!team) { - return ; + if (!teamAnnotation && !routingKeyAnnotation) { + return ; } if (!usersAndTeam?.foundTeam) { - return ; + return ( + + ); } if (!eventsRestEndpoint) { diff --git a/plugins/splunk-on-call/src/components/types.ts b/plugins/splunk-on-call/src/components/types.ts index 3c1902e52b..0feefe8b03 100644 --- a/plugins/splunk-on-call/src/components/types.ts +++ b/plugins/splunk-on-call/src/components/types.ts @@ -117,3 +117,15 @@ export type EscalationPolicyTeam = { name: string; slug: string; }; + +export type RoutingKey = { + routingKey: string; + targets: RoutingKeyTarget[]; + isDefault: boolean; +}; + +export type RoutingKeyTarget = { + policyName: string; + policySlug: string; + _teamUrl: string; +};