Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React from 'react';

import { ComponentMeta, ComponentStory } from '@storybook/react';

import { VariantAnalysisContainer } from '../../view/variant-analysis/VariantAnalysisContainer';
import { VariantAnalysisOutcomePanels } from '../../view/variant-analysis/VariantAnalysisOutcomePanels';
import {
VariantAnalysis,
VariantAnalysisQueryLanguage,
VariantAnalysisRepoStatus,
VariantAnalysisScannedRepository,
VariantAnalysisStatus
} from '../../remote-queries/shared/variant-analysis';

export default {
title: 'Variant Analysis/Variant Analysis Outcome Panels',
component: VariantAnalysisOutcomePanels,
decorators: [
(Story) => (
<VariantAnalysisContainer>
<Story />
</VariantAnalysisContainer>
)
],
} as ComponentMeta<typeof VariantAnalysisOutcomePanels>;

const Template: ComponentStory<typeof VariantAnalysisOutcomePanels> = (args) => (
<VariantAnalysisOutcomePanels {...args} />
);

const buildVariantAnalysis = (data: Partial<VariantAnalysis>) => ({
id: 1,
controllerRepoId: 1,
query: {
name: 'Query name',
filePath: 'example.ql',
language: VariantAnalysisQueryLanguage.Javascript,
},
databases: {},
status: VariantAnalysisStatus.InProgress,
...data,
});

const buildScannedRepo = (id: number, data?: Partial<VariantAnalysisScannedRepository>): VariantAnalysisScannedRepository => ({
repository: {
id: id,
fullName: `octodemo/hello-world-${id}`,
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Pending,
...data,
});

export const WithoutSkippedRepos = Template.bind({});
WithoutSkippedRepos.args = {
variantAnalysis: buildVariantAnalysis({
scannedRepos: [
buildScannedRepo(1, {
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 99_999,
}),
buildScannedRepo(2, {
analysisStatus: VariantAnalysisRepoStatus.Failed,
}),
buildScannedRepo(3, {
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 0,
}),
buildScannedRepo(4),
buildScannedRepo(5),
buildScannedRepo(6),
buildScannedRepo(7),
buildScannedRepo(8),
buildScannedRepo(9),
buildScannedRepo(10),
]
}),
};

export const WithSkippedRepos = Template.bind({});
WithSkippedRepos.args = {
...WithoutSkippedRepos.args,
variantAnalysis: buildVariantAnalysis({
...WithoutSkippedRepos.args.variantAnalysis,
skippedRepos: {
notFoundRepos: {
repositoryCount: 2,
repositories: [
{
fullName: 'octodemo/hello-globe'
},
{
fullName: 'octodemo/hello-planet'
}
]
},
noCodeqlDbRepos: {
repositoryCount: 4,
repositories: [
{
id: 100,
fullName: 'octodemo/no-db-1'
},
{
id: 101,
fullName: 'octodemo/no-db-2'
},
{
id: 102,
fullName: 'octodemo/no-db-3'
},
{
id: 103,
fullName: 'octodemo/no-db-4'
}
]
},
overLimitRepos: {
repositoryCount: 1,
repositories: [
{
id: 201,
fullName: 'octodemo/over-limit-1'
}
]
},
accessMismatchRepos: {
repositoryCount: 1,
repositories: [
{
id: 205,
fullName: 'octodemo/private'
}
]
}
},
}),
};

export const WithOnlyWarningsSkippedRepos = Template.bind({});
WithOnlyWarningsSkippedRepos.args = {
...WithoutSkippedRepos.args,
variantAnalysis: buildVariantAnalysis({
...WithSkippedRepos.args.variantAnalysis,
skippedRepos: {
...WithSkippedRepos.args.variantAnalysis?.skippedRepos,
notFoundRepos: undefined,
noCodeqlDbRepos: undefined,
}
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ import {
} from '../../remote-queries/shared/variant-analysis';
import { VariantAnalysisContainer } from './VariantAnalysisContainer';
import { VariantAnalysisHeader } from './VariantAnalysisHeader';
import { VariantAnalysisOutcomePanels } from './VariantAnalysisOutcomePanels';
import { VariantAnalysisLoading } from './VariantAnalysisLoading';

const variantAnalysis: VariantAnalysisDomainModel = {
id: 1,
controllerRepoId: 1,
actionsWorkflowRunId: 789263,
query: {
name: 'Example query',
filePath: 'example.ql',
language: VariantAnalysisQueryLanguage.Javascript,
},
databases: {},
status: VariantAnalysisStatus.InProgress,
actionsWorkflowRunId: 123,
scannedRepos: [
{
repository: {
Expand Down Expand Up @@ -102,7 +103,59 @@ const variantAnalysis: VariantAnalysisDomainModel = {
},
analysisStatus: VariantAnalysisRepoStatus.Pending,
},
]
],
skippedRepos: {
notFoundRepos: {
repositoryCount: 2,
repositories: [
{
fullName: 'octodemo/hello-globe'
},
{
fullName: 'octodemo/hello-planet'
}
]
},
noCodeqlDbRepos: {
repositoryCount: 4,
repositories: [
{
id: 100,
fullName: 'octodemo/no-db-1'
},
{
id: 101,
fullName: 'octodemo/no-db-2'
},
{
id: 102,
fullName: 'octodemo/no-db-3'
},
{
id: 103,
fullName: 'octodemo/no-db-4'
}
]
},
overLimitRepos: {
repositoryCount: 1,
repositories: [
{
id: 201,
fullName: 'octodemo/over-limit-1'
}
]
},
accessMismatchRepos: {
repositoryCount: 1,
repositories: [
{
id: 205,
fullName: 'octodemo/private'
}
]
}
},
};

function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
Expand All @@ -111,15 +164,18 @@ function getContainerContents(variantAnalysis: VariantAnalysisDomainModel) {
}

return (
<VariantAnalysisHeader
variantAnalysis={variantAnalysis}
onOpenQueryFileClick={() => console.log('Open query')}
onViewQueryTextClick={() => console.log('View query')}
onStopQueryClick={() => console.log('Stop query')}
onCopyRepositoryListClick={() => console.log('Copy repository list')}
onExportResultsClick={() => console.log('Export results')}
onViewLogsClick={() => console.log('View logs')}
/>
<>
<VariantAnalysisHeader
variantAnalysis={variantAnalysis}
onOpenQueryFileClick={() => console.log('Open query')}
onViewQueryTextClick={() => console.log('View query')}
onStopQueryClick={() => console.log('Stop query')}
onCopyRepositoryListClick={() => console.log('Copy repository list')}
onExportResultsClick={() => console.log('Export results')}
onViewLogsClick={() => console.log('View logs')}
/>
<VariantAnalysisOutcomePanels variantAnalysis={variantAnalysis} />
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from 'react';

export const VariantAnalysisAnalyzedRepos = () => {
return <div>This is the analyzed view</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from 'react';

export const VariantAnalysisNoCodeqlDbRepos = () => {
return <div>This is the no database found view</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from 'react';

export const VariantAnalysisNotFoundRepos = () => {
return <div>This is the no access view</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as React from 'react';
import styled from 'styled-components';
import { VSCodeBadge, VSCodePanels, VSCodePanelTab, VSCodePanelView } from '@vscode/webview-ui-toolkit/react';
import { formatDecimal } from '../../pure/number';
import { VariantAnalysis } from '../../remote-queries/shared/variant-analysis';
import { VariantAnalysisAnalyzedRepos } from './VariantAnalysisAnalyzedRepos';
import { VariantAnalysisNotFoundRepos } from './VariantAnalysisNotFoundRepos';
import { VariantAnalysisNoCodeqlDbRepos } from './VariantAnalysisNoCodeqlDbRepos';
import { Alert } from '../common';

export type VariantAnalysisOutcomePanelProps = {
variantAnalysis: VariantAnalysis;
};

const Tab = styled(VSCodePanelTab)`
text-transform: uppercase;
`;

const WarningsContainer = styled.div`
display: flex;
flex-direction: column;
gap: 1em;

margin-top: 1em;

> * {
// Add a margin to the last alert, independent of the number of alerts. This will not add a margin when
// there is no warning to ensure we do not have a margin-top AND a margin-bottom.
&:last-child {
margin-bottom: 1em;
}
}
`;

export const VariantAnalysisOutcomePanels = ({
variantAnalysis
}: VariantAnalysisOutcomePanelProps) => {
const noCodeqlDbRepositoryCount = variantAnalysis.skippedRepos?.noCodeqlDbRepos?.repositoryCount ?? 0;
const notFoundRepositoryCount = variantAnalysis.skippedRepos?.notFoundRepos?.repositoryCount ?? 0;
const overLimitRepositoryCount = variantAnalysis.skippedRepos?.overLimitRepos?.repositoryCount ?? 0;
const accessMismatchRepositoryCount = variantAnalysis.skippedRepos?.accessMismatchRepos?.repositoryCount ?? 0;

const warnings = (
<WarningsContainer>
{overLimitRepositoryCount > 0 && (
<Alert
type="warning"
title="Repository limit exceeded"
message={`The number of requested repositories exceeds the maximum number of repositories supported by multi-repository variant analysis. ${overLimitRepositoryCount} ${overLimitRepositoryCount === 1 ? 'repository was' : 'repositories were'} skipped.`}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm just missing them but I can't see this warning or the "access mismatch" warning as part of the designs. Do we need general warning above the tabs, or is it enough just to have the tabs and then there are warnings inside that explain why repos were skipped?

We can leave the implementation of the warnings inside the tabs for when implementing those tabs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about this and I was getting confused between the different error states. These general warnings are for the "too many repos" and "public controller repo" cases, which are different from the "no access" and "no db" cases which get their own tabs. So these warnings are all good.

Regarding the specific text for them, let's go with this for now as just a placeholder while everything is private. We'll have a discussion offline about what text to use here and in general throughout the extension.

/>
)}
{accessMismatchRepositoryCount > 0 && (
<Alert
type="warning"
title="Access mismatch"
message={`${accessMismatchRepositoryCount} ${accessMismatchRepositoryCount === 1 ? 'repository is' : 'repositories are'} private, while the controller repository is public. ${accessMismatchRepositoryCount === 1 ? 'This repository was' : 'These repositories were'} skipped.`}
/>
)}
</WarningsContainer>
);

if (noCodeqlDbRepositoryCount === 0 && notFoundRepositoryCount === 0) {
return (
<>
{warnings}
<VariantAnalysisAnalyzedRepos />
</>
);
}

return (
<>
{warnings}
<VSCodePanels>
<Tab>
Analyzed
<VSCodeBadge appearance="secondary">{formatDecimal(variantAnalysis.scannedRepos?.length ?? 0)}</VSCodeBadge>
</Tab>
{notFoundRepositoryCount > 0 && (
<Tab>
No access
<VSCodeBadge appearance="secondary">{formatDecimal(notFoundRepositoryCount)}</VSCodeBadge>
</Tab>
)}
{noCodeqlDbRepositoryCount > 0 && (
<Tab>
No database
<VSCodeBadge appearance="secondary">{formatDecimal(noCodeqlDbRepositoryCount)}</VSCodeBadge>
</Tab>
)}
<VSCodePanelView><VariantAnalysisAnalyzedRepos /></VSCodePanelView>
{notFoundRepositoryCount > 0 && <VSCodePanelView><VariantAnalysisNotFoundRepos /></VSCodePanelView>}
{noCodeqlDbRepositoryCount > 0 && <VSCodePanelView><VariantAnalysisNoCodeqlDbRepos /></VSCodePanelView>}
</VSCodePanels>
</>
);
};
Loading