[wip] support 'splunk.com/on-call-routing-key' annotation

Extends the Splunk On Call plugin Entity card to infer the
corresponding team names from a `splunk.com/on-call-routing-key`
annotation.

This seeks to enable the use of the Splunk On Call plugin on
entity pages for those orgs who associate a component with a
routing key rather than a team name.

Signed-off-by: Mike Ball <mikedball@gmail.com>
This commit is contained in:
Mike Ball
2022-02-03 12:04:55 -05:00
parent 28cad572ff
commit 70903de879
4 changed files with 79 additions and 8 deletions
+11
View File
@@ -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<RoutingKey[]> {
const url = `${await this.config.discoveryApi.getBaseUrl(
'proxy',
)}/splunk-on-call/v1/org/routing-keys`;
const { routingKeys } = await this.getByUrl<ListRoutingKeyResponse>(url);
return routingKeys;
}
async getUsers(): Promise<User[]> {
const url = `${await this.config.discoveryApi.getBaseUrl(
'proxy',
+11
View File
@@ -18,6 +18,7 @@ import {
EscalationPolicyInfo,
Incident,
OnCall,
RoutingKey,
Team,
User,
} from '../components/types';
@@ -65,6 +66,11 @@ export interface SplunkOnCallApi {
*/
getTeams(): Promise<Team[]>;
/**
* Get a list of routing keys for your organization.
*/
getRoutingKeys(): Promise<RoutingKey[]>;
/**
* 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[];
};
@@ -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 = () => (
<MissingAnnotationEmptyState annotation={SPLUNK_ON_CALL_TEAM} />
export const MissingAnnotation = () => (
<div>
<Typography>
The Splunk On Call plugin requires setting either the{' '}
<code>{SPLUNK_ON_CALL_TEAM}</code> or the{' '}
<code>{SPLUNK_ON_CALL_ROUTING_KEY}</code> annotation.
</Typography>
<MissingAnnotationEmptyState annotation={SPLUNK_ON_CALL_TEAM} />
<MissingAnnotationEmptyState annotation={SPLUNK_ON_CALL_ROUTING_KEY} />
</div>
);
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<boolean>(false);
const [refreshIncidents, setRefreshIncidents] = useState<boolean>(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 <Progress />;
}
const team =
usersAndTeam?.foundTeam && usersAndTeam?.foundTeam.name
? usersAndTeam?.foundTeam.name
: '';
const Content = () => {
if (!team) {
return <MissingTeamAnnotation />;
if (!teamAnnotation && !routingKeyAnnotation) {
return <MissingAnnotation />;
}
if (!usersAndTeam?.foundTeam) {
return <InvalidTeamAnnotation teamName={team} />;
return (
<InvalidTeamAnnotation
teamName={teamAnnotation || routingKeyAnnotation || ''}
/>
);
}
if (!eventsRestEndpoint) {
@@ -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;
};