@atomist/automation-client
Version:
Atomist API for software low-level client
99 lines (90 loc) • 4.46 kB
text/typescript
import { ActionResult } from "../../action/ActionResult";
import { HandleCommand } from "../../HandleCommand";
import { HandlerContext } from "../../HandlerContext";
import {
commandHandlerFrom,
OnCommand,
} from "../../onCommand";
import { Maker } from "../../util/constructionUtils";
import { CommandDetails } from "../CommandDetails";
import { EditorOrReviewerParameters } from "../common/params/BaseEditorOrReviewerParameters";
import {
andFilter,
RepoFilter,
} from "../common/repoFilter";
import { RepoFinder } from "../common/repoFinder";
import { RepoRef } from "../common/RepoId";
import { issueRaisingReviewRouter } from "./issueRaisingReviewRouter";
import { ProjectReviewer } from "./projectReviewer";
import { reviewAll } from "./reviewAll";
import { ProjectReview } from "./ReviewResult";
/**
* Function signature to route ProjectReviews, for example to GitHub issues or MessageClient
*/
export type ReviewRouter<PARAMS> = (pr: ProjectReview, params: PARAMS, title: string, ctx: HandlerContext) =>
Promise<ActionResult<RepoRef>>;
/**
* Further details of an editor to allow selective customization
*/
export interface ReviewerCommandDetails<PARAMS extends EditorOrReviewerParameters> extends CommandDetails<PARAMS> {
repoFilter?: RepoFilter;
reviewRouter: ReviewRouter<PARAMS>;
}
function defaultDetails(name: string): ReviewerCommandDetails<EditorOrReviewerParameters> {
return {
description: name,
reviewRouter: issueRaisingReviewRouter,
};
}
/**
* Create a handle function that reviews one or many repos, following AllReposByDefaultParameters
* @param reviewerFactory function returning a reviewer instance for the appropriate parameters
* @param factory construction function
* @param {string} name
* @param {string} details object allowing customization beyond reasonable defaults
* @return {HandleCommand}
*/
export function reviewerHandler<PARAMS extends EditorOrReviewerParameters>(reviewerFactory: (params: PARAMS) => ProjectReviewer<PARAMS>,
factory: Maker<PARAMS>,
name: string,
details: Partial<ReviewerCommandDetails<PARAMS>> = {}): HandleCommand {
const detailsToUse: ReviewerCommandDetails<EditorOrReviewerParameters> = {
...defaultDetails(name),
...details,
} as ReviewerCommandDetails<EditorOrReviewerParameters>;
return commandHandlerFrom(handleReviewOneOrMany(reviewerFactory, name, detailsToUse),
factory,
name,
detailsToUse.description, detailsToUse.intent, detailsToUse.tags);
}
/**
* If owner and repo are required, review just one repo. Otherwise review all repos
* in the present team
*/
function handleReviewOneOrMany<PARAMS extends EditorOrReviewerParameters>(reviewerFactory: (params: PARAMS) => ProjectReviewer<PARAMS>,
name: string,
details: ReviewerCommandDetails<PARAMS>): OnCommand<PARAMS> {
return (ctx: HandlerContext, parameters: PARAMS) => {
const repoFinder: RepoFinder = parameters.targets.repoRef ?
() => Promise.resolve([parameters.targets.repoRef]) :
details.repoFinder;
return reviewAll(ctx, parameters.targets.credentials, reviewerFactory(parameters), parameters,
repoFinder,
andFilter(parameters.targets.test, details.repoFilter),
!!details.repoLoader ? details.repoLoader(parameters) : undefined)
.then(projectReviews => {
return Promise.all(projectReviews
.filter(pr => pr.comments.length > 0)
.map(pr => {
return details.reviewRouter(pr, parameters, name, ctx);
}))
.then(persisted =>
ctx.messageClient.respond(
`${name} reviewed ${projectReviews.length} repositories: Reported on ${persisted.length} with problems`)
.then(() => ({
projectsReviewed: projectReviews.length,
projectReviews,
})));
});
};
}