-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Nick Watts <[email protected]> Co-authored-by: Christina Ahrens Roberts <[email protected]>
- Loading branch information
1 parent
71a606f
commit 49a44a9
Showing
12 changed files
with
663 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React from 'react'; | ||
import FooterWrapper from 'src/components/FooterWrapper'; | ||
import { PageBox, PageBoxVariants } from 'src/components/PageBox'; | ||
import TopBar from 'src/components/TopBar'; | ||
import * as Nav from 'src/libs/nav'; | ||
import * as Style from 'src/libs/style'; | ||
import { SupportResourceList } from 'src/support/SupportResourceList'; | ||
|
||
interface SupportPageProps { | ||
queryParams: { | ||
resourceType: string | undefined; | ||
resourceId: string | undefined; | ||
}; | ||
} | ||
|
||
const SupportPage = (props: SupportPageProps) => { | ||
const selectedType = props.queryParams.resourceType; | ||
const resourceId = props.queryParams.resourceId; | ||
const breadcrumbs = `Support > ${selectedType}`; | ||
|
||
return ( | ||
<FooterWrapper> | ||
<TopBar title='Support' href={Nav.getLink('support')}> | ||
{resourceId && ( | ||
<div style={Style.breadcrumb.breadcrumb}> | ||
<div style={Style.noWrapEllipsis}>{breadcrumbs}</div> | ||
<div style={Style.breadcrumb.textUnderBreadcrumb}>{resourceId}</div> | ||
</div> | ||
)} | ||
</TopBar> | ||
<PageBox role='main' variant={PageBoxVariants.light}> | ||
<h2 style={{ ...Style.elements.sectionHeader, textTransform: 'uppercase' }}>Support</h2> | ||
<p>Select resource type.</p> | ||
<SupportResourceList queryParams={{ resourceType: selectedType, resourceId }} /> | ||
</PageBox> | ||
</FooterWrapper> | ||
); | ||
}; | ||
|
||
export const navPaths = [ | ||
{ | ||
name: 'support', | ||
path: '/support', | ||
component: SupportPage, | ||
title: 'Support', | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { ButtonPrimary } from '@terra-ui-packages/components'; | ||
import React, { Fragment, useState } from 'react'; | ||
import { TextInput } from 'src/components/input'; | ||
import colors from 'src/libs/colors'; | ||
import * as Nav from 'src/libs/nav'; | ||
import { ResourcePolicies } from 'src/support/ResourcePolicies'; | ||
import { ResourceTypeSummaryProps } from 'src/support/SupportResourceType'; | ||
import { SupportSummary } from 'src/support/SupportSummary'; | ||
|
||
export const LookupSummaryAndPolicies = (props: ResourceTypeSummaryProps) => { | ||
const { query } = Nav.useRoute(); | ||
const [resourceId, setResourceId] = useState<string>(props.fqResourceId.resourceId); | ||
|
||
function submit() { | ||
Nav.updateSearch({ ...query, resourceId: resourceId || undefined }); | ||
} | ||
|
||
// event hook to clear the resourceId when resourceType changes | ||
React.useEffect(() => { | ||
setResourceId(''); | ||
}, [props.fqResourceId.resourceTypeName]); | ||
|
||
return ( | ||
<> | ||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}> | ||
<div | ||
style={{ | ||
color: colors.dark(), | ||
fontSize: 18, | ||
fontWeight: 600, | ||
display: 'flex', | ||
alignItems: 'center', | ||
marginLeft: '1rem', | ||
}} | ||
> | ||
{props.displayName} | ||
</div> | ||
<TextInput | ||
style={{ marginRight: '1rem', marginLeft: '1rem' }} | ||
placeholder={`Enter ${props.displayName} ID`} | ||
onChange={(newResourceId) => { | ||
setResourceId(newResourceId); | ||
}} | ||
onKeyDown={(e) => { | ||
if (e.key === 'Enter') { | ||
submit(); | ||
} | ||
}} | ||
value={resourceId} | ||
/> | ||
<ButtonPrimary onClick={() => submit()}>Load</ButtonPrimary> | ||
</div> | ||
{!!props.loadSupportSummaryFn && <SupportSummary {...props} />} | ||
<ResourcePolicies {...props} /> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { asMockedFn } from '@terra-ui-packages/test-utils'; | ||
import { act, render, screen } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { Ajax } from 'src/libs/ajax'; | ||
import { FullyQualifiedResourceId } from 'src/libs/ajax/SamResources'; | ||
import { reportError } from 'src/libs/error'; | ||
import { ResourcePolicies } from 'src/support/ResourcePolicies'; | ||
import { ResourceTypeSummaryProps } from 'src/support/SupportResourceType'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
|
||
jest.mock('src/libs/ajax'); | ||
type ErrorExports = typeof import('src/libs/error'); | ||
jest.mock( | ||
'src/libs/error', | ||
(): ErrorExports => ({ | ||
...jest.requireActual('src/libs/error'), | ||
reportError: jest.fn(), | ||
}) | ||
); | ||
|
||
describe('ResourcePolicies', () => { | ||
function setGetResourcePoliciesMock(getResourcePolicies: jest.Mock<Promise<Awaited<object>>, []>) { | ||
asMockedFn(Ajax).mockImplementation(() => { | ||
return { | ||
SamResources: { getResourcePolicies } as Partial<ReturnType<typeof Ajax>['SamResources']>, | ||
} as ReturnType<typeof Ajax>; | ||
}); | ||
} | ||
|
||
it('calls Ajax().SamResources.getResourcePolicies and displays the result', async () => { | ||
// Arrange | ||
const testValue = uuidv4(); | ||
const getResourcePolicies = jest.fn(() => Promise.resolve({ policy: testValue })); | ||
setGetResourcePoliciesMock(getResourcePolicies); | ||
const fqResourceId: FullyQualifiedResourceId = { resourceId: 'resource-id', resourceTypeName: 'resource-type' }; | ||
const props: ResourceTypeSummaryProps = { | ||
displayName: 'display-name', | ||
fqResourceId, | ||
loadSupportSummaryFn: undefined, | ||
}; | ||
|
||
// Act | ||
await act(async () => { | ||
render(<ResourcePolicies {...props} />); | ||
}); | ||
|
||
// Assert | ||
expect(getResourcePolicies).toHaveBeenCalledWith(fqResourceId); | ||
expect(screen.getByText('Sam Policies')).toBeInTheDocument(); | ||
expect(screen.getByText(new RegExp(testValue, 'i'))).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays an error message when getResourcePolicies throws an error', async () => { | ||
// Arrange | ||
const errorMessage = 'test error message'; | ||
const getResourcePolicies = jest.fn(() => Promise.reject(new Error(errorMessage))); | ||
setGetResourcePoliciesMock(getResourcePolicies); | ||
const fqResourceId: FullyQualifiedResourceId = { resourceId: 'resource-id', resourceTypeName: 'resource-type' }; | ||
const props: ResourceTypeSummaryProps = { | ||
displayName: 'display-name', | ||
fqResourceId, | ||
loadSupportSummaryFn: undefined, | ||
}; | ||
|
||
// Act | ||
await act(async () => { | ||
render(<ResourcePolicies {...props} />); | ||
}); | ||
|
||
// Assert | ||
expect(getResourcePolicies).toHaveBeenCalledWith(fqResourceId); | ||
expect(reportError).toHaveBeenCalled(); | ||
}); | ||
|
||
it('displays an error message when getResourcePolicies returns an empty array', async () => { | ||
// Arrange | ||
const getResourcePolicies = jest.fn(() => Promise.resolve([])); | ||
setGetResourcePoliciesMock(getResourcePolicies); | ||
const fqResourceId: FullyQualifiedResourceId = { resourceId: 'resource-id', resourceTypeName: 'resource-type' }; | ||
const props: ResourceTypeSummaryProps = { | ||
displayName: 'display-name', | ||
fqResourceId, | ||
loadSupportSummaryFn: undefined, | ||
}; | ||
|
||
// Act | ||
await act(async () => { | ||
render(<ResourcePolicies {...props} />); | ||
}); | ||
|
||
// Assert | ||
expect(getResourcePolicies).toHaveBeenCalledWith(fqResourceId); | ||
expect(screen.getByText('No policies found')).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays an error message when getResourcePolicies throws 403', async () => { | ||
// Arrange | ||
const getResourcePolicies = jest.fn(() => Promise.reject(new Response('', { status: 403 }))); | ||
setGetResourcePoliciesMock(getResourcePolicies); | ||
const fqResourceId: FullyQualifiedResourceId = { resourceId: 'resource-id', resourceTypeName: 'resource-type' }; | ||
const props: ResourceTypeSummaryProps = { | ||
displayName: 'display-name', | ||
fqResourceId, | ||
loadSupportSummaryFn: undefined, | ||
}; | ||
|
||
// Act | ||
await act(async () => { | ||
render(<ResourcePolicies {...props} />); | ||
}); | ||
|
||
// Assert | ||
expect(getResourcePolicies).toHaveBeenCalledWith(fqResourceId); | ||
expect( | ||
screen.getByText('You do not have permission to view display-name policies or are not on VPN') | ||
).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import ReactJson from '@microlink/react-json-view'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { Ajax } from 'src/libs/ajax'; | ||
import colors from 'src/libs/colors'; | ||
import { reportError } from 'src/libs/error'; | ||
import { ResourceTypeSummaryProps } from 'src/support/SupportResourceType'; | ||
|
||
export const ResourcePolicies = (props: ResourceTypeSummaryProps) => { | ||
const [resourcePolicies, setResourcePolicies] = useState<object>(); | ||
const [errorMessage, setErrorMessage] = useState<string>(''); | ||
|
||
function clear() { | ||
setErrorMessage(''); | ||
setResourcePolicies(undefined); | ||
} | ||
|
||
useEffect(() => { | ||
const loadResourcePolicies = async () => { | ||
clear(); | ||
if (props.fqResourceId.resourceId) { | ||
try { | ||
const policies = await Ajax().SamResources.getResourcePolicies(props.fqResourceId); | ||
Array.isArray(policies) && policies.length === 0 | ||
? setErrorMessage('No policies found') | ||
: setResourcePolicies(policies); | ||
} catch (e) { | ||
if (e instanceof Response && (e.status === 404 || e.status === 403)) { | ||
setErrorMessage(`You do not have permission to view ${props.displayName} policies or are not on VPN`); | ||
} else { | ||
await reportError('Error loading resource policies', e); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
loadResourcePolicies(); | ||
}, [props.fqResourceId, props.displayName]); | ||
|
||
return ( | ||
<> | ||
{!!errorMessage && <div style={{ color: colors.danger(), marginLeft: '1rem' }}>{errorMessage}</div>} | ||
{!!resourcePolicies && ( | ||
<> | ||
<div | ||
style={{ | ||
color: colors.dark(), | ||
fontSize: 18, | ||
fontWeight: 600, | ||
display: 'flex', | ||
alignItems: 'center', | ||
marginLeft: '1rem', | ||
marginTop: '1rem', | ||
}} | ||
> | ||
Sam Policies | ||
</div> | ||
<ReactJson | ||
src={resourcePolicies} | ||
name={false} | ||
style={{ marginLeft: '1rem', border: '1px solid black', padding: '1rem' }} | ||
/> | ||
</> | ||
)} | ||
</> | ||
); | ||
}; |
Oops, something went wrong.