diff --git a/src/compute-pr-actions.ts b/src/compute-pr-actions.ts index 558e3a655..17ab73c5d 100644 --- a/src/compute-pr-actions.ts +++ b/src/compute-pr-actions.ts @@ -2,7 +2,7 @@ import * as Comments from "./comments"; import { PrInfo, BotError, BotEnsureRemovedFromProject, BotNoPackages, FileInfo } from "./pr-info"; import { CIResult } from "./util/CIResult"; import { ReviewInfo } from "./pr-info"; -import { noNulls, flatten, unique, sameUser, daysSince, sha256 } from "./util/util"; +import { noNullish, flatten, unique, sameUser, daysSince, sha256 } from "./util/util"; type ColumnName = | "Needs Maintainer Action" @@ -150,10 +150,10 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo { const noOtherOwners = allOwners.every(isAuthor); const tooManyOwners = allOwners.length > 50; const editsOwners = info.pkgInfo.some(p => p.kind === "edit" && p.addedOwners.length + p.deletedOwners.length > 0); - const packages = noNulls(info.pkgInfo.map(p => p.name)); + const packages = noNullish(info.pkgInfo.map(p => p.name)); const hasMultiplePackages = packages.length > 1; const hasTests = info.pkgInfo.some(p => p.files.some(f => f.kind === "test")); - const newPackages = noNulls(info.pkgInfo.map(p => p.kind === "add" ? p.name : null)); + const newPackages = noNullish(info.pkgInfo.map(p => p.kind === "add" ? p.name : null)); const hasNewPackages = newPackages.length > 0; const requireMaintainer = editsInfra || editsConfig || hasMultiplePackages || !hasTests || hasNewPackages || tooManyOwners; const blessable = !(hasNewPackages || editsInfra || noOtherOwners); @@ -202,7 +202,7 @@ function extendPrInfo(info: PrInfo): ExtendedPrInfo { } function getPendingCriticalPackages() { - return noNulls(info.pkgInfo.map(p => + return noNullish(info.pkgInfo.map(p => p.popularityLevel === "Critical" && !p.owners.some(o => approvedReviews.some(r => sameUser(o, r.reviewer))) ? p.name : null)); } diff --git a/src/execute-pr-actions.ts b/src/execute-pr-actions.ts index 2c3fc8460..98ff091c6 100644 --- a/src/execute-pr-actions.ts +++ b/src/execute-pr-actions.ts @@ -2,7 +2,7 @@ import { PR as PRQueryResult, PR_repository_pullRequest } from "./queries/schema import { Actions, LabelNames, LabelName } from "./compute-pr-actions"; import { createMutation, mutate } from "./graphql-client"; import { getProjectBoardColumns, getLabels } from "./util/cachedQueries"; -import { noNulls, flatten } from "./util/util"; +import { noNullish, flatten } from "./util/util"; import * as comment from "./util/comment"; // https://github.com/DefinitelyTyped/DefinitelyTyped/projects/5 @@ -22,7 +22,7 @@ export const deleteProjectCard = `mutation($input: DeleteProjectCardInput!) { de export async function executePrActions(actions: Actions, info: PRQueryResult, dry?: boolean) { const pr = info.repository?.pullRequest!; const botComments: ParsedComment[] = getBotComments(pr); - const mutations = noNulls([ + const mutations = noNullish([ ...await getMutationsForLabels(actions, pr), ...await getMutationsForProjectChanges(actions, pr), ...getMutationsForComments(actions, pr.id, botComments), @@ -38,7 +38,7 @@ export async function executePrActions(actions: Actions, info: PRQueryResult, dr async function getMutationsForLabels(actions: Actions, pr: PR_repository_pullRequest) { if (!actions.shouldUpdateLabels) return [] - const labels = noNulls(pr.labels?.nodes!).map(l => l.name); + const labels = noNullish(pr.labels?.nodes).map(l => l.name); const makeMutations = async (pred: (l: LabelName) => boolean, query: string) => { const labels = LabelNames.filter(pred); return labels.length === 0 ? null @@ -72,7 +72,7 @@ async function getMutationsForProjectChanges(actions: Actions, pr: PR_repository type ParsedComment = { id: string, body: string, tag: string, status: string }; function getBotComments(pr: PR_repository_pullRequest): ParsedComment[] { - return noNulls((pr.comments.nodes ?? []) + return noNullish((pr.comments.nodes ?? []) .filter(comment => comment?.author?.login === "typescript-bot") .map(c => { const { id, body } = c!, parsed = comment.parse(body); diff --git a/src/pr-info.ts b/src/pr-info.ts index 71d2e28d5..f045b34bc 100644 --- a/src/pr-info.ts +++ b/src/pr-info.ts @@ -16,7 +16,7 @@ import { getMonthlyDownloadCount } from "./util/npm"; import { client } from "./graphql-client"; import { ApolloQueryResult } from "apollo-boost"; import { fetchFile as defaultFetchFile } from "./util/fetchFile"; -import { noNulls, notUndefined, findLast, forEachReverse, sameUser, authorNotBot, latestDate } from "./util/util"; +import { noNullish, findLast, forEachReverse, sameUser, authorNotBot, latestDate } from "./util/util"; import * as comment from "./util/comment"; import * as HeaderParser from "definitelytyped-header-parser"; import * as jsonDiff from "fast-json-patch"; @@ -219,7 +219,7 @@ export async function deriveStateForPR( const reopenedDate = getReopenedDate(prInfo.timelineItems); const pkgInfoEtc = await getPackageInfosEtc( - noNulls(prInfo.files?.nodes).map(f => f.path).sort(), + noNullish(prInfo.files?.nodes).map(f => f.path).sort(), headCommit.oid, fetchFile, async name => await getDownloads(name, lastPushDate)); if (pkgInfoEtc instanceof Error) return botError(prInfo.number, pkgInfoEtc.message); const { pkgInfo, popularityLevel } = pkgInfoEtc; @@ -227,7 +227,7 @@ export async function deriveStateForPR( const reviews = getReviews(prInfo); const latestReview = latestDate(...reviews.map(r => r.date)); - const comments = noNulls(prInfo.comments.nodes || []); + const comments = noNullish(prInfo.comments.nodes); const mergeOfferDate = getMergeOfferDate(comments, headCommit.abbreviatedOid); const mergeRequest = getMergeRequest(comments, pkgInfo.length === 1 ? [author, ...pkgInfo[0].owners] : [author], @@ -465,7 +465,7 @@ function getReviews(prInfo: PR_repository_pullRequest) { const headCommitOid: string = prInfo.headRefOid; const reviews: ReviewInfo[] = []; // Do this in reverse order so we can detect up-to-date-reviews correctly - for (const r of noNulls(prInfo.reviews.nodes).reverse()) { + for (const r of noNullish(prInfo.reviews.nodes).reverse()) { const [reviewer, date] = [r?.author?.login, new Date(r.submittedAt)]; // Skip nulls if (!(r?.commit && reviewer)) continue; @@ -522,5 +522,5 @@ async function getOwnersOfPackage(packageName: string, version: string, fetchFil } catch (e) { if (e instanceof Error) return new Error(`error parsing owners: ${e.message}`); } - return parsed!.contributors.map(c => c.githubUsername).filter(notUndefined); + return noNullish(parsed!.contributors.map(c => c.githubUsername)); } diff --git a/src/util/cachedQueries.ts b/src/util/cachedQueries.ts index f76f9693c..dbd4a8bae 100644 --- a/src/util/cachedQueries.ts +++ b/src/util/cachedQueries.ts @@ -3,31 +3,26 @@ import { GetProjectColumns as GetProjectColumnsResult } from "../queries/schema/ import { GetLabels as GetLabelsResult } from "../queries/schema/GetLabels"; import { createCache } from "../ttl-cache"; import { client } from "../graphql-client"; +import { noNullish } from "./util"; const cache = createCache(); export async function getProjectBoardColumns() { return cache.getAsync("project board colum names", Infinity, async () => { - const res = (await query(GetProjectColumns)) - .repository?.project?.columns.nodes?.filter(defined) - ?? []; + const res = noNullish((await query(GetProjectColumns)) + .repository?.project?.columns.nodes); return res.sort((a,b) => a.name.localeCompare(b.name)); }); } export async function getLabels() { return await cache.getAsync("label ids", Infinity, async () => { - const res = (await query(GetLabels)) - .repository?.labels?.nodes?.filter(defined) - ?? []; + const res = noNullish((await query(GetLabels)) + .repository?.labels?.nodes); return res.sort((a,b) => a.name.localeCompare(b.name)); }); } -function defined(arg: T | null | undefined): arg is T { - return arg != null; -} - async function query(gql: any): Promise { const res = await client.query({ query: gql, diff --git a/src/util/util.ts b/src/util/util.ts index 506f96ad3..1a9ade973 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -1,13 +1,11 @@ import * as crypto from "crypto"; import * as moment from "moment"; -export function noNulls(arr: ReadonlyArray | null | undefined): T[] { +export function noNullish(arr: ReadonlyArray | null | undefined): T[] { if (arr == null) return []; return arr.filter(arr => arr != null) as T[]; } -export function notUndefined(arg: T | undefined): arg is T { return arg !== undefined; } - export function flatten(xs: T[][]) { return ([] as T[]).concat(...xs); }