enable 'read only' mode Splunk OnCall option
This exposes a `splunkOnCall.readOnly` configuration option to address issue #9634 Signed-off-by: Mike Ball <mikedball@gmail.com>
This commit is contained in:
@@ -53,6 +53,14 @@ const mockSplunkOnCallApi: Partial<SplunkOnCallClient> = {
|
||||
const configApi: ConfigApi = new ConfigReader({
|
||||
splunkOnCall: {
|
||||
eventsRestEndpoint: 'EXAMPLE_REST_ENDPOINT',
|
||||
readOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
const readOnlyConfigApi: ConfigApi = new ConfigReader({
|
||||
splunkOnCall: {
|
||||
eventsRestEndpoint: 'EXAMPLE_REST_ENDPOINT',
|
||||
readOnly: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -62,6 +70,12 @@ const apis = TestApiRegistry.from(
|
||||
[alertApiRef, {}],
|
||||
);
|
||||
|
||||
const readOnlyApis = TestApiRegistry.from(
|
||||
[splunkOnCallApiRef, mockSplunkOnCallApi],
|
||||
[configApiRef, readOnlyConfigApi],
|
||||
[alertApiRef, {}],
|
||||
);
|
||||
|
||||
const mockEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
@@ -131,6 +145,32 @@ describe('SplunkOnCallCard', () => {
|
||||
expect(getByText('Empty escalation policy')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render a "Create incident" link in read only mode', async () => {
|
||||
mockSplunkOnCallApi.getUsers = jest
|
||||
.fn()
|
||||
.mockImplementationOnce(async () => [MOCKED_USER]);
|
||||
mockSplunkOnCallApi.getTeams = jest
|
||||
.fn()
|
||||
.mockImplementation(async () => [MOCK_TEAM_NO_INCIDENTS]);
|
||||
|
||||
const { getByText, queryByTestId } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={readOnlyApis}>
|
||||
<EntityProvider entity={mockEntityNoIncidents}>
|
||||
<EntitySplunkOnCallCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
await waitFor(() => !queryByTestId('progress'));
|
||||
expect(() => getByText('Create Incident')).toThrow();
|
||||
await waitFor(
|
||||
() => expect(getByText('Nice! No incidents found!')).toBeInTheDocument(),
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
expect(getByText('Empty escalation policy')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles a "splunk.com/on-call-routing-key" annotation', async () => {
|
||||
mockSplunkOnCallApi.getUsers = jest
|
||||
.fn()
|
||||
|
||||
@@ -124,7 +124,9 @@ export const EntitySplunkOnCallCard = () => {
|
||||
: undefined;
|
||||
|
||||
const eventsRestEndpoint =
|
||||
config.getOptionalString('splunkOnCall.eventsRestEndpoint') || null;
|
||||
config.getOptionalString('splunkOnCall.eventsRestEndpoint') || true;
|
||||
|
||||
const readOnly = config.getOptionalBoolean('splunkOnCall.readOnly') || false;
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
setRefreshIncidents(x => !x);
|
||||
@@ -218,7 +220,11 @@ export const EntitySplunkOnCallCard = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Incidents team={teamName} refreshIncidents={refreshIncidents} />
|
||||
<Incidents
|
||||
readOnly={readOnly}
|
||||
team={teamName}
|
||||
refreshIncidents={refreshIncidents}
|
||||
/>
|
||||
{usersHashMap && team && (
|
||||
<EscalationPolicy team={teamName} users={usersHashMap} />
|
||||
)}
|
||||
@@ -259,7 +265,7 @@ export const EntitySplunkOnCallCard = () => {
|
||||
</Typography>,
|
||||
<HeaderIconLinkRow
|
||||
key="incident_trigger"
|
||||
links={[serviceLink, triggerLink]}
|
||||
links={!readOnly ? [serviceLink, triggerLink] : [serviceLink]}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -64,6 +64,7 @@ type Props = {
|
||||
team: string;
|
||||
incident: Incident;
|
||||
onIncidentAction: () => void;
|
||||
readOnly: boolean;
|
||||
};
|
||||
|
||||
const IncidentPhaseStatus = ({
|
||||
@@ -135,6 +136,7 @@ const IncidentAction = ({
|
||||
|
||||
export const IncidentListItem = ({
|
||||
incident,
|
||||
readOnly,
|
||||
onIncidentAction,
|
||||
team,
|
||||
}: Props) => {
|
||||
@@ -241,12 +243,16 @@ export const IncidentListItem = ({
|
||||
|
||||
{incident.incidentLink && incident.incidentNumber && (
|
||||
<ListItemSecondaryAction>
|
||||
<IncidentAction
|
||||
currentPhase={incident.currentPhase || ''}
|
||||
incidentId={incident.entityId}
|
||||
resolveAction={handleResolveIncident}
|
||||
acknowledgeAction={handleAcknowledgeIncident}
|
||||
/>
|
||||
{!readOnly ? (
|
||||
<IncidentAction
|
||||
currentPhase={incident.currentPhase || ''}
|
||||
incidentId={incident.entityId}
|
||||
resolveAction={handleResolveIncident}
|
||||
acknowledgeAction={handleAcknowledgeIncident}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<Tooltip title="View in Splunk On-Call" placement="top">
|
||||
<IconButton
|
||||
href={incident.incidentLink}
|
||||
|
||||
@@ -90,6 +90,46 @@ describe('Incidents', () => {
|
||||
expect(getAllByTitle('View in Splunk On-Call').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('does not render incident action buttons in read only mode', async () => {
|
||||
mockSplunkOnCallApi.getIncidents.mockResolvedValue([MOCK_INCIDENT]);
|
||||
mockSplunkOnCallApi.getTeams.mockResolvedValue([MOCK_TEAM]);
|
||||
|
||||
const {
|
||||
getByText,
|
||||
getByTitle,
|
||||
getAllByTitle,
|
||||
getByLabelText,
|
||||
queryByTestId,
|
||||
} = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<Incidents readOnly team="test" refreshIncidents={false} />
|
||||
</ApiProvider>,
|
||||
),
|
||||
);
|
||||
await waitFor(() => !queryByTestId('progress'));
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
getByText('user', {
|
||||
exact: false,
|
||||
}),
|
||||
).toBeInTheDocument(),
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
expect(getByText('test-incident')).toBeInTheDocument();
|
||||
expect(getByLabelText('Status warning')).toBeInTheDocument();
|
||||
expect(() => getAllByTitle('Acknowledge')).toThrow(
|
||||
'Unable to find an element with the title: Acknowledge.',
|
||||
);
|
||||
expect(() => getAllByTitle('Resolve')).toThrow(
|
||||
'Unable to find an element with the title: Resolve.',
|
||||
);
|
||||
|
||||
// assert links, mailto and hrefs, date calculation
|
||||
expect(getAllByTitle('View in Splunk On-Call').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('Handle errors', async () => {
|
||||
mockSplunkOnCallApi.getIncidents.mockRejectedValueOnce(
|
||||
new Error('Error occurred'),
|
||||
|
||||
@@ -49,9 +49,10 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
type Props = {
|
||||
refreshIncidents: boolean;
|
||||
team: string;
|
||||
readOnly: boolean;
|
||||
};
|
||||
|
||||
export const Incidents = ({ refreshIncidents, team }: Props) => {
|
||||
export const Incidents = ({ readOnly, refreshIncidents, team }: Props) => {
|
||||
const classes = useStyles();
|
||||
const api = useApi(splunkOnCallApiRef);
|
||||
|
||||
@@ -108,6 +109,7 @@ export const Incidents = ({ refreshIncidents, team }: Props) => {
|
||||
key={index}
|
||||
team={team}
|
||||
incident={incident}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user