lowercase h in github, in plugins/github-issues

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2022-10-24 15:41:14 +02:00
parent e3f5f3a471
commit ead285b9e4
24 changed files with 120 additions and 108 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-github-issues': minor
---
BREAKING: Changed the casing of all exported types to have a lowercase "h" in "github". E.g. "GitHubIssuesPage" was renamed to "GithubIssuesPage". Please rename your imports where necessary.
+12 -12
View File
@@ -9,7 +9,15 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
// @public (undocumented)
export const GitHubIssuesCard: (props: GitHubIssuesProps) => JSX.Element;
export interface GithubIssuesByRepoOptions {
// (undocumented)
filterBy?: GithubIssuesFilters;
// (undocumented)
orderBy?: GithubIssuesOrdering;
}
// @public (undocumented)
export const GithubIssuesCard: (props: GithubIssuesProps) => JSX.Element;
// @public (undocumented)
export interface GithubIssuesFilters {
@@ -36,10 +44,10 @@ export interface GithubIssuesOrdering {
}
// @public (undocumented)
export const GitHubIssuesPage: (props: GitHubIssuesProps) => JSX.Element;
export const GithubIssuesPage: (props: GithubIssuesProps) => JSX.Element;
// @public (undocumented)
export const gitHubIssuesPlugin: BackstagePlugin<
export const githubIssuesPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
@@ -48,20 +56,12 @@ export const gitHubIssuesPlugin: BackstagePlugin<
>;
// @public (undocumented)
export type GitHubIssuesProps = {
export type GithubIssuesProps = {
itemsPerPage?: number;
itemsPerRepo?: number;
filterBy?: GithubIssuesFilters;
orderBy?: GithubIssuesOrdering;
};
// @public (undocumented)
export interface GitubIssuesByRepoOptions {
// (undocumented)
filterBy?: GithubIssuesFilters;
// (undocumented)
orderBy?: GithubIssuesOrdering;
}
// (No @packageDocumentation comment for this package)
```
+8 -8
View File
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { createDevApp } from '@backstage/dev-utils';
import {
@@ -20,21 +21,20 @@ import {
catalogApiRef,
EntityProvider,
} from '@backstage/plugin-catalog-react';
import { gitHubIssuesPlugin, GitHubIssuesPage } from '../src';
import { GitHubIssuesApi, gitHubIssuesApiRef } from '../src/api';
import { githubIssuesPlugin, GithubIssuesPage } from '../src';
import { GithubIssuesApi, githubIssuesApiRef } from '../src/api';
import testData from './__fixtures__/component-issues-data.json';
createDevApp()
.registerPlugin(gitHubIssuesPlugin)
.registerPlugin(githubIssuesPlugin)
.registerApi({
api: gitHubIssuesApiRef,
api: githubIssuesApiRef,
deps: {},
factory: () =>
({
fetchIssuesByRepoFromGitHub: async () => testData,
} as GitHubIssuesApi),
fetchIssuesByRepoFromGithub: async () => testData,
} as GithubIssuesApi),
})
.registerApi({
api: catalogApiRef,
@@ -59,7 +59,7 @@ createDevApp()
kind: 'Component',
}}
>
<GitHubIssuesPage />
<GithubIssuesPage />
</EntityProvider>
),
})
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const mockGraphQLQuery = jest.fn(() => ({}));
jest.mock('octokit', () => ({
Octokit: jest.fn(() => ({ graphql: mockGraphQLQuery })),
@@ -20,8 +21,8 @@ jest.mock('octokit', () => ({
import { ConfigApi, ErrorApi } from '@backstage/core-plugin-api';
import { ForwardedError } from '@backstage/errors';
import { createFilterByClause, gitHubIssuesApi } from './gitHubIssuesApi';
import type { GithubIssuesFilters } from './gitHubIssuesApi';
import { createFilterByClause, githubIssuesApi } from './githubIssuesApi';
import type { GithubIssuesFilters } from './githubIssuesApi';
function getFragment(
filterBy = '',
@@ -84,21 +85,21 @@ function getFragment(
' ...issues\n' +
' }\n' +
' \n' +
' } \n' +
' }\n' +
' '
);
}
describe('gitHubIssuesApi', () => {
describe('fetchIssuesByRepoFromGitHub', () => {
let api: ReturnType<typeof gitHubIssuesApi>;
describe('githubIssuesApi', () => {
describe('fetchIssuesByRepoFromGithub', () => {
let api: ReturnType<typeof githubIssuesApi>;
afterEach(() => {
jest.clearAllMocks();
});
beforeEach(() => {
api = gitHubIssuesApi(
api = githubIssuesApi(
{ getAccessToken: jest.fn() },
{
getOptionalConfigArray: jest.fn(),
@@ -108,7 +109,7 @@ describe('gitHubIssuesApi', () => {
});
it('should call GitHub API with correct query with fragment for each repo', async () => {
await api.fetchIssuesByRepoFromGitHub(
await api.fetchIssuesByRepoFromGithub(
['mrwolny/yo-yo', 'mrwolny/yoyo', 'mrwolny/yo.yo'],
10,
);
@@ -118,7 +119,7 @@ describe('gitHubIssuesApi', () => {
});
it('should call Github API with the correct filterBy and orderBy clauses', async () => {
await api.fetchIssuesByRepoFromGitHub(
await api.fetchIssuesByRepoFromGithub(
['mrwolny/yo-yo', 'mrwolny/yoyo', 'mrwolny/yo.yo'],
10,
{
@@ -221,7 +222,7 @@ describe('gitHubIssuesApi', () => {
}),
);
const api = gitHubIssuesApi(
const api = githubIssuesApi(
{ getAccessToken: jest.fn() },
{
getOptionalConfigArray: jest.fn(),
@@ -229,7 +230,7 @@ describe('gitHubIssuesApi', () => {
{ post: jest.fn() } as unknown as ErrorApi,
);
const data = await api.fetchIssuesByRepoFromGitHub(
const data = await api.fetchIssuesByRepoFromGithub(
['mrwolny/yo-yo', 'mrwolny/notfound'],
10,
);
@@ -291,7 +292,7 @@ describe('gitHubIssuesApi', () => {
}),
);
const api = gitHubIssuesApi(
const api = githubIssuesApi(
{ getAccessToken: jest.fn() },
{
getOptionalConfigArray: jest.fn(),
@@ -299,7 +300,7 @@ describe('gitHubIssuesApi', () => {
{ post: jest.fn() } as unknown as ErrorApi,
);
const data = await api.fetchIssuesByRepoFromGitHub(
const data = await api.fetchIssuesByRepoFromGithub(
['mrwolny/notfound'],
10,
);
@@ -332,7 +333,7 @@ describe('gitHubIssuesApi', () => {
const mockErrorApi = { post: jest.fn() };
const api = gitHubIssuesApi(
const api = githubIssuesApi(
{ getAccessToken: jest.fn() },
{
getOptionalConfigArray: jest.fn(),
@@ -340,7 +341,7 @@ describe('gitHubIssuesApi', () => {
mockErrorApi as unknown as ErrorApi,
);
await api.fetchIssuesByRepoFromGitHub(['mrwolny/notfound'], 10);
await api.fetchIssuesByRepoFromGithub(['mrwolny/notfound'], 10);
expect(mockErrorApi.post).toHaveBeenCalledTimes(1);
expect(mockErrorApi.post).toHaveBeenCalledWith(
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Octokit } from 'octokit';
import {
createApiRef,
@@ -71,7 +72,7 @@ export type RepoIssues = {
export type IssuesByRepo = Record<string, RepoIssues>;
/** @internal */
export type GitHubIssuesApi = ReturnType<typeof gitHubIssuesApi>;
export type GithubIssuesApi = ReturnType<typeof githubIssuesApi>;
/**
* @public
@@ -96,18 +97,18 @@ export interface GithubIssuesOrdering {
/**
* @public
*/
export interface GitubIssuesByRepoOptions {
export interface GithubIssuesByRepoOptions {
filterBy?: GithubIssuesFilters;
orderBy?: GithubIssuesOrdering;
}
/** @internal */
export const gitHubIssuesApiRef = createApiRef<GitHubIssuesApi>({
export const githubIssuesApiRef = createApiRef<GithubIssuesApi>({
id: 'plugin.githubissues.service',
});
/** @internal */
export const gitHubIssuesApi = (
export const githubIssuesApi = (
githubAuthApi: OAuthApi,
configApi: ConfigApi,
errorApi: ErrorApi,
@@ -128,7 +129,7 @@ export const gitHubIssuesApi = (
return octokit.graphql;
};
const fetchIssuesByRepoFromGitHub = async (
const fetchIssuesByRepoFromGithub = async (
repos: Array<string>,
itemsPerRepo: number,
{
@@ -137,7 +138,7 @@ export const gitHubIssuesApi = (
field: 'UPDATED_AT',
direction: 'DESC',
},
}: GitubIssuesByRepoOptions = {},
}: GithubIssuesByRepoOptions = {},
): Promise<IssuesByRepo> => {
const graphql = await getOctokit();
const safeNames: Array<string> = [];
@@ -186,7 +187,7 @@ export const gitHubIssuesApi = (
}, {} as IssuesByRepo);
};
return { fetchIssuesByRepoFromGitHub };
return { fetchIssuesByRepoFromGithub };
};
function formatFilterValue(
@@ -224,7 +225,7 @@ function createIssueByRepoQuery(
owner: string;
}>,
itemsPerRepo: number,
{ filterBy, orderBy }: GitubIssuesByRepoOptions,
{ filterBy, orderBy }: GithubIssuesByRepoOptions,
): string {
const fragment = `
fragment issues on Repository {
@@ -278,7 +279,7 @@ function createIssueByRepoQuery(
}
`,
)}
}
}
`;
return query;
+2 -1
View File
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './gitHubIssuesApi';
export * from './githubIssuesApi';
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import {
@@ -21,10 +22,8 @@ import {
CatalogApi,
} from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { GitHubIssuesApi, gitHubIssuesApiRef, Issue } from '../../api';
import { GitHubIssues } from './GitHubIssues';
import { GithubIssuesApi, githubIssuesApiRef, Issue } from '../../api';
import { GithubIssues } from './GithubIssues';
const getTestIssue = (overwrites: Partial<Issue> = {}): { node: Issue } => ({
node: {
@@ -80,10 +79,10 @@ const mockCatalogApi = {
getEntities: () => ({}),
} as CatalogApi;
describe('GitHubIssues', () => {
describe('GithubIssues', () => {
it('should render correctly when there are no issues in GitHub', async () => {
const mockApi = {
fetchIssuesByRepoFromGitHub: async () => ({
fetchIssuesByRepoFromGithub: async () => ({
backstage: {
issues: {
totalCount: 0,
@@ -91,17 +90,17 @@ describe('GitHubIssues', () => {
},
},
}),
} as GitHubIssuesApi;
} as GithubIssuesApi;
const apis = [
[gitHubIssuesApiRef, mockApi],
[githubIssuesApiRef, mockApi],
[catalogApiRef, mockCatalogApi],
] as const;
const { getByTestId } = await renderInTestApp(
<TestApiProvider apis={apis}>
<EntityProvider entity={entityComponent}>
<GitHubIssues />
<GithubIssues />
</EntityProvider>
</TestApiProvider>,
);
@@ -118,7 +117,7 @@ describe('GitHubIssues', () => {
});
const mockApi = {
fetchIssuesByRepoFromGitHub: async () => ({
fetchIssuesByRepoFromGithub: async () => ({
backstage: {
issues: {
totalCount: 1,
@@ -126,16 +125,16 @@ describe('GitHubIssues', () => {
},
},
}),
} as GitHubIssuesApi;
} as GithubIssuesApi;
const apis = [
[gitHubIssuesApiRef, mockApi],
[githubIssuesApiRef, mockApi],
[catalogApiRef, mockCatalogApi],
] as const;
const { getByText, getByTestId } = await renderInTestApp(
<TestApiProvider apis={apis}>
<EntityProvider entity={entityComponent}>
<GitHubIssues />
<GithubIssues />
</EntityProvider>
</TestApiProvider>,
);
@@ -13,41 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React from 'react';
import { Box, IconButton, Typography } from '@material-ui/core';
import { InfoCard, Progress } from '@backstage/core-components';
import RefreshIcon from '@material-ui/icons/Refresh';
import { useEntityGitHubRepositories } from '../../hooks/useEntityGitHubRepositories';
import { useGetIssuesByRepoFromGitHub } from '../../hooks/useGetIssuesByRepoFromGitHub';
import { useEntityGithubRepositories } from '../../hooks/useEntityGithubRepositories';
import { useGetIssuesByRepoFromGithub } from '../../hooks/useGetIssuesByRepoFromGithub';
import { IssuesList } from './IssuesList';
import { NoRepositoriesInfo } from './NoRepositoriesInfo';
import type {
GithubIssuesFilters,
GithubIssuesOrdering,
} from '../../api/gitHubIssuesApi';
} from '../../api/githubIssuesApi';
/**
* @public
*/
export type GitHubIssuesProps = {
export type GithubIssuesProps = {
itemsPerPage?: number;
itemsPerRepo?: number;
filterBy?: GithubIssuesFilters;
orderBy?: GithubIssuesOrdering;
};
export const GitHubIssues = (props: GitHubIssuesProps) => {
export const GithubIssues = (props: GithubIssuesProps) => {
const { itemsPerPage = 10, itemsPerRepo = 40, filterBy, orderBy } = props;
const { repositories } = useEntityGitHubRepositories();
const { repositories } = useEntityGithubRepositories();
const {
isLoading,
gitHubIssuesByRepo: issuesByRepository,
githubIssuesByRepo: issuesByRepository,
retry,
} = useGetIssuesByRepoFromGitHub(repositories, itemsPerRepo, {
} = useGetIssuesByRepoFromGithub(repositories, itemsPerRepo, {
filterBy,
orderBy,
});
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Typography, Box, Avatar, makeStyles } from '@material-ui/core';
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { ChatIcon } from '@backstage/core-components';
import { Box, Badge } from '@material-ui/core';
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { DateTime } from 'luxon';
import {
Box,
Paper,
@@ -25,7 +25,6 @@ import {
} from '@material-ui/core';
import { Assignees } from './Assignees';
import { CommentsCount } from './CommentsCount';
import Divider from '@material-ui/core/Divider';
type IssueCardProps = {
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { IssueCard } from './IssueCard';
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Select, SelectedItems, SelectItem } from '@backstage/core-components';
import { makeStyles, Box, Typography } from '@material-ui/core';
type RepositoryFiltersProps = {
items: Array<SelectItem>;
totalIssuesInGitHub: number;
totalIssuesInGithub: number;
placeholder: string;
onChange: (active: Array<string>) => void;
};
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './Filters';
@@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React from 'react';
import { Box } from '@material-ui/core';
import { Pagination } from '@material-ui/lab';
import { IssueCard } from '../IssueCard';
import { IssuesByRepo } from '../../../api';
import { RepositoryFilters } from './Filters';
@@ -60,7 +59,7 @@ export const IssuesList = ({
[issuesByRepository],
);
const totalIssuesInGitHub = React.useMemo(
const totalIssuesInGithub = React.useMemo(
() =>
issuesByRepository
? Object.values(issuesByRepository).reduce(
@@ -113,12 +112,12 @@ export const IssuesList = ({
{issues.length > 0 && (
<RepositoryFilters
placeholder={`All repositories ${getIssuesCountForFilterLabel(
totalIssuesInGitHub,
totalIssuesInGithub,
issues.length,
)}`}
items={filters}
onChange={setActiveFilter}
totalIssuesInGitHub={totalIssuesInGitHub}
totalIssuesInGithub={totalIssuesInGithub}
/>
)}
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './IssuesList';
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React from 'react';
import { EmptyState } from '@backstage/core-components';
export const NoRepositoriesInfo = () => {
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './NoRepositoriesInfo';
@@ -13,4 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './GitHubIssues';
export * from './GithubIssues';
@@ -25,7 +25,7 @@ export const getProjectNameFromEntity = (entity: Entity): string => {
return entity?.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION] ?? '';
};
export function useEntityGitHubRepositories() {
export function useEntityGithubRepositories() {
const { entity } = useEntity();
const catalogApi = useApi(catalogApiRef);
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useApi } from '@backstage/core-plugin-api';
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { gitHubIssuesApiRef, GitubIssuesByRepoOptions } from '../api';
import { githubIssuesApiRef, GithubIssuesByRepoOptions } from '../api';
export const useGetIssuesByRepoFromGitHub = (
export const useGetIssuesByRepoFromGithub = (
repos: Array<string>,
itemsPerRepo: number,
options?: GitubIssuesByRepoOptions,
options?: GithubIssuesByRepoOptions,
) => {
const gitHubIssuesApi = useApi(gitHubIssuesApiRef);
const githubIssuesApi = useApi(githubIssuesApiRef);
const {
value: issues,
@@ -31,7 +31,7 @@ export const useGetIssuesByRepoFromGitHub = (
retry,
} = useAsyncRetry(async () => {
if (repos.length > 0) {
return await gitHubIssuesApi.fetchIssuesByRepoFromGitHub(
return await githubIssuesApi.fetchIssuesByRepoFromGithub(
repos,
itemsPerRepo,
options,
@@ -41,5 +41,5 @@ export const useGetIssuesByRepoFromGitHub = (
return {};
}, [repos]);
return { isLoading, gitHubIssuesByRepo: issues, retry };
return { isLoading, githubIssuesByRepo: issues, retry };
};
+8 -8
View File
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
gitHubIssuesPlugin,
GitHubIssuesPage,
GitHubIssuesCard,
} from './plugin';
export type { GitHubIssuesProps } from './components/GitHubIssues';
export {
githubIssuesPlugin,
GithubIssuesPage,
GithubIssuesCard,
} from './plugin';
export type { GithubIssuesProps } from './components/GithubIssues';
export type {
GithubIssuesFilters,
GithubIssuesOrdering,
GitubIssuesByRepoOptions,
} from './api/gitHubIssuesApi';
GithubIssuesByRepoOptions,
} from './api/githubIssuesApi';
+3 -2
View File
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { gitHubIssuesPlugin } from './plugin';
import { githubIssuesPlugin } from './plugin';
describe('github-issues', () => {
it('should export plugin', () => {
expect(gitHubIssuesPlugin).toBeDefined();
expect(githubIssuesPlugin).toBeDefined();
});
});
+11 -11
View File
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createPlugin,
createApiFactory,
@@ -22,23 +23,22 @@ import {
errorApiRef,
githubAuthApiRef,
} from '@backstage/core-plugin-api';
import { gitHubIssuesApi, gitHubIssuesApiRef } from './api';
import { githubIssuesApi, githubIssuesApiRef } from './api';
import { rootRouteRef } from './routes';
/** @public */
export const gitHubIssuesPlugin = createPlugin({
export const githubIssuesPlugin = createPlugin({
id: 'github-issues',
apis: [
createApiFactory({
api: gitHubIssuesApiRef,
api: githubIssuesApiRef,
deps: {
configApi: configApiRef,
githubAuthApi: githubAuthApiRef,
errorApi: errorApiRef,
},
factory: ({ configApi, githubAuthApi, errorApi }) =>
gitHubIssuesApi(githubAuthApi, configApi, errorApi),
githubIssuesApi(githubAuthApi, configApi, errorApi),
}),
],
routes: {
@@ -47,21 +47,21 @@ export const gitHubIssuesPlugin = createPlugin({
});
/** @public */
export const GitHubIssuesCard = gitHubIssuesPlugin.provide(
export const GithubIssuesCard = githubIssuesPlugin.provide(
createComponentExtension({
name: 'GitHubIssuesCard',
name: 'GithubIssuesCard',
component: {
lazy: () => import('./components/GitHubIssues').then(m => m.GitHubIssues),
lazy: () => import('./components/GithubIssues').then(m => m.GithubIssues),
},
}),
);
/** @public */
export const GitHubIssuesPage = gitHubIssuesPlugin.provide(
export const GithubIssuesPage = githubIssuesPlugin.provide(
createRoutableExtension({
name: 'GitHubIssuesPage',
name: 'GithubIssuesPage',
component: () =>
import('./components/GitHubIssues').then(m => m.GitHubIssues),
import('./components/GithubIssues').then(m => m.GithubIssues),
mountPoint: rootRouteRef,
}),
);