From dcbef85813d5e698a11d927fe5317a6953c77094 Mon Sep 17 00:00:00 2001 From: Sergio Zharinov <zharinov@users.noreply.github.com> Date: Mon, 1 Jun 2020 16:01:09 +0400 Subject: [PATCH] refactor(gitea): Switch to new http wrapper (#6394) --- lib/platform/gitea/gitea-got-wrapper.ts | 68 ------------ lib/platform/gitea/gitea-helper.spec.ts | 4 +- lib/platform/gitea/gitea-helper.ts | 102 +++++++++--------- lib/platform/gitea/index.spec.ts | 3 + lib/platform/gitea/index.ts | 4 +- .../http/__snapshots__/gitea.spec.ts.snap} | 6 +- .../http/gitea.spec.ts} | 21 ++-- lib/util/http/gitea.ts | 62 +++++++++++ 8 files changed, 138 insertions(+), 132 deletions(-) delete mode 100644 lib/platform/gitea/gitea-got-wrapper.ts rename lib/{platform/gitea/__snapshots__/gitea-got-wrapper.spec.ts.snap => util/http/__snapshots__/gitea.spec.ts.snap} (89%) rename lib/{platform/gitea/gitea-got-wrapper.spec.ts => util/http/gitea.spec.ts} (76%) create mode 100644 lib/util/http/gitea.ts diff --git a/lib/platform/gitea/gitea-got-wrapper.ts b/lib/platform/gitea/gitea-got-wrapper.ts deleted file mode 100644 index 26f4d7cff7..0000000000 --- a/lib/platform/gitea/gitea-got-wrapper.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { URL } from 'url'; -import { PLATFORM_TYPE_GITEA } from '../../constants/platforms'; -import got from '../../util/got'; -import { GotApi, GotApiOptions, GotResponse } from '../common'; - -const hostType = PLATFORM_TYPE_GITEA; -let baseUrl: string; - -function getPaginationContainer(body: any): any[] { - if (Array.isArray(body) && body.length) { - return body; - } - if (Array.isArray(body?.data) && body.data.length) { - return body.data; - } - - return null; -} - -async function get(path: string, options?: any): Promise<GotResponse> { - const opts = { - hostType, - baseUrl, - json: true, - ...options, - }; - - const res = await got(path, opts); - const pc = getPaginationContainer(res.body); - if (opts.paginate && pc) { - const url = new URL(res.url); - const total = parseInt(res.headers['x-total-count'] as string, 10); - let nextPage = parseInt(url.searchParams.get('page') || '1', 10); - - while (total && pc.length < total) { - nextPage += 1; - url.searchParams.set('page', nextPage.toString()); - - const nextRes = await got(url.toString(), opts); - pc.push(...getPaginationContainer(nextRes.body)); - } - } - - return res; -} - -const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete']; - -export type GiteaGotOptions = { - paginate?: boolean; - token?: string; -} & GotApiOptions; - -export interface GiteaGotApi extends GotApi<GiteaGotOptions> { - setBaseUrl(url: string): void; -} - -export const api: GiteaGotApi = {} as any; - -for (const x of helpers) { - (api as any)[x] = (path: string, options: any): Promise<GotResponse> => - get(path, { ...options, method: x.toUpperCase() }); -} - -// eslint-disable-next-line @typescript-eslint/unbound-method -api.setBaseUrl = (e: string): void => { - baseUrl = e; -}; diff --git a/lib/platform/gitea/gitea-helper.spec.ts b/lib/platform/gitea/gitea-helper.spec.ts index d1cce2fd5e..5115c9ebb8 100644 --- a/lib/platform/gitea/gitea-helper.spec.ts +++ b/lib/platform/gitea/gitea-helper.spec.ts @@ -1,6 +1,6 @@ import * as httpMock from '../../../test/httpMock'; import { PR_STATE_CLOSED } from '../../constants/pull-requests'; -import { api } from './gitea-got-wrapper'; +import { setBaseUrl } from '../../util/http/gitea'; import * as ght from './gitea-helper'; describe('platform/gitea/gitea-helper', () => { @@ -141,7 +141,7 @@ describe('platform/gitea/gitea-helper', () => { jest.resetAllMocks(); httpMock.reset(); httpMock.setup(); - api.setBaseUrl(baseUrl); + setBaseUrl(baseUrl); }); describe('getCurrentUser', () => { diff --git a/lib/platform/gitea/gitea-helper.ts b/lib/platform/gitea/gitea-helper.ts index fd7e48a0b2..60ba5f35d3 100644 --- a/lib/platform/gitea/gitea-helper.ts +++ b/lib/platform/gitea/gitea-helper.ts @@ -1,8 +1,9 @@ import { URLSearchParams } from 'url'; import { PR_STATE_CLOSED } from '../../constants/pull-requests'; import { BranchStatus } from '../../types'; -import { GotResponse } from '../common'; -import { GiteaGotOptions, api } from './gitea-got-wrapper'; +import { GiteaHttp, GiteaHttpOptions } from '../../util/http/gitea'; + +const giteaHttp = new GiteaHttp(); export type PRState = 'open' | 'closed' | 'all'; export type IssueState = 'open' | 'closed' | 'all'; @@ -200,20 +201,21 @@ function queryParams(params: Record<string, any>): URLSearchParams { return usp; } -export async function getCurrentUser(options?: GiteaGotOptions): Promise<User> { +export async function getCurrentUser( + options?: GiteaHttpOptions +): Promise<User> { const url = 'user'; - const res: GotResponse<User> = await api.get(url, options); - + const res = await giteaHttp.getJson<User>(url, options); return res.body; } export async function searchRepos( params: RepoSearchParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Repo[]> { const query = queryParams(params).toString(); const url = `repos/search?${query}`; - const res: GotResponse<RepoSearchResults> = await api.get(url, { + const res = await giteaHttp.getJson<RepoSearchResults>(url, { ...options, paginate: true, }); @@ -229,11 +231,10 @@ export async function searchRepos( export async function getRepo( repoPath: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Repo> { const url = `repos/${repoPath}`; - const res: GotResponse<Repo> = await api.get(url, options); - + const res = await giteaHttp.getJson<Repo>(url, options); return res.body; } @@ -241,11 +242,11 @@ export async function getRepoContents( repoPath: string, filePath: string, ref?: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<RepoContents> { const query = queryParams(ref ? { ref } : {}).toString(); const url = `repos/${repoPath}/contents/${urlEscape(filePath)}?${query}`; - const res: GotResponse<RepoContents> = await api.get(url, options); + const res = await giteaHttp.getJson<RepoContents>(url, options); if (res.body.content) { res.body.contentString = Buffer.from(res.body.content, 'base64').toString(); @@ -257,10 +258,10 @@ export async function getRepoContents( export async function createPR( repoPath: string, params: PRCreateParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<PR> { const url = `repos/${repoPath}/pulls`; - const res: GotResponse<PR> = await api.post(url, { + const res = await giteaHttp.postJson<PR>(url, { ...options, body: params, }); @@ -272,10 +273,10 @@ export async function updatePR( repoPath: string, idx: number, params: PRUpdateParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<PR> { const url = `repos/${repoPath}/pulls/${idx}`; - const res: GotResponse<PR> = await api.patch(url, { + const res = await giteaHttp.patchJson<PR>(url, { ...options, body: params, }); @@ -286,7 +287,7 @@ export async function updatePR( export async function closePR( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<void> { await updatePR(repoPath, idx, { ...options, @@ -298,11 +299,11 @@ export async function mergePR( repoPath: string, idx: number, method: PRMergeMethod, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<void> { const params: PRMergeParams = { Do: method }; const url = `repos/${repoPath}/pulls/${idx}/merge`; - await api.post(url, { + await giteaHttp.postJson(url, { ...options, body: params, }); @@ -311,22 +312,21 @@ export async function mergePR( export async function getPR( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<PR> { const url = `repos/${repoPath}/pulls/${idx}`; - const res: GotResponse<PR> = await api.get(url, options); - + const res = await giteaHttp.getJson<PR>(url, options); return res.body; } export async function searchPRs( repoPath: string, params: PRSearchParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<PR[]> { const query = queryParams(params).toString(); const url = `repos/${repoPath}/pulls?${query}`; - const res: GotResponse<PR[]> = await api.get(url, { + const res = await giteaHttp.getJson<PR[]>(url, { ...options, paginate: true, }); @@ -337,10 +337,10 @@ export async function searchPRs( export async function createIssue( repoPath: string, params: IssueCreateParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Issue> { const url = `repos/${repoPath}/issues`; - const res: GotResponse<Issue> = await api.post(url, { + const res = await giteaHttp.postJson<Issue>(url, { ...options, body: params, }); @@ -352,10 +352,10 @@ export async function updateIssue( repoPath: string, idx: number, params: IssueUpdateParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Issue> { const url = `repos/${repoPath}/issues/${idx}`; - const res: GotResponse<Issue> = await api.patch(url, { + const res = await giteaHttp.patchJson<Issue>(url, { ...options, body: params, }); @@ -366,7 +366,7 @@ export async function updateIssue( export async function closeIssue( repoPath: string, idx: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<void> { await updateIssue(repoPath, idx, { ...options, @@ -377,11 +377,11 @@ export async function closeIssue( export async function searchIssues( repoPath: string, params: IssueSearchParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Issue[]> { const query = queryParams(params).toString(); const url = `repos/${repoPath}/issues?${query}`; - const res: GotResponse<Issue[]> = await api.get(url, { + const res = await giteaHttp.getJson<Issue[]>(url, { ...options, paginate: true, }); @@ -391,20 +391,20 @@ export async function searchIssues( export async function getRepoLabels( repoPath: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Label[]> { const url = `repos/${repoPath}/labels`; - const res: GotResponse<Label[]> = await api.get(url, options); + const res = await giteaHttp.getJson<Label[]>(url, options); return res.body; } export async function getOrgLabels( orgName: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Label[]> { const url = `orgs/${orgName}/labels`; - const res: GotResponse<Label[]> = await api.get(url, options); + const res = await giteaHttp.getJson<Label[]>(url, options); return res.body; } @@ -413,21 +413,21 @@ export async function unassignLabel( repoPath: string, issue: number, label: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<void> { const url = `repos/${repoPath}/issues/${issue}/labels/${label}`; - await api.delete(url, options); + await giteaHttp.deleteJson(url, options); } export async function createComment( repoPath: string, issue: number, body: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Comment> { const params: CommentCreateParams = { body }; const url = `repos/${repoPath}/issues/${issue}/comments`; - const res: GotResponse<Comment> = await api.post(url, { + const res = await giteaHttp.postJson<Comment>(url, { ...options, body: params, }); @@ -439,11 +439,11 @@ export async function updateComment( repoPath: string, idx: number, body: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Comment> { const params: CommentUpdateParams = { body }; const url = `repos/${repoPath}/issues/comments/${idx}`; - const res: GotResponse<Comment> = await api.patch(url, { + const res = await giteaHttp.patchJson<Comment>(url, { ...options, body: params, }); @@ -454,19 +454,19 @@ export async function updateComment( export async function deleteComment( repoPath, idx: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<void> { const url = `repos/${repoPath}/issues/comments/${idx}`; - await api.delete(url, options); + await giteaHttp.deleteJson(url, options); } export async function getComments( repoPath, issue: number, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Comment[]> { const url = `repos/${repoPath}/issues/${issue}/comments`; - const res: GotResponse<Comment[]> = await api.get(url, options); + const res = await giteaHttp.getJson<Comment[]>(url, options); return res.body; } @@ -475,10 +475,10 @@ export async function createCommitStatus( repoPath: string, branchCommit: string, params: CommitStatusCreateParams, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<CommitStatus> { const url = `repos/${repoPath}/statuses/${branchCommit}`; - const res: GotResponse<CommitStatus> = await api.post(url, { + const res = await giteaHttp.postJson<CommitStatus>(url, { ...options, body: params, }); @@ -523,10 +523,10 @@ function filterStatus(data: CommitStatus[]): CommitStatus[] { export async function getCombinedCommitStatus( repoPath: string, branchName: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<CombinedCommitStatus> { const url = `repos/${repoPath}/commits/${urlEscape(branchName)}/statuses`; - const res: GotResponse<CommitStatus[]> = await api.get(url, { + const res = await giteaHttp.getJson<CommitStatus[]>(url, { ...options, paginate: true, }); @@ -545,10 +545,10 @@ export async function getCombinedCommitStatus( export async function getBranch( repoPath: string, branchName: string, - options?: GiteaGotOptions + options?: GiteaHttpOptions ): Promise<Branch> { const url = `repos/${repoPath}/branches/${urlEscape(branchName)}`; - const res: GotResponse<Branch> = await api.get(url, options); + const res = await giteaHttp.getJson<Branch>(url, options); return res.body; } diff --git a/lib/platform/gitea/index.spec.ts b/lib/platform/gitea/index.spec.ts index 98333eeaf4..0a0ea9f36c 100644 --- a/lib/platform/gitea/index.spec.ts +++ b/lib/platform/gitea/index.spec.ts @@ -18,6 +18,7 @@ import { } from '../../constants/error-messages'; import { logger as _logger } from '../../logger'; import { BranchStatus } from '../../types'; +import { setBaseUrl } from '../../util/http/gitea'; import * as ght from './gitea-helper'; describe('platform/gitea', () => { @@ -201,6 +202,8 @@ describe('platform/gitea', () => { })); global.gitAuthor = { name: 'Renovate', email: 'renovate@example.com' }; + + setBaseUrl('https://gitea.renovatebot.com/api/v1'); }); function initFakeRepo( diff --git a/lib/platform/gitea/index.ts b/lib/platform/gitea/index.ts index ae71c3fc56..62947e6bd2 100644 --- a/lib/platform/gitea/index.ts +++ b/lib/platform/gitea/index.ts @@ -15,6 +15,7 @@ import { PR_STATE_ALL, PR_STATE_OPEN } from '../../constants/pull-requests'; import { logger } from '../../logger'; import { BranchStatus } from '../../types'; import * as hostRules from '../../util/host-rules'; +import { setBaseUrl } from '../../util/http/gitea'; import { sanitize } from '../../util/sanitize'; import { ensureTrailingSlash } from '../../util/url'; import { @@ -35,7 +36,6 @@ import { } from '../common'; import GitStorage, { StatusResult } from '../git/storage'; import { smartTruncate } from '../utils/pr-body'; -import { api } from './gitea-got-wrapper'; import * as helper from './gitea-helper'; type GiteaRenovateConfig = { @@ -224,7 +224,7 @@ const platform: Platform = { } else { logger.debug('Using default Gitea endpoint: ' + defaults.endpoint); } - api.setBaseUrl(defaults.endpoint); + setBaseUrl(defaults.endpoint); let gitAuthor: string; try { diff --git a/lib/platform/gitea/__snapshots__/gitea-got-wrapper.spec.ts.snap b/lib/util/http/__snapshots__/gitea.spec.ts.snap similarity index 89% rename from lib/platform/gitea/__snapshots__/gitea-got-wrapper.spec.ts.snap rename to lib/util/http/__snapshots__/gitea.spec.ts.snap index c72a7fa4e0..42efc4a783 100644 --- a/lib/platform/gitea/__snapshots__/gitea-got-wrapper.spec.ts.snap +++ b/lib/util/http/__snapshots__/gitea.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`platform/gitea/gitea-got-wrapper supports pagination on data property 1`] = ` +exports[`util/http/gitea supports pagination on data property 1`] = ` Array [ Object { "headers": Object { @@ -35,7 +35,7 @@ Array [ ] `; -exports[`platform/gitea/gitea-got-wrapper supports responses without pagination when enabled 1`] = ` +exports[`util/http/gitea supports responses without pagination when enabled 1`] = ` Array [ Object { "headers": Object { @@ -50,7 +50,7 @@ Array [ ] `; -exports[`platform/gitea/gitea-got-wrapper supports root-level pagination 1`] = ` +exports[`util/http/gitea supports root-level pagination 1`] = ` Array [ Object { "headers": Object { diff --git a/lib/platform/gitea/gitea-got-wrapper.spec.ts b/lib/util/http/gitea.spec.ts similarity index 76% rename from lib/platform/gitea/gitea-got-wrapper.spec.ts rename to lib/util/http/gitea.spec.ts index 69ef28291a..bb5ecd2d3c 100644 --- a/lib/platform/gitea/gitea-got-wrapper.spec.ts +++ b/lib/util/http/gitea.spec.ts @@ -1,16 +1,21 @@ import * as httpMock from '../../../test/httpMock'; -import { api } from './gitea-got-wrapper'; +import { getName } from '../../../test/util'; +import { GiteaHttp, setBaseUrl } from './gitea'; -describe('platform/gitea/gitea-got-wrapper', () => { +describe(getName(__filename), () => { const baseUrl = 'https://gitea.renovatebot.com/api/v1'; + let giteaHttp: GiteaHttp; + beforeEach(() => { + giteaHttp = new GiteaHttp(); + jest.resetAllMocks(); httpMock.reset(); httpMock.setup(); - api.setBaseUrl(baseUrl); + setBaseUrl(baseUrl); }); it('supports responses without pagination when enabled', async () => { @@ -19,7 +24,9 @@ describe('platform/gitea/gitea-got-wrapper', () => { .get('/pagination-example-1') .reply(200, { hello: 'world' }); - const res = await api.get('pagination-example-1', { paginate: true }); + const res = await giteaHttp.getJson('pagination-example-1', { + paginate: true, + }); expect(res.body).toEqual({ hello: 'world' }); expect(httpMock.getTrace()).toMatchSnapshot(); }); @@ -34,7 +41,7 @@ describe('platform/gitea/gitea-got-wrapper', () => { .get('/pagination-example-1?page=3') .reply(200, ['mno', 'pqr']); - const res = await api.get(`${baseUrl}/pagination-example-1`, { + const res = await giteaHttp.getJson(`${baseUrl}/pagination-example-1`, { paginate: true, }); httpMock.getTrace(); @@ -54,7 +61,9 @@ describe('platform/gitea/gitea-got-wrapper', () => { .get('/pagination-example-2?page=3') .reply(200, { data: ['mno', 'pqr'] }); - const res = await api.get('pagination-example-2', { paginate: true }); + const res = await giteaHttp.getJson<{ data: any }>('pagination-example-2', { + paginate: true, + }); expect(res.body.data).toHaveLength(6); expect(res.body.data).toEqual(['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr']); expect(httpMock.getTrace()).toMatchSnapshot(); diff --git a/lib/util/http/gitea.ts b/lib/util/http/gitea.ts new file mode 100644 index 0000000000..d9d4867242 --- /dev/null +++ b/lib/util/http/gitea.ts @@ -0,0 +1,62 @@ +import url from 'url'; +import { PLATFORM_TYPE_GITEA } from '../../constants/platforms'; +import { Http, HttpOptions, HttpResponse, InternalHttpOptions } from '.'; + +let baseUrl; +export const setBaseUrl = (newBaseUrl: string): void => { + baseUrl = newBaseUrl.replace(/\/*$/, '/'); +}; + +export interface GiteaHttpOptions extends InternalHttpOptions { + paginate?: boolean; + token?: string; +} + +function getPaginationContainer(body: any): any[] { + if (Array.isArray(body) && body.length) { + return body; + } + if (Array.isArray(body?.data) && body.data.length) { + return body.data; + } + + return null; +} + +function resolveUrl(path: string): URL { + const resolvedUrlString = url.resolve(baseUrl, path); + return new url.URL(resolvedUrlString); +} + +export class GiteaHttp extends Http<GiteaHttpOptions, GiteaHttpOptions> { + constructor(options?: HttpOptions) { + super(PLATFORM_TYPE_GITEA, options); + } + + protected async request<T>( + path: string, + options?: InternalHttpOptions & GiteaHttpOptions + ): Promise<HttpResponse<T> | null> { + const resolvedUrl = resolveUrl(path); + const opts = { + baseUrl, + ...options, + }; + const res = await super.request<T>(resolvedUrl, opts); + const pc = getPaginationContainer(res.body); + if (opts.paginate && pc) { + const total = parseInt(res.headers['x-total-count'] as string, 10); + let nextPage = parseInt(resolvedUrl.searchParams.get('page') || '1', 10); + + while (total && pc.length < total) { + nextPage += 1; + resolvedUrl.searchParams.set('page', nextPage.toString()); + + const nextRes = await super.request<T>(resolvedUrl.toString(), opts); + pc.push(...getPaginationContainer(nextRes.body)); + } + } + + return res; + } +} -- GitLab