@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
155 lines (134 loc) • 6.39 kB
text/typescript
/*
* Copyright © 2020 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 { configurationValue } from "@atomist/automation-client/lib/configuration";
import { Goal } from "../../api/goal/Goal";
import { SdmGoalEvent } from "../../api/goal/SdmGoalEvent";
import {
GoalFulfillment,
GoalFulfillmentCallback,
GoalImplementation,
GoalImplementationMapper,
GoalSideEffect,
} from "../../api/goal/support/GoalImplementationMapper";
import { PushListenerInvocation } from "../../api/listener/PushListener";
import { AnyPush } from "../../api/mapping/support/commonPushTests";
/**
* Concrete implementation of GoalImplementationMapper
*/
export class DefaultGoalImplementationMapper implements GoalImplementationMapper {
private readonly implementations: GoalImplementation[] = [];
private readonly sideEffects: GoalSideEffect[] = [];
private readonly callbacks: GoalFulfillmentCallback[] = [];
private readonly goals: Goal[] = [];
public hasImplementation(): boolean {
return this.goals.filter(g => g.uniqueName !== "nevermind").length > 0;
}
public findImplementationBySdmGoal(goal: SdmGoalEvent): GoalImplementation {
let matchedNames = this.implementations.filter(m =>
m.implementationName === goal.fulfillment.name &&
m.goal.uniqueName === uniqueName(goal));
if (matchedNames.length > 1) {
throw new Error(`Multiple implementations found for name '${goal.fulfillment.name}' on goal '${goal.uniqueName}'`);
}
if (matchedNames.length === 0) {
matchedNames = this.implementations.filter(m => m.goal.name === goal.fulfillment.name);
if (matchedNames.length > 1) {
throw new Error(`Multiple implementations found for name '${goal.fulfillment.name}' on goal '${goal.name}'`);
}
if (matchedNames.length === 0) {
throw new Error(`No implementation found with name '${goal.fulfillment.name}': ` +
`Found ${this.implementations.map(impl => impl.implementationName)}`);
}
}
return matchedNames[0];
}
public addImplementation(implementation: GoalImplementation): this {
if (this.implementations.some(i =>
i.implementationName === implementation.implementationName &&
i.goal.uniqueName === implementation.goal.uniqueName &&
i.goal.environment === implementation.goal.environment)) {
throw new Error(`Implementation with name '${implementation.implementationName
}' already registered for goal '${implementation.goal.name}'`);
}
this.addGoal(implementation.goal);
this.implementations.push(implementation);
return this;
}
public addSideEffect(sideEffect: GoalSideEffect): this {
sideEffect.pushTest = sideEffect.pushTest || AnyPush;
sideEffect.registration = sideEffect.registration || configurationValue("name");
this.addGoal(sideEffect.goal);
this.sideEffects.push(sideEffect);
return this;
}
public addFulfillmentCallback(callback: GoalFulfillmentCallback): this {
this.callbacks.push(callback);
return this;
}
public async findFulfillmentByPush(goal: Goal, inv: PushListenerInvocation): Promise<GoalFulfillment | undefined> {
const implementationsForGoal = this.implementations.filter(
m => m.goal.uniqueName === uniqueName(goal) &&
m.goal.environment === goal.environment);
const matchingFulfillments: GoalImplementation[] = [];
for (const implementation of implementationsForGoal) {
if (await implementation.pushTest.mapping(inv)) {
matchingFulfillments.push(implementation);
}
}
if (matchingFulfillments.length > 1) {
throw new Error(`Multiple matching implementations for goal '${goal.uniqueName}' found: '${
matchingFulfillments.map(f => f.implementationName).join(", ")}'`);
} else if (matchingFulfillments.length === 1) {
return matchingFulfillments[0];
}
const knownSideEffects = this.sideEffects.filter(
m => m.goal.uniqueName === uniqueName(goal) &&
m.goal.environment === goal.environment);
for (const sideEffect of knownSideEffects) {
if (await sideEffect.pushTest.mapping(inv)) {
return sideEffect;
}
}
return undefined;
}
public findFulfillmentCallbackForGoal(sdmGoal: SdmGoalEvent): GoalFulfillmentCallback[] {
return this.callbacks.filter(c =>
c.goal.uniqueName === uniqueName(sdmGoal) &&
// This slice is required because environment is suffixed with /
(c.goal.definition.environment.slice(0, -1) === sdmGoal.environment
|| c.goal.definition.environment === sdmGoal.environment));
}
public findGoalBySdmGoal(sdmGoal: SdmGoalEvent): Goal | undefined {
return this.goals.find(g =>
g.uniqueName === uniqueName(sdmGoal) &&
// This slice is required because environment is suffixed with /
(g.definition.environment.slice(0, -1) === g.environment
|| g.definition.environment === g.environment),
);
}
private addGoal(goal: Goal): void {
const existingGoal = this.goals.find(g => g.uniqueName === goal.uniqueName);
if (!existingGoal) {
this.goals.push(goal);
} else if (existingGoal !== goal) {
throw new Error(`Goal with uniqueName '${goal.uniqueName}' already registered`);
}
}
}
function uniqueName(goal: Pick<SdmGoalEvent, "uniqueName">): string {
const un = goal.uniqueName.split("#sdm:");
return un[0];
}