Skip to content

Commit

Permalink
feat: ADDON-65107 implement download button for openapi (#932)
Browse files Browse the repository at this point in the history
feat: ADDON-65107 implement download button for openapi
  • Loading branch information
soleksy-splunk authored Nov 8, 2023
1 parent b385759 commit f71d7ec
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ There has to be defined valid `globalConfig.json` and `app.manifest` to have the

Once `ucc-gen` command is executed, OpenAPI description document is located in output `static` subdirectory.

One of ways is to download it via button displayed in top right corner of configuration page.

When add-on is installed to Splunk instance, it is exposed via web and management interface, so is available under following addresses accordingly:

* \[protocol\]://\[domain\]:\[port\]/en-GB/splunkd/__raw/servicesNS/\[user\]/\[appname\]/static/openapi.json
Expand Down
19 changes: 19 additions & 0 deletions ui/src/components/DownloadButton/DownloadButton.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';
import DownloadButton from './DownloadButton';

const meta = {
component: DownloadButton,
title: 'Components/DownloadButton',
} satisfies Meta<typeof DownloadButton>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Base: Story = {
args: {
// using index.json as it needs to be from the same domain
fileUrl: '/index.json',
children: 'Download',
fileNameAfterDownload: 'index.json',
},
};
25 changes: 25 additions & 0 deletions ui/src/components/DownloadButton/DownloadButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import DownloadButton from './DownloadButton';

describe('DownloadButton', () => {
it('Check if download button displays content correctly', async () => {
const btnText = 'some btn text';
const exampleContent = {
fileUrl: 'http://localhost:6006/index.json',
children: btnText,
fileNameAfterDownload: 'fileName',
};

render(<DownloadButton {...exampleContent} />);

const downloadBtn: HTMLAnchorElement = screen.getByTestId('downloadButton');
expect(downloadBtn).toBeInTheDocument();

expect(downloadBtn).toHaveTextContent(btnText);

expect(downloadBtn.href).toEqual(exampleContent.fileUrl);

expect(downloadBtn.download).toEqual(exampleContent.fileNameAfterDownload);
});
});
24 changes: 24 additions & 0 deletions ui/src/components/DownloadButton/DownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { ReactElement } from 'react';
import Button from '@splunk/react-ui/Button';

interface DownloadButtonProps {
// needs to be same domain if not it will just open link
fileUrl: string;
fileNameAfterDownload: string;
children: ReactElement | ReactElement[] | string;
}

function DownloadButton(props: DownloadButtonProps) {
return (
<Button
target="_blank"
to={props.fileUrl}
download={props.fileNameAfterDownload}
data-test="downloadButton"
>
{props.children}
</Button>
);
}

export default DownloadButton;
24 changes: 24 additions & 0 deletions ui/src/components/DownloadButton/OpenApiDownloadBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { createRESTURL } from '@splunk/splunk-utils/url';
import { app } from '@splunk/splunk-utils/config';
import Icon from '@splunk/react-icons/ArrowBroadUnderbarDown';
import DownloadButton from './DownloadButton';

function OpenApiDownloadButton() {
return (
<div>
<DownloadButton
fileUrl={createRESTURL('static/openapi.json', {
app,
owner: 'nobody',
})}
fileNameAfterDownload="openapi.json"
>
<Icon />
<span>OpenAPI.json</span>
</DownloadButton>
</div>
);
}

export default OpenApiDownloadButton;
2 changes: 1 addition & 1 deletion ui/src/components/TextComponent.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import TextComponent from './TextComponent';

const meta = {
component: TextComponent,
title: 'Components/Temporary',
title: 'Components/TextComponent',
argTypes: { handleChange: { action: 'handleChange' } },
render: (props) => {
// due to stories incompatibility, eslint rule is off
Expand Down
5 changes: 4 additions & 1 deletion ui/src/pages/Configuration/ConfigurationPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ErrorBoundary from '../../components/ErrorBoundary';
import CustomTab from '../../components/CustomTab';
import ConfigurationFormView from '../../components/ConfigurationFormView';
import ConfigurationTable from '../../components/ConfigurationTable';
import OpenApiDownloadButton from '../../components/DownloadButton/OpenApiDownloadBtn';

const Row = styled(ColumnLayout.Row)`
padding: 5px 0px;
Expand Down Expand Up @@ -100,7 +101,9 @@ function ConfigurationPage() {
<ColumnLayout gutter={8}>
<Row>
<ColumnLayout.Column span={9}>
<TitleComponent>{_(title)}</TitleComponent>
<TitleComponent>
{_(title)} <OpenApiDownloadButton />
</TitleComponent>
<SubTitleComponent>{_(description || '')}</SubTitleComponent>
</ColumnLayout.Column>
</Row>
Expand Down
2 changes: 2 additions & 0 deletions ui/src/pages/Input/InputPageStyle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const TitleComponent = styled.div.attrs({
&.pageTitle {
font-size: ${variables.fontSizeXXLarge};
margin-bottom: 20px;
display: flex;
justify-content: space-between;
}
`;

Expand Down

0 comments on commit f71d7ec

Please sign in to comment.