From 030b1a61a45a98557ddfe22732ffa1807619cba5 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov <zharinov@users.noreply.github.com> Date: Mon, 10 Jan 2022 00:22:27 +0300 Subject: [PATCH] refactor(util/http): Strict null checks for http utils (#13416) * refactor(util/http): Strict null checks for http utils * More tests for queue.ts Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- lib/util/http/auth.ts | 31 ++++-- lib/util/http/bitbucket-server.ts | 2 +- lib/util/http/bitbucket.ts | 7 +- lib/util/http/gitea.ts | 22 ++-- lib/util/http/github.spec.ts | 6 +- lib/util/http/github.ts | 175 ++++++++++++++++-------------- lib/util/http/gitlab.ts | 22 ++-- lib/util/http/host-rules.ts | 18 ++- lib/util/http/index.spec.ts | 38 +++++-- lib/util/http/index.ts | 12 +- tsconfig.strict.json | 4 +- 11 files changed, 193 insertions(+), 144 deletions(-) diff --git a/lib/util/http/auth.ts b/lib/util/http/auth.ts index cd182af48d..e083970ca2 100644 --- a/lib/util/http/auth.ts +++ b/lib/util/http/auth.ts @@ -1,5 +1,5 @@ import is from '@sindresorhus/is'; -import type { NormalizedOptions } from 'got'; +import type { Options } from 'got'; import { GITHUB_API_USING_HOST_TYPES, GITLAB_API_USING_HOST_TYPES, @@ -14,10 +14,14 @@ export function applyAuthorization(inOptions: GotOptions): GotOptions { return options; } + options.headers ??= {}; if (options.token) { if (options.hostType === PlatformId.Gitea) { options.headers.authorization = `token ${options.token}`; - } else if (GITHUB_API_USING_HOST_TYPES.includes(options.hostType)) { + } else if ( + options.hostType && + GITHUB_API_USING_HOST_TYPES.includes(options.hostType) + ) { options.headers.authorization = `token ${options.token}`; if (options.token.startsWith('x-access-token:')) { const appToken = options.token.replace('x-access-token:', ''); @@ -29,7 +33,10 @@ export function applyAuthorization(inOptions: GotOptions): GotOptions { ); } } - } else if (GITLAB_API_USING_HOST_TYPES.includes(options.hostType)) { + } else if ( + options.hostType && + GITLAB_API_USING_HOST_TYPES.includes(options.hostType) + ) { // GitLab versions earlier than 12.2 only support authentication with // a personal access token, which is 20 characters long. if (options.token.length === 20) { @@ -51,7 +58,7 @@ export function applyAuthorization(inOptions: GotOptions): GotOptions { } else if (options.password !== undefined) { // Otherwise got will add username and password to url and header const auth = Buffer.from( - `${options.username || ''}:${options.password}` + `${options.username ?? ''}:${options.password}` ).toString('base64'); options.headers.authorization = `Basic ${auth}`; delete options.username; @@ -61,20 +68,20 @@ export function applyAuthorization(inOptions: GotOptions): GotOptions { } // isAmazon return true if request options contains Amazon related headers -function isAmazon(options: NormalizedOptions): boolean { - return options.search?.includes('X-Amz-Algorithm'); +function isAmazon(options: Options): boolean { + return !!options.search?.includes('X-Amz-Algorithm'); } // isAzureBlob return true if request options contains Azure container registry related data -function isAzureBlob(options: NormalizedOptions): boolean { - return ( +function isAzureBlob(options: Options): boolean { + return !!( options.hostname?.endsWith('.blob.core.windows.net') && // lgtm [js/incomplete-url-substring-sanitization] options.href?.includes('/docker/registry') ); } // removeAuthorization from the redirect options -export function removeAuthorization(options: NormalizedOptions): void { +export function removeAuthorization(options: Options): void { if (!options.password && !options.headers?.authorization) { return; } @@ -83,7 +90,7 @@ export function removeAuthorization(options: NormalizedOptions): void { if (isAmazon(options) || isAzureBlob(options)) { // if there is no port in the redirect URL string, then delete it from the redirect options. // This can be evaluated for removal after upgrading to Got v10 - const portInUrl = options.href.split('/')[2].split(':')[1]; + const portInUrl = options.href?.split?.('/')?.[2]?.split(':')?.[1]; // istanbul ignore next if (!portInUrl) { delete options.port; // Redirect will instead use 80 or 443 for HTTP or HTTPS respectively @@ -91,7 +98,9 @@ export function removeAuthorization(options: NormalizedOptions): void { // registry is hosted on Amazon or Azure blob, redirect url includes // authentication which is not required and should be removed - delete options.headers.authorization; + if (options?.headers?.authorization) { + delete options.headers.authorization; + } delete options.username; delete options.password; } diff --git a/lib/util/http/bitbucket-server.ts b/lib/util/http/bitbucket-server.ts index 6d2d91aa3e..dc3d8eaa0a 100644 --- a/lib/util/http/bitbucket-server.ts +++ b/lib/util/http/bitbucket-server.ts @@ -15,7 +15,7 @@ export class BitbucketServerHttp extends Http { protected override request<T>( path: string, options?: InternalHttpOptions - ): Promise<HttpResponse<T> | null> { + ): Promise<HttpResponse<T>> { const url = resolveBaseUrl(baseUrl, path); const opts = { baseUrl, diff --git a/lib/util/http/bitbucket.ts b/lib/util/http/bitbucket.ts index 1eaaab9e53..1445d0e847 100644 --- a/lib/util/http/bitbucket.ts +++ b/lib/util/http/bitbucket.ts @@ -15,11 +15,8 @@ export class BitbucketHttp extends Http { protected override request<T>( url: string | URL, options?: InternalHttpOptions - ): Promise<HttpResponse<T> | null> { - const opts = { - baseUrl, - ...options, - }; + ): Promise<HttpResponse<T>> { + const opts = { baseUrl, ...options }; return super.request<T>(url, opts); } } diff --git a/lib/util/http/gitea.ts b/lib/util/http/gitea.ts index 110589be7d..dc154fb99e 100644 --- a/lib/util/http/gitea.ts +++ b/lib/util/http/gitea.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import { PlatformId } from '../../constants'; import { resolveBaseUrl } from '../url'; import { Http, HttpOptions, HttpResponse, InternalHttpOptions } from '.'; @@ -12,12 +13,13 @@ export interface GiteaHttpOptions extends InternalHttpOptions { token?: string; } -function getPaginationContainer(body: any): any[] { - if (Array.isArray(body) && body.length) { - return body; +function getPaginationContainer<T = unknown>(body: unknown): T[] | null { + if (is.array(body) && body.length) { + return body as T[]; } - if (Array.isArray(body?.data) && body.data.length) { - return body.data; + + if (is.plainObject(body) && is.array(body?.data) && body.data.length) { + return body.data as T[]; } return null; @@ -36,24 +38,24 @@ export class GiteaHttp extends Http<GiteaHttpOptions, GiteaHttpOptions> { protected override async request<T>( path: string, options?: InternalHttpOptions & GiteaHttpOptions - ): Promise<HttpResponse<T> | null> { - const resolvedUrl = resolveUrl(path, options.baseUrl ?? baseUrl); + ): Promise<HttpResponse<T>> { + const resolvedUrl = resolveUrl(path, options?.baseUrl ?? baseUrl); const opts = { baseUrl, ...options, }; const res = await super.request<T>(resolvedUrl, opts); - const pc = getPaginationContainer(res.body); + const pc = getPaginationContainer<T>(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); + 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); - const nextPc = getPaginationContainer(nextRes.body); + const nextPc = getPaginationContainer<T>(nextRes.body); if (nextPc === null) { break; } diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts index abd17fc506..26d2714252 100644 --- a/lib/util/http/github.spec.ts +++ b/lib/util/http/github.spec.ts @@ -146,7 +146,7 @@ describe('util/http/github', () => { async function fail( code: number, body: any = undefined, - headers: httpMock.ReplyHeaders = undefined + headers: httpMock.ReplyHeaders = {} ) { const url = '/some-url'; httpMock @@ -441,9 +441,9 @@ describe('util/http/github', () => { .post('/graphql') .reply(200, { data: { repository } }); - const { data } = await githubApi.requestGraphql(graphqlQuery); + const res = await githubApi.requestGraphql(graphqlQuery); expect(httpMock.getTrace()).toHaveLength(1); - expect(data).toStrictEqual({ repository }); + expect(res?.data).toStrictEqual({ repository }); }); it('queryRepoField', async () => { httpMock diff --git a/lib/util/http/github.ts b/lib/util/http/github.ts index fa4bfbe04a..ec0e949a68 100644 --- a/lib/util/http/github.ts +++ b/lib/util/http/github.ts @@ -49,11 +49,12 @@ function handleGotError( err: GotLegacyError, url: string | URL, opts: GithubHttpOptions -): never { +): Error { const path = url.toString(); let message = err.message || ''; - if (is.plainObject(err.response?.body) && 'message' in err.response.body) { - message = String(err.response.body.message); + const body = err.response?.body; + if (is.plainObject(body) && 'message' in body) { + message = String(body.message); } if ( err.code === 'ENOTFOUND' || @@ -62,37 +63,37 @@ function handleGotError( err.code === 'ECONNRESET' ) { logger.debug({ err }, 'GitHub failure: RequestError'); - throw new ExternalHostError(err, PlatformId.Github); + return new ExternalHostError(err, PlatformId.Github); } if (err.name === 'ParseError') { logger.debug({ err }, ''); - throw new ExternalHostError(err, PlatformId.Github); + return new ExternalHostError(err, PlatformId.Github); } - if (err.statusCode >= 500 && err.statusCode < 600) { + if (err.statusCode && err.statusCode >= 500 && err.statusCode < 600) { logger.debug({ err }, 'GitHub failure: 5xx'); - throw new ExternalHostError(err, PlatformId.Github); + return new ExternalHostError(err, PlatformId.Github); } if ( err.statusCode === 403 && message.startsWith('You have triggered an abuse detection mechanism') ) { logger.debug({ err }, 'GitHub failure: abuse detection'); - throw new Error(PLATFORM_RATE_LIMIT_EXCEEDED); + return new Error(PLATFORM_RATE_LIMIT_EXCEEDED); } if ( err.statusCode === 403 && message.startsWith('You have exceeded a secondary rate limit') ) { logger.debug({ err }, 'GitHub failure: secondary rate limit'); - throw new Error(PLATFORM_RATE_LIMIT_EXCEEDED); + return new Error(PLATFORM_RATE_LIMIT_EXCEEDED); } if (err.statusCode === 403 && message.includes('Upgrade to GitHub Pro')) { logger.debug({ path }, 'Endpoint needs paid GitHub plan'); - throw err; + return err; } if (err.statusCode === 403 && message.includes('rate limit exceeded')) { logger.debug({ err }, 'GitHub failure: rate limit'); - throw new Error(PLATFORM_RATE_LIMIT_EXCEEDED); + return new Error(PLATFORM_RATE_LIMIT_EXCEEDED); } if ( err.statusCode === 403 && @@ -102,7 +103,7 @@ function handleGotError( { err }, 'GitHub failure: Resource not accessible by integration' ); - throw new Error(PLATFORM_INTEGRATION_UNAUTHORIZED); + return new Error(PLATFORM_INTEGRATION_UNAUTHORIZED); } if (err.statusCode === 401 && message.includes('Bad credentials')) { const rateLimit = err.headers?.['x-ratelimit-limit'] ?? -1; @@ -114,40 +115,40 @@ function handleGotError( 'GitHub failure: Bad credentials' ); if (rateLimit === '60') { - throw new ExternalHostError(err, PlatformId.Github); + return new ExternalHostError(err, PlatformId.Github); } - throw new Error(PLATFORM_BAD_CREDENTIALS); + return new Error(PLATFORM_BAD_CREDENTIALS); } if (err.statusCode === 422) { if ( message.includes('Review cannot be requested from pull request author') ) { - throw err; + return err; } else if (err.body?.errors?.find((e: any) => e.code === 'invalid')) { logger.debug({ err }, 'Received invalid response - aborting'); - throw new Error(REPOSITORY_CHANGED); + return new Error(REPOSITORY_CHANGED); } else if ( err.body?.errors?.find((e: any) => e.message?.startsWith('A pull request already exists') ) ) { - throw err; + return err; } logger.debug({ err }, '422 Error thrown from GitHub'); - throw new ExternalHostError(err, PlatformId.Github); + return new ExternalHostError(err, PlatformId.Github); } if ( err.statusCode === 410 && err.body?.message === 'Issues are disabled for this repo' ) { - throw err; + return err; } if (err.statusCode === 404) { logger.debug({ url: path }, 'GitHub 404'); } else { logger.debug({ err }, 'Unknown GitHub error'); } - throw err; + return err; } interface GraphqlOptions { @@ -155,10 +156,16 @@ interface GraphqlOptions { paginate?: boolean; count?: number; limit?: number; - cursor?: string; + cursor?: string | null; acceptHeader?: string; } +interface GraphqlPaginatedContent<T = unknown> { + nodes: T[]; + edges: T[]; + pageInfo: { hasNextPage: boolean; endCursor: string }; +} + function constructAcceptString(input?: any): string { const defaultAccept = 'application/vnd.github.v3+json'; const acceptStrings = @@ -184,9 +191,7 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { url: string | URL, options?: GithubInternalOptions & GithubHttpOptions, okToRetry = true - ): Promise<HttpResponse<T> | null> { - let result = null; - + ): Promise<HttpResponse<T>> { const opts = { baseUrl, ...options, @@ -201,68 +206,67 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { }; try { - result = await super.request<T>(url, opts); - - // istanbul ignore else: Can result be null ??? - if (result !== null) { - if (opts.paginate) { - // Check if result is paginated - const pageLimit = opts.pageLimit || 10; - const linkHeader = - result?.headers?.link && - parseLinkHeader(result.headers.link as string); - if (linkHeader?.next && linkHeader?.last) { - let lastPage = +linkHeader.last.page; - // istanbul ignore else: needs a test - if (!process.env.RENOVATE_PAGINATE_ALL && opts.paginate !== 'all') { - lastPage = Math.min(pageLimit, lastPage); + const result = await super.request<T>(url, opts); + if (opts.paginate) { + // Check if result is paginated + const pageLimit = opts.pageLimit ?? 10; + const linkHeader = + result?.headers?.link && + parseLinkHeader(result.headers.link as string); + if (linkHeader?.next && linkHeader?.last) { + let lastPage = +linkHeader.last.page; + // istanbul ignore else: needs a test + if (!process.env.RENOVATE_PAGINATE_ALL && opts.paginate !== 'all') { + lastPage = Math.min(pageLimit, lastPage); + } + const pageNumbers = Array.from( + new Array(lastPage), + (x, i) => i + 1 + ).slice(1); + const queue = pageNumbers.map( + (page) => (): Promise<HttpResponse<T>> => { + const nextUrl = new URL(linkHeader.next.url, baseUrl); + nextUrl.search = ''; + nextUrl.searchParams.set('page', page.toString()); + return this.request<T>( + nextUrl, + { ...opts, paginate: false }, + okToRetry + ); } - const pageNumbers = Array.from( - new Array(lastPage), - (x, i) => i + 1 - ).slice(1); - const queue = pageNumbers.map( - (page) => (): Promise<HttpResponse> => { - const nextUrl = new URL(linkHeader.next.url, baseUrl); - delete nextUrl.search; - nextUrl.searchParams.set('page', page.toString()); - return this.request( - nextUrl, - { ...opts, paginate: false }, - okToRetry - ); + ); + const pages = await pAll(queue, { concurrency: 5 }); + if (opts.paginationField && is.plainObject(result.body)) { + const paginatedResult = result.body[opts.paginationField]; + if (is.array<T>(paginatedResult)) { + for (const nextPage of pages) { + if (is.plainObject(nextPage.body)) { + const nextPageResults = nextPage.body[opts.paginationField]; + if (is.array<T>(nextPageResults)) { + paginatedResult.push(...nextPageResults); + } + } + } + } + } else if (is.array<T>(result.body)) { + for (const nextPage of pages) { + if (is.array<T>(nextPage.body)) { + result.body.push(...nextPage.body); } - ); - const pages = await pAll(queue, { concurrency: 5 }); - if (opts.paginationField) { - result.body[opts.paginationField] = result.body[ - opts.paginationField - ].concat( - ...pages - .filter(Boolean) - .map((page) => page.body[opts.paginationField]) - ); - } else { - result.body = result.body.concat( - ...pages.filter(Boolean).map((page) => page.body) - ); } } } } + return result; } catch (err) { - handleGotError(err, url, opts); + throw handleGotError(err, url, opts); } - - return result; } public async requestGraphql<T = unknown>( query: string, options: GraphqlOptions = {} - ): Promise<GithubGraphqlResponse<T>> { - let result = null; - + ): Promise<GithubGraphqlResponse<T> | null> { const path = 'graphql'; const { paginate, count = 100, cursor = null } = options; @@ -289,16 +293,15 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { 'graphql', opts ); - result = res?.body; + return res?.body; } catch (err) { logger.debug({ err, query, options }, 'Unexpected GraphQL Error'); if (err instanceof ExternalHostError && count && count > 10) { logger.info('Reducing pagination count to workaround graphql errors'); return null; } - handleGotError(err, path, opts); + throw handleGotError(err, path, opts); } - return result; } async queryRepoField<T = Record<string, unknown>>( @@ -311,10 +314,10 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { const { paginate = true } = options; let optimalCount: null | number = null; - const initialCount = options.count || 100; + const initialCount = options.count ?? 100; let count = initialCount; - let limit = options.limit || 1000; - let cursor: string = null; + let limit = options.limit ?? 1000; + let cursor: string | null = null; let isIterating = true; while (isIterating) { @@ -324,11 +327,19 @@ export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { cursor, paginate, }); - const fieldData = res?.data?.repository?.[fieldName]; - if (fieldData) { + const repositoryData = res?.data?.repository; + if ( + repositoryData && + is.plainObject(repositoryData) && + repositoryData[fieldName] + ) { optimalCount = count; - const { nodes = [], edges = [], pageInfo } = fieldData; + const { + nodes = [], + edges = [], + pageInfo, + } = repositoryData[fieldName] as GraphqlPaginatedContent<T>; result.push(...nodes); result.push(...edges); diff --git a/lib/util/http/gitlab.ts b/lib/util/http/gitlab.ts index 7c6589ce98..538837d2f1 100644 --- a/lib/util/http/gitlab.ts +++ b/lib/util/http/gitlab.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import parseLinkHeader from 'parse-link-header'; import { PlatformId } from '../../constants'; import { logger } from '../../logger'; @@ -27,9 +28,7 @@ export class GitlabHttp extends Http<GitlabHttpOptions, GitlabHttpOptions> { protected override async request<T>( url: string | URL, options?: GitlabInternalOptions & GitlabHttpOptions - ): Promise<HttpResponse<T> | null> { - let result = null; - + ): Promise<HttpResponse<T>> { const opts = { baseUrl, ...options, @@ -37,22 +36,25 @@ export class GitlabHttp extends Http<GitlabHttpOptions, GitlabHttpOptions> { }; try { - result = await super.request<T>(url, opts); - if (opts.paginate) { + const result = await super.request<T>(url, opts); + if (opts.paginate && is.array(result.body)) { // Check if result is paginated try { const linkHeader = parseLinkHeader(result.headers.link as string); - if (linkHeader?.next) { - const nextUrl = parseUrl(linkHeader.next.url); + const nextUrl = linkHeader?.next?.url + ? parseUrl(linkHeader.next.url) + : null; + if (nextUrl) { if (process.env.GITLAB_IGNORE_REPO_URL) { const defaultEndpoint = new URL(baseUrl); nextUrl.protocol = defaultEndpoint.protocol; nextUrl.host = defaultEndpoint.host; } - result.body = result.body.concat( - (await this.request<T>(nextUrl, opts)).body - ); + const nextResult = await this.request<T>(nextUrl, opts); + if (is.array(nextResult.body)) { + result.body.push(...nextResult.body); + } } } catch (err) /* istanbul ignore next */ { logger.warn({ err }, 'Pagination error'); diff --git a/lib/util/http/host-rules.ts b/lib/util/http/host-rules.ts index 4d86c913d6..3c786d1709 100644 --- a/lib/util/http/host-rules.ts +++ b/lib/util/http/host-rules.ts @@ -20,6 +20,7 @@ function findMatchingRules(options: GotOptions, url: string): HostRule { // Fallback to `github` hostType if ( + hostType && GITHUB_API_USING_HOST_TYPES.includes(hostType) && hostType !== PlatformId.Github ) { @@ -34,6 +35,7 @@ function findMatchingRules(options: GotOptions, url: string): HostRule { // Fallback to `gitlab` hostType if ( + hostType && GITLAB_API_USING_HOST_TYPES.includes(hostType) && hostType !== PlatformId.Gitlab ) { @@ -74,11 +76,17 @@ export function applyHostRules(url: string, inOptions: GotOptions): GotOptions { options.enabled = false; } // Apply optional params - ['abortOnError', 'abortIgnoreStatusCodes', 'timeout'].forEach((param) => { - if (foundRules[param]) { - options[param] = foundRules[param]; - } - }); + if (foundRules.abortOnError) { + options.abortOnError = foundRules.abortOnError; + } + + if (foundRules.abortIgnoreStatusCodes) { + options.abortIgnoreStatusCodes = foundRules.abortIgnoreStatusCodes; + } + + if (foundRules.timeout) { + options.timeout = foundRules.timeout; + } if (!hasProxy() && foundRules.enableHttp2 === true) { options.http2 = true; diff --git a/lib/util/http/index.spec.ts b/lib/util/http/index.spec.ts index c239376549..04b1f54f03 100644 --- a/lib/util/http/index.spec.ts +++ b/lib/util/http/index.spec.ts @@ -192,22 +192,44 @@ describe('util/http/index', () => { let bar = false; let baz = false; - const mockRequestResponse = () => { - let resolveRequest; + const dummyResolve = (_: unknown): void => { + return; + }; + + interface MockedRequestResponse<T = unknown> { + request: Promise<T>; + resolveRequest: (_?: T) => void; + response: Promise<T>; + resolveResponse: (_?: T) => void; + } + + const mockRequestResponse = (): MockedRequestResponse => { + let resolveRequest = dummyResolve; const request = new Promise((resolve) => { resolveRequest = resolve; }); - let resolveResponse; + let resolveResponse = dummyResolve; const response = new Promise((resolve) => { resolveResponse = resolve; }); - return [request, resolveRequest, response, resolveResponse]; + return { request, resolveRequest, response, resolveResponse }; }; - const [fooReq, fooStart, fooResp, fooFinish] = mockRequestResponse(); - const [barReq, barStart, barResp, barFinish] = mockRequestResponse(); + const { + request: fooReq, + resolveRequest: fooStart, + response: fooResp, + resolveResponse: fooFinish, + } = mockRequestResponse(); + + const { + request: barReq, + resolveRequest: barStart, + response: barResp, + resolveResponse: barFinish, + } = mockRequestResponse(); httpMock .scope(baseUrl) @@ -256,7 +278,7 @@ describe('util/http/index', () => { it('getBuffer', async () => { httpMock.scope(baseUrl).get('/').reply(200, Buffer.from('test')); const res = await http.getBuffer('http://renovate.com'); - expect(res.body).toBeInstanceOf(Buffer); - expect(res.body.toString('utf-8')).toBe('test'); + expect(res?.body).toBeInstanceOf(Buffer); + expect(res?.body.toString('utf-8')).toBe('test'); }); }); diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts index e1a7d00c3e..553e912672 100644 --- a/lib/util/http/index.ts +++ b/lib/util/http/index.ts @@ -74,7 +74,7 @@ function applyDefaultHeaders(options: Options): void { options.headers = { ...options.headers, 'user-agent': - process.env.RENOVATE_USER_AGENT || + process.env.RENOVATE_USER_AGENT ?? `RenovateBot/${renovateVersion} (https://github.com/renovatebot/renovate)`, }; } @@ -95,7 +95,7 @@ async function gotRoutine<T>( // Otherwise it doesn't typecheck. const resp = await got<T>(url, { ...options, hooks } as GotJSONOptions); const duration = - resp.timings.phases.total || /* istanbul ignore next: can't be tested */ 0; + resp.timings.phases.total ?? /* istanbul ignore next: can't be tested */ 0; const httpRequests = memCache.get('http-requests') || []; httpRequests.push({ ...requestStats, duration }); @@ -107,14 +107,14 @@ async function gotRoutine<T>( export class Http<GetOptions = HttpOptions, PostOptions = HttpPostOptions> { private options?: GotOptions; - constructor(private hostType: string, options?: HttpOptions) { + constructor(private hostType: string, options: HttpOptions = {}) { this.options = merge<GotOptions>(options, { context: { hostType } }); } protected async request<T>( requestUrl: string | URL, - httpOptions?: InternalHttpOptions - ): Promise<HttpResponse<T> | null> { + httpOptions: InternalHttpOptions = {} + ): Promise<HttpResponse<T>> { let url = requestUrl.toString(); if (httpOptions?.baseUrl) { url = resolveBaseUrl(httpOptions.baseUrl, url); @@ -160,7 +160,7 @@ export class Http<GetOptions = HttpOptions, PostOptions = HttpPostOptions> { // Cache GET requests unless useCache=false if ( - ['get', 'head'].includes(options.method) && + (options.method === 'get' || options.method === 'head') && options.useCache !== false ) { resPromise = memCache.get(cacheKey); diff --git a/tsconfig.strict.json b/tsconfig.strict.json index 2a9c713c0b..abbedf333e 100644 --- a/tsconfig.strict.json +++ b/tsconfig.strict.json @@ -50,9 +50,7 @@ "lib/util/git/types.ts", "lib/util/host-rules.ts", "lib/util/html.ts", - "lib/util/http/hooks.ts", - "lib/util/http/legacy.ts", - "lib/util/http/types.ts", + "lib/util/http/**/.ts", "lib/util/index.ts", "lib/util/json-writer/code-format.ts", "lib/util/json-writer/editor-config.ts", -- GitLab