feat: hooks refactor to use new plugin api
Signed-off-by: Kamil Wolny <mrwolny@gmail.com>
This commit is contained in:
@@ -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);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user