UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

127 lines (117 loc) 4.4 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 { logger } from "@atomist/automation-client/lib/util/logger"; import { PredicateMapping, PredicateMappingCompositionStyle, } from "../PredicateMapping"; import { DefaultPredicateMappingCostAnalyzer } from "./defaultPredicateMappingCostAnalyzer"; import { ExpectedPredicateMappingCost, PredicateMappingCostAnalyzer, } from "./PredicateMappingCostAnalyzer"; /** * Return the opposite of this predicate mapping */ export function whenNot<F>(t: PredicateMapping<F>): PredicateMapping<F> { return { name: `not (${t.name})`, mapping: async pi => !(await t.mapping(pi)), structure: { components: [t], compositionStyle: PredicateMappingCompositionStyle.Not, }, }; } /** * Wrap all these predicates in a single predicate * AND: Return true if all are satisfied * @param {PredicateMapping} predicates * @param analyzer analyzer to use for performance optimization * @return {PredicateMapping} */ export function all<F>(predicates: Array<PredicateMapping<F>>, analyzer: PredicateMappingCostAnalyzer<F> = DefaultPredicateMappingCostAnalyzer): PredicateMapping<F> { return { name: predicates.map(g => g.name).join(" && "), mapping: async pci => optimizedAndEvaluation(predicates, analyzer)(pci), structure: { components: predicates, compositionStyle: PredicateMappingCompositionStyle.And, }, }; } /** * Wrap all these predicates in a single predicate * OR: Return true if any is satisfied * @param {PredicateMapping} predicates * @param analyzer analyzer to use for performance optimization * @return {PredicateMapping} */ export function any<F>(predicates: Array<PredicateMapping<F>>, analyzer: PredicateMappingCostAnalyzer<F> = DefaultPredicateMappingCostAnalyzer): PredicateMapping<F> { return { name: predicates.map(g => g.name).join(" || "), mapping: async pci => { // Cannot short-circuit this const allResults: boolean[] = await gatherResults(predicates)(pci); return allResults.includes(true); }, structure: { components: predicates, compositionStyle: PredicateMappingCompositionStyle.Or, }, }; } /** * Evaluate predicates for an AND, running non-expensive ones first * @param {Array<PredicateMapping<F>>} predicates * @param {PredicateMappingCostAnalyzer<F>} analyzer * @return {(f: F) => Promise<boolean>} */ function optimizedAndEvaluation<F>(predicates: Array<PredicateMapping<F>>, analyzer: PredicateMappingCostAnalyzer<F>): (f: F) => Promise<boolean> { const cheap: Array<PredicateMapping<F>> = []; const remaining: Array<PredicateMapping<F>> = []; for (const p of predicates) { const cost = analyzer(p); if (cost !== ExpectedPredicateMappingCost.expensive) { cheap.push(p); } else { remaining.push(p); } } logger.debug("Cheap: [%j], remaining: [%j]", cheap, remaining); return async pci => { const cheapResults = await gatherResults(cheap)(pci); if (cheapResults.includes(false)) { return false; } const remainingResults = await gatherResults(remaining)(pci); return !remainingResults.includes(false); }; } function gatherResults<F>(predicates: Array<PredicateMapping<F>>): (f: F) => Promise<boolean[]> { return pci => { return Promise.all( predicates.map(async pt => { const result = await pt.mapping(pci); logger.debug(`Result of PushTest '${pt.name}' was ${result}`); return result; }), ); }; }