feat: hooks refactor to use new plugin api

Signed-off-by: Kamil Wolny <mrwolny@gmail.com>
This commit is contained in:
Kamil Wolny
2022-08-09 23:39:14 +01:00
parent e1c5cd7dbe
commit 19eb649999
3 changed files with 20 additions and 297 deletions
@@ -1,100 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const mockGraphQLQuery = jest.fn(() => ({}));
jest.mock('./useOctokitGraphQL', () => ({
useOctokitGraphQL: jest.fn(() => mockGraphQLQuery),
}));
import React from 'react';
import { render } from '@testing-library/react';
import { useGetIssuesByRepoFromGitHub } from './useGetIssuesByRepoFromGitHub';
describe('useGetIssuesBeRepoFromGitHub', () => {
it('should call GitHub API with correct query with fragment for each repo', async () => {
const Helper = () => {
const getIssues = useGetIssuesByRepoFromGitHub();
getIssues(['mrwolny/yo-yo', 'mrwolny/yoyo', 'mrwolny/yo.yo'], 10);
return <div />;
};
render(<Helper />);
expect(mockGraphQLQuery).toHaveBeenCalledTimes(1);
expect(mockGraphQLQuery).toHaveBeenCalledWith(
'\n' +
' \n' +
' fragment issues on Repository {\n' +
' issues(\n' +
' states: OPEN\n' +
' first: 10\n' +
' orderBy: { field: UPDATED_AT, direction: DESC }\n' +
' ) {\n' +
' totalCount\n' +
' edges {\n' +
' node {\n' +
' assignees(first: 10) {\n' +
' edges {\n' +
' node {\n' +
' avatarUrl\n' +
' login\n' +
' }\n' +
' }\n' +
' }\n' +
' author {\n' +
' login\n' +
' avatarUrl\n' +
' url\n' +
' }\n' +
' repository {\n' +
' nameWithOwner\n' +
' }\n' +
' title\n' +
' url\n' +
' participants {\n' +
' totalCount\n' +
' }\n' +
' updatedAt\n' +
' createdAt\n' +
' comments(last: 1) {\n' +
' totalCount\n' +
' }\n' +
' }\n' +
' }\n' +
' }\n' +
' }\n' +
' \n' +
'\n' +
' query {\n' +
' \n' +
' yoyo: repository(name: "yo-yo", owner: "mrwolny") {\n' +
' ...issues\n' +
' }\n' +
' ,\n' +
' yoyox: repository(name: "yoyo", owner: "mrwolny") {\n' +
' ...issues\n' +
' }\n' +
' ,\n' +
' yoyoxx: repository(name: "yo.yo", owner: "mrwolny") {\n' +
' ...issues\n' +
' }\n' +
' \n' +
' } \n' +
' ',
);
});
});
@@ -13,161 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useOctokitGraphQL } from './useOctokitGraphQL';
import { useApi } from '@backstage/core-plugin-api';
type Assignee = {
avatarUrl: string;
login: string;
};
import { useAsyncRetry } from 'react-use';
import { gitHubIssuesApiRef } from '../api';
export type EdgesWithNodes<T> = {
edges: Array<{
node: T;
}>;
};
export type Node<T> = {
node: T;
};
type IssueAuthor = {
login: string;
};
export type Issue = {
assignees: EdgesWithNodes<Assignee>;
author: IssueAuthor;
repository: {
nameWithOwner: string;
};
title: string;
url: string;
participants: {
totalCount: number;
};
createdAt: string;
updatedAt: string;
comments: {
totalCount: number;
};
};
export type RepoIssues = {
issues: {
totalCount: number;
} & EdgesWithNodes<Issue>;
};
export type RepoIssuesQueryResults = Record<string, RepoIssues>;
const createQuery = (
repositories: Array<{
safeName: string;
name: string;
owner: string;
}>,
export const useGetIssuesByRepoFromGitHub = (
repos: Array<string>,
itemsPerRepo: number,
): string => {
const fragment = `
fragment issues on Repository {
issues(
states: OPEN
first: ${itemsPerRepo}
orderBy: { field: UPDATED_AT, direction: DESC }
) {
totalCount
edges {
node {
assignees(first: 10) {
edges {
node {
avatarUrl
login
}
}
}
author {
login
avatarUrl
url
}
repository {
nameWithOwner
}
title
url
participants {
totalCount
}
updatedAt
createdAt
comments(last: 1) {
totalCount
}
}
}
}
}
`;
) => {
const gitHubIssuesApi = useApi(gitHubIssuesApiRef);
const query = `
${fragment}
query {
${repositories.map(
({ safeName, name, owner }) => `
${safeName}: repository(name: "${name}", owner: "${owner}") {
...issues
}
`,
)}
}
`;
return query;
};
export const useGetIssuesByRepoFromGitHub = () => {
const graphql = useOctokitGraphQL<RepoIssuesQueryResults>();
const fn = React.useRef(
async (
repos: Array<string>,
itemsPerRepo: number,
): Promise<Record<string, RepoIssues>> => {
const safeNames: Array<string> = [];
const repositories = repos.map(repo => {
const [owner, name] = repo.split('/');
const safeNameRegex = /-|\./gi;
let safeName = name.replace(safeNameRegex, '');
while (safeNames.includes(safeName)) {
safeName += 'x';
}
safeNames.push(safeName);
return {
safeName,
name,
owner,
};
});
const issuesByRepo: RepoIssuesQueryResults = await graphql(
createQuery(repositories, itemsPerRepo),
const {
value: issues,
loading: isLoading,
retry,
} = useAsyncRetry(async () => {
if (repos.length > 0) {
return await gitHubIssuesApi.fetchIssuesByRepoFromGitHub(
repos,
itemsPerRepo,
);
}
return repositories.reduce((acc, { safeName, name, owner }) => {
acc[`${owner}/${name}`] = issuesByRepo[safeName];
return {};
}, [repos]);
return acc;
}, {} as Record<string, RepoIssues>);
},
);
return fn.current;
return { isLoading, gitHubIssuesByRepo: issues, retry };
};
@@ -1,47 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Octokit } from 'octokit';
import {
useApi,
githubAuthApiRef,
configApiRef,
} from '@backstage/core-plugin-api';
import { readGitHubIntegrationConfigs } from '@backstage/integration';
let octokit: Octokit;
export const useOctokitGraphQL = <T>() => {
const auth = useApi(githubAuthApiRef);
const config = useApi(configApiRef);
const baseUrl = readGitHubIntegrationConfigs(
config.getOptionalConfigArray('integrations.github') ?? [],
)[0].apiBaseUrl;
return (path: string, options?: any): Promise<T> =>
auth
.getAccessToken(['repo'])
.then((token: string) => {
if (!octokit) {
octokit = new Octokit({ auth: token, ...(baseUrl && { baseUrl }) });
}
return octokit;
})
.then(octokitInstance => {
return octokitInstance.graphql(path, options);
});
};