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
Expand Up @@ -73,10 +73,13 @@ export interface VariantAnalysisSkippedRepositories {

export interface VariantAnalysisSkippedRepositoryGroup {
repositoryCount: number,
repositories: Array<{
id?: number,
fullName: string
}>
repositories: VariantAnalysisSkippedRepository[],
}

export interface VariantAnalysisSkippedRepository {
id?: number,
fullName: string,
private?: boolean,
}

export interface VariantAnalysisScannedRepositoryResult {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';

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

import { VariantAnalysisContainer } from '../../view/variant-analysis/VariantAnalysisContainer';
import { VariantAnalysisSkippedRepositoriesTab } from '../../view/variant-analysis/VariantAnalysisSkippedRepositoriesTab';

export default {
title: 'Variant Analysis/Variant Analysis Skipped Repositories Tab',
component: VariantAnalysisSkippedRepositoriesTab,
decorators: [
(Story) => (
<VariantAnalysisContainer>
<Story />
</VariantAnalysisContainer>
)
],
} as ComponentMeta<typeof VariantAnalysisSkippedRepositoriesTab>;

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

export const NoAccessNoOmissions = Template.bind({});
NoAccessNoOmissions.args = {
alertTitle: 'No access',
alertMessage: 'The following repositories could not be scanned because you do not have read access.',
skippedRepositoryGroup: {
repositoryCount: 2,
repositories: [
{
fullName: 'octodemo/hello-globe',
},
{
fullName: 'octodemo/hello-planet',
},
],
},
};

export const NoAccessWithOmissions = Template.bind({});
NoAccessWithOmissions.args = {
...NoAccessNoOmissions.args,
skippedRepositoryGroup: {
repositoryCount: 12345,
repositories: [
{
fullName: 'octodemo/hello-globe',
},
{
fullName: 'octodemo/hello-planet',
},
{
fullName: 'octodemo/hello-universe',
},
],
},
};

export const NoDatabaseNoOmissions = Template.bind({});
NoDatabaseNoOmissions.args = {
alertTitle: 'No database',
alertMessage: 'The following repositories could not be scanned because they do not have an available CodeQL database.',
skippedRepositoryGroup: {
repositoryCount: 2,
repositories: [
{
id: 1,
fullName: 'octodemo/hello-globe',
private: false,
},
{
id: 2,
fullName: 'octodemo/hello-planet',
private: true,
},
],
},
};

export const NoDatabaseWithOmissions = Template.bind({});
NoDatabaseWithOmissions.args = {
...NoDatabaseNoOmissions.args,
skippedRepositoryGroup: {
repositoryCount: 12345,
repositories: [
{
id: 1,
fullName: 'octodemo/hello-globe',
private: false,
},
{
id: 2,
fullName: 'octodemo/hello-planet',
private: true,
},
{
id: 3,
fullName: 'octodemo/hello-universe',
private: false,
},
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';

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

import { VariantAnalysisContainer } from '../../view/variant-analysis/VariantAnalysisContainer';
import { VariantAnalysisSkippedRepositoryRow } from '../../view/variant-analysis/VariantAnalysisSkippedRepositoryRow';

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

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

export const OnlyFullName = Template.bind({});
OnlyFullName.args = {
repository: {
fullName: 'octodemo/hello-globe',
}
};

export const Public = Template.bind({});
Public.args = {
repository: {
fullName: 'octodemo/hello-globe',
private: false,
}
};

export const Private = Template.bind({});
Private.args = {
repository: {
fullName: 'octodemo/hello-globe',
private: true,
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const variantAnalysis: VariantAnalysisDomainModel = {
],
skippedRepos: {
notFoundRepos: {
repositoryCount: 2,
repositoryCount: 9999,
repositories: [
{
fullName: 'octodemo/hello-globe'
Expand All @@ -121,19 +121,23 @@ const variantAnalysis: VariantAnalysisDomainModel = {
repositories: [
{
id: 100,
fullName: 'octodemo/no-db-1'
fullName: 'octodemo/no-db-1',
private: false,
},
{
id: 101,
fullName: 'octodemo/no-db-2'
fullName: 'octodemo/no-db-2',
private: true,
},
{
id: 102,
fullName: 'octodemo/no-db-3'
fullName: 'octodemo/no-db-3',
private: true,
},
{
id: 103,
fullName: 'octodemo/no-db-4'
fullName: 'octodemo/no-db-4',
private: false,
}
]
},
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { VSCodeBadge, VSCodePanels, VSCodePanelTab, VSCodePanelView } from '@vsc
import { formatDecimal } from '../../pure/number';
import { VariantAnalysis, VariantAnalysisScannedRepositoryResult } from '../../remote-queries/shared/variant-analysis';
import { VariantAnalysisAnalyzedRepos } from './VariantAnalysisAnalyzedRepos';
import { VariantAnalysisNotFoundRepos } from './VariantAnalysisNotFoundRepos';
import { VariantAnalysisNoCodeqlDbRepos } from './VariantAnalysisNoCodeqlDbRepos';
import { Alert } from '../common';
import { VariantAnalysisSkippedRepositoriesTab } from './VariantAnalysisSkippedRepositoriesTab';

export type VariantAnalysisOutcomePanelProps = {
variantAnalysis: VariantAnalysis;
Expand Down Expand Up @@ -37,8 +36,8 @@ export const VariantAnalysisOutcomePanels = ({
variantAnalysis,
repositoryResults,
}: VariantAnalysisOutcomePanelProps) => {
const noCodeqlDbRepositoryCount = variantAnalysis.skippedRepos?.noCodeqlDbRepos?.repositoryCount ?? 0;
const notFoundRepositoryCount = variantAnalysis.skippedRepos?.notFoundRepos?.repositoryCount ?? 0;
const noCodeqlDbRepos = variantAnalysis.skippedRepos?.noCodeqlDbRepos;
const notFoundRepos = variantAnalysis.skippedRepos?.notFoundRepos;
const overLimitRepositoryCount = variantAnalysis.skippedRepos?.overLimitRepos?.repositoryCount ?? 0;
const accessMismatchRepositoryCount = variantAnalysis.skippedRepos?.accessMismatchRepos?.repositoryCount ?? 0;

Expand All @@ -61,7 +60,7 @@ export const VariantAnalysisOutcomePanels = ({
</WarningsContainer>
);

if (noCodeqlDbRepositoryCount === 0 && notFoundRepositoryCount === 0) {
if (!noCodeqlDbRepos?.repositoryCount && !notFoundRepos?.repositoryCount) {
return (
<>
{warnings}
Expand All @@ -78,21 +77,33 @@ export const VariantAnalysisOutcomePanels = ({
Analyzed
<VSCodeBadge appearance="secondary">{formatDecimal(variantAnalysis.scannedRepos?.length ?? 0)}</VSCodeBadge>
</Tab>
{notFoundRepositoryCount > 0 && (
{notFoundRepos?.repositoryCount && (
<Tab>
No access
<VSCodeBadge appearance="secondary">{formatDecimal(notFoundRepositoryCount)}</VSCodeBadge>
<VSCodeBadge appearance="secondary">{formatDecimal(notFoundRepos.repositoryCount)}</VSCodeBadge>
</Tab>
)}
{noCodeqlDbRepositoryCount > 0 && (
{noCodeqlDbRepos?.repositoryCount && (
<Tab>
No database
<VSCodeBadge appearance="secondary">{formatDecimal(noCodeqlDbRepositoryCount)}</VSCodeBadge>
<VSCodeBadge appearance="secondary">{formatDecimal(noCodeqlDbRepos.repositoryCount)}</VSCodeBadge>
</Tab>
)}
<VSCodePanelView><VariantAnalysisAnalyzedRepos variantAnalysis={variantAnalysis} repositoryResults={repositoryResults} /></VSCodePanelView>
{notFoundRepositoryCount > 0 && <VSCodePanelView><VariantAnalysisNotFoundRepos /></VSCodePanelView>}
{noCodeqlDbRepositoryCount > 0 && <VSCodePanelView><VariantAnalysisNoCodeqlDbRepos /></VSCodePanelView>}
{notFoundRepos?.repositoryCount &&
<VSCodePanelView>
<VariantAnalysisSkippedRepositoriesTab
alertTitle='No access'
alertMessage='The following repositories could not be scanned because you do not have read access.'
skippedRepositoryGroup={notFoundRepos} />
</VSCodePanelView>}
{noCodeqlDbRepos?.repositoryCount &&
<VSCodePanelView>
<VariantAnalysisSkippedRepositoriesTab
alertTitle='No database'
alertMessage='The following repositories could not be scanned because they do not have an available CodeQL database.'
skippedRepositoryGroup={noCodeqlDbRepos} />
</VSCodePanelView>}
</VSCodePanels>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
import styled from 'styled-components';
import { VariantAnalysisSkippedRepositoryGroup } from '../../remote-queries/shared/variant-analysis';
import { Alert } from '../common';
import { VariantAnalysisSkippedRepositoryRow } from './VariantAnalysisSkippedRepositoryRow';

export type VariantAnalysisSkippedRepositoriesTabProps = {
alertTitle: string,
alertMessage: string,
skippedRepositoryGroup: VariantAnalysisSkippedRepositoryGroup,
};

function getSkipReasonAlert(
title: string,
message: string,
repos: VariantAnalysisSkippedRepositoryGroup
) {
const repositoriesOmittedText = repos.repositoryCount > repos.repositories.length
? ` (Only the first ${repos.repositories.length > 1 ? `${repos.repositories.length} repositories are` : 'repository is'} shown.)`
: '';
return (
<Alert
key='alert'
type='warning'
title={title}
message={message + repositoriesOmittedText}
/>
);
}

const Container = styled.div`
display: flex;
flex-direction: column;
gap: 0.5em;
width: 100%;
`;

export const VariantAnalysisSkippedRepositoriesTab = ({
alertTitle,
alertMessage,
skippedRepositoryGroup,
}: VariantAnalysisSkippedRepositoriesTabProps) => {
return (
<Container>
{getSkipReasonAlert(alertTitle, alertMessage, skippedRepositoryGroup)}
{skippedRepositoryGroup.repositories.map((repo) =>
<VariantAnalysisSkippedRepositoryRow key={`repo/${repo.fullName}`} repository={repo} />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The key only needs to be unique within its parent, so just using repo.fullName would be enough:

Suggested change
<VariantAnalysisSkippedRepositoryRow key={`repo/${repo.fullName}`} repository={repo} />
<VariantAnalysisSkippedRepositoryRow key={repo.fullName} repository={repo} />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My reasoning here was only to make super sure it doesn't conflict with the key for the alert, which is alert. Should be fine since a repo name contains a slash, but I didn't want to rely on that or risk conflicts if another component is added to this container. So unless there's more of a benefit to simplifying it I'd prefer to keep it as it is.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm happy to leave it as-is, but I don't think you need the key on the alert at all since it's not part of the list. React only uses the keys for change detection in a map call/list. Even when the component is a sibling of a map call, it shouldn't be necessary to add the key.

)}
</Container>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { VSCodeBadge, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
import * as React from 'react';
import styled from 'styled-components';
import { Codicon, WarningIcon } from '../common';
import { VariantAnalysisSkippedRepository as SkippedRepo } from '../../remote-queries/shared/variant-analysis';

export type VariantAnalysisSkippedRepositoryRowProps = {
repository: SkippedRepo,
};

const Row = styled.div`
display: flex;
flex-direction: row;
gap: 0.5em;
align-items: center;
`;

const ChevronIcon = styled(Codicon)`
color: var(--vscode-disabledForeground);
`;

const PrivacyText = styled.span`
font-size: small;
color: var(--vscode-descriptionForeground);
`;

function getPrivacyElement(isPrivate: boolean | undefined) {
if (isPrivate === undefined) {
return undefined;
}
const text = isPrivate ? 'private' : 'public';
return <PrivacyText>{text}</PrivacyText>;
}

export const VariantAnalysisSkippedRepositoryRow = ({
repository,
}: VariantAnalysisSkippedRepositoryRowProps) => {
return (
<Row>
<VSCodeCheckbox />
<ChevronIcon name='chevron-right' label='Expand' />
<VSCodeBadge>-</VSCodeBadge>
<span>{repository.fullName}</span>
{getPrivacyElement(repository.private)}
<WarningIcon />
</Row>
);
};
Loading