UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

174 lines (147 loc) 5.25 kB
/* * Copyright © 2019 Atomist, Inc. * * 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 * as _ from "lodash"; import { Locking } from "./common/Locking"; import { Goal, GoalDefinition, GoalWithPrecondition, } from "./Goal"; /** * Represents goals set in response to a push */ export class Goals { public readonly goals: Goal[]; /** * Return a Goal set that contains these goals and one more goal, * with an appropriate name * @param {Goal} g goal to add * @return {Goals} */ public and(g: Goal): Goals { return new Goals(this.name + ", " + g.name, ...this.goals.concat(g)); } /** * Return a form of these goals that is locked, so that more goals cannot be * added through contribution model * @return {Goals} */ public andLock(): Goals { return this.and(Locking); } // tslint:disable-next-line:no-shadowed-variable constructor(public name: string, ...goals: Goal[]) { this.goals = goals; } } export function isGoals(a: any): a is Goals { return !!a && !!(a as Goals).goals; } /** * Builder to build Goals instances. */ export interface GoalsBuilder { /** * Plan the given goal or goals to this Goals instance. * @param {GoalDefinition | Goal | Goals} goals * @returns {Goals & GoalsAndPreConditionBuilder} */ plan(...goals: Array<GoalDefinition | Goal | Goals>): Goals & GoalsAndPreConditionBuilder; } /** * Extension to GoalsBuilder allowing to add preConditions. */ export interface GoalsAndPreConditionBuilder extends GoalsBuilder { /** * Add preCondition(s) to previously planned goal or goals. * Note: * This only will affect the goal or goals that were planned immediately * before calling `after`. * Only call this once; an additional call will remove other preconditions * that may have existed on the original goal. * @param {Goal} goals * @returns {Goals & GoalsBuilder} */ after(...goals: Array<Goals | GoalDefinition | Goal>): Goals & GoalsBuilder; } /** * Create Goals instance using a fluent API. * * const simpleGoals = goals("Simple Goals") * .plan(CodeInspectionGoal) * .plan(BuildGoal, Autofix).after(CodeInspectionGoal) * .plan(StagingEndpointGoal).after(BuildGoal) * .plan(ProductionDeploymentGoal).after(BuildGoal, StagingEndpointGoal); * * @param {string} name * @returns {Goals & GoalsBuilder} */ export function goals(name: string): Goals & GoalsBuilder { return new DefaultGoalsBuilder(name); } class DefaultGoalsBuilder extends Goals implements GoalsBuilder, GoalsAndPreConditionBuilder { private lastGoals: Goal[] = []; constructor(public name: string) { super(name); } public plan(...newGoals: Array<GoalDefinition | Goal | Goals>): Goals & GoalsAndPreConditionBuilder { const currentGoals = []; // Keep track of what goals where last added so that we can add the preConditions later convertToGoals(...newGoals).forEach(g => { if (isGoals(g)) { currentGoals.push(...g.goals); } else { currentGoals.push(g); } }); this.goals.push(...currentGoals); this.lastGoals = currentGoals; return this; } public after(...newGoals: Array<Goals | GoalDefinition | Goal>): Goals & GoalsBuilder { const lastGoalsWithPreConditions = []; this.lastGoals.forEach(g => { // Add the preCondition into the last added goals const newGoal = new GoalWithPrecondition(g.definition, ...convertToGoals(...newGoals)); // Preserve the plan function if it exists as it might influence goal planning (newGoal as any).plan = (g as any).plan; lastGoalsWithPreConditions.push(newGoal); // Remove the previously added goals const ix = this.goals.indexOf(g); if (ix >= 0) { this.goals.splice(ix, 1); } else { throw new Error("Unable to remove previously planned goal"); } }); // Add the newly created goals with preConditions this.goals.push(...lastGoalsWithPreConditions); this.lastGoals = lastGoalsWithPreConditions; return this; } } function convertToGoals(...pgoals: Array<GoalDefinition | Goal | Goals>): Goal[] { return _.flatten<Goal>(pgoals.map(g => { if (g instanceof Goal) { return g; } else if (isGoals(g)) { return g.goals; } else if (!!g) { return new Goal(g); } return undefined; })).filter(g => !!g); }