diff --git a/plugins/splunk-on-call/src/components/Errors/MissingTokenError.tsx b/plugins/splunk-on-call/src/components/Errors/MissingTokenError.tsx new file mode 100644 index 0000000000..221ab780e7 --- /dev/null +++ b/plugins/splunk-on-call/src/components/Errors/MissingTokenError.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { EmptyState } from '@backstage/core'; +import { Button } from '@material-ui/core'; + +export const MissingTokenError = () => ( + + Read More + + } + /> +); diff --git a/plugins/splunk-on-call/src/components/Errors/index.ts b/plugins/splunk-on-call/src/components/Errors/index.ts new file mode 100644 index 0000000000..3c2dfa65f2 --- /dev/null +++ b/plugins/splunk-on-call/src/components/Errors/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { MissingTokenError } from './MissingTokenError'; diff --git a/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx b/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx new file mode 100644 index 0000000000..1f61e435c9 --- /dev/null +++ b/plugins/splunk-on-call/src/components/SplunkOnCallCard.tsx @@ -0,0 +1,142 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useState, useCallback } from 'react'; +import { useApi, Progress, HeaderIconLinkRow } from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { + Button, + makeStyles, + Card, + CardHeader, + Divider, + CardContent, +} from '@material-ui/core'; +import { Incidents } from './Incident'; +import { EscalationPolicy } from './Escalation'; +import { useAsync } from 'react-use'; +import { Alert } from '@material-ui/lab'; +import { splunkOnCallApiRef, UnauthorizedError } from '../api'; +import AlarmAddIcon from '@material-ui/icons/AlarmAdd'; +import { TriggerDialog } from './TriggerDialog'; +import { MissingTokenError } from './Errors/MissingTokenError'; +import { User } from './types'; + +const useStyles = makeStyles({ + triggerAlarm: { + paddingTop: 0, + paddingBottom: 0, + fontSize: '0.7rem', + textTransform: 'uppercase', + fontWeight: 600, + letterSpacing: 1.2, + lineHeight: 1.5, + '&:hover, &:focus, &.focus': { + backgroundColor: 'transparent', + textDecoration: 'none', + }, + }, +}); + +export const SPLUNK_ON_CALL_INTEGRATION_KEY = ''; + +export const isPluginApplicableToEntity = (entity: Entity) => + Boolean(entity.metadata.annotations?.[SPLUNK_ON_CALL_INTEGRATION_KEY]); + +type Props = { + entity: Entity; +}; + +export const SplunkOnCallCard = () => { + const classes = useStyles(); + const api = useApi(splunkOnCallApiRef); + const [showDialog, setShowDialog] = useState(false); + const [refreshIncidents, setRefreshIncidents] = useState(false); + + const handleRefresh = useCallback(() => { + setRefreshIncidents(x => !x); + }, []); + + const handleDialog = useCallback(() => { + setShowDialog(x => !x); + }, []); + + const { value: users, loading, error } = useAsync(async () => { + const users = await api.getUsers(); + const usersHashMap = users.reduce( + (map: Record, obj: User) => { + if (obj.username) { + map[obj.username] = obj; + } + return map; + }, + {}, + ); + return { usersHashMap, userList: users }; + }); + + if (error instanceof UnauthorizedError) { + return ; + } + + if (error) { + return ( + + Error encountered while fetching information. {error.message} + + ); + } + + if (loading) { + return ; + } + + const triggerLink = { + label: 'Create Incident', + action: ( + + ), + icon: , + }; + + return ( + + } + /> + + + + {users?.usersHashMap && } + {users && ( + + )} + + + ); +}; diff --git a/plugins/splunk-on-call/src/components/SplunkOnCallPage.tsx b/plugins/splunk-on-call/src/components/SplunkOnCallPage.tsx new file mode 100644 index 0000000000..9765e6701e --- /dev/null +++ b/plugins/splunk-on-call/src/components/SplunkOnCallPage.tsx @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Grid, makeStyles } from '@material-ui/core'; +import { + Content, + ContentHeader, + Page, + Header, + SupportButton, +} from '@backstage/core'; +import { SplunkOnCallCard } from '..'; + +const useStyles = makeStyles(() => ({ + overflowXScroll: { + overflowX: 'scroll', + }, +})); + +export type SplunkOnCallProps = { + title?: string; + subtitle?: string; + pageTitle?: string; +}; + +export const SplunkOnCallPage = ({ + title, + subtitle, + pageTitle, + ...props +}: SplunkOnCallProps): JSX.Element => { + const classes = useStyles(); + return ( + +
+ + + + This is used for visualizing the official guidelines of different + areas of software development such as languages, frameworks, + infrastructure and processes. + + + + + + + + + + ); +}; + +SplunkOnCallPage.defaultProps = { + title: 'Splunk On-Call', + subtitle: 'Offers a simple way to identify incidents and escalation policies', + pageTitle: 'Welcome to Splunk On-Call in Backstage!', +}; diff --git a/plugins/splunk-on-call/src/components/types.ts b/plugins/splunk-on-call/src/components/types.ts new file mode 100644 index 0000000000..5964c04f09 --- /dev/null +++ b/plugins/splunk-on-call/src/components/types.ts @@ -0,0 +1,155 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IncidentTarget } from '../api/types'; + +export type Service = { + id: string; + name: string; + html_url: string; + integrationKey: string; + escalation_policy: { + id: string; + user: User; + }; +}; + +export type Assignee = { + id: string; + summary: string; + html_url: string; +}; + +export type SubHeaderLink = { + title: string; + href?: string; + icon: React.ReactNode; + action?: React.ReactNode; +}; + +// GET Escalation Policies + +// export type EscalationPolicyInfo = { +// policy: EscalationPolicySummary; +// team: Team; +// } +// export type EscalationPolicySummary = { +// name: string; +// slug: string; +// _selfUrl: string; +// } +// export type Team = { +// name: string; +// slug: string; +// } + +// GET oncall +export type OnCall = { + team?: OnCallTeamResource; + oncallNow?: OnCallNowResource[]; +}; + +export type OnCallTeamResource = { + name?: string; + slug?: string; +}; + +export type OnCallNowResource = { + escalationPolicy?: OnCallEscalationPolicyResource; + users?: OnCallUsersResource[]; +}; + +export type OnCallEscalationPolicyResource = { + name?: string; + slug?: string; +}; + +export type OnCallUsersResource = { + onCalluser?: OnCallUser; +}; + +export type OnCallUser = { + username?: string; +}; + +// GET /api-public/v2/user +export type User = { + firstName?: string; + lastName?: string; + username?: string; + email?: string; + createdAt?: string; + passwordLastUpdated?: string; + verified?: boolean; + _selfUrl?: string; +}; + +// Incident creation +export type CreateIncidentRequest = { + summary: string; + details: string; + userName: string; + targets: IncidentTarget; + isMultiResponder: boolean; +}; + +// GET incidents +export type Incident = { + incidentNumber?: string; + startTime?: string; + currentPhase?: string; + alertCount?: number; + lastAlertTime?: string; + lastAlertId?: string; + entityId?: string; + host?: string; + service?: string; + pagedUsers?: string[]; + pagedTeams?: string[]; + entityDisplayName?: string; + pagedPolicies?: EscalationPolicyInfo[]; + transitions?: IncidentTransition[]; + firstAlertUuid?: string; + monitorName?: string; + monitorType?: string; + incidentLink?: string; +}; + +export type EscalationPolicyInfo = { + policy: EscalationPolicySummary; + team: Team; +}; + +export type IncidentTransition = { + name?: string; + at?: string; + by?: string; + message?: string; + manually?: boolean; + alertId?: string; + alertUrl?: string; +}; + +export type EscalationPolicySummary = { + name: string; + slug: string; + _selfUrl: string; +}; + +export type Team = { + name: string; + slug: string; +};