@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
254 lines (229 loc) • 8.38 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 * as changeCase from "change-case";
import { hasCommit } from "../../../api-helper/pushtest/commit";
import { isMaterialChange } from "../../../api-helper/pushtest/materialChangeTest";
import { StatefulPushListenerInvocation } from "../../../api/dsl/goalContribution";
import { isGoal } from "../../../api/mapping/goalTest";
import {
pushTest,
PushTest,
} from "../../../api/mapping/PushTest";
import {
hasFile,
hasFileContaining,
hasResourceProvider,
isBranch,
isRepo,
ToDefaultBranch,
} from "../../../api/mapping/support/commonPushTests";
import {
and,
not,
or,
} from "../../../api/mapping/support/pushTestUtils";
import { SdmGoalState } from "../../../typings/types";
import { isSkillConfigured } from "../../mapping/pushtest/isSkillConfigured";
import { toArray } from "../../util/misc/array";
import { camelCase } from "./util";
export type PushTestMaker<G extends Record<string, any> = any> =
(params: G) => ((pli: StatefulPushListenerInvocation) => Promise<boolean>) | Promise<PushTest> | PushTest;
export async function mapTests(tests: any,
additionalTests: Record<string, PushTest>,
extensionTests: Record<string, PushTestMaker>): Promise<PushTest | PushTest[]> {
const newTests = [];
for (const t of toArray(tests || [])) {
const test = typeof t !== "string" && !Array.isArray(t) ? camelCase(t) : t as any;
newTests.push(await mapTest(test, additionalTests, extensionTests));
}
return newTests;
}
type CreatePushTest = (test: any,
additionalTests: Record<string, PushTest>,
extensionTests: Record<string, PushTestMaker>) => Promise<PushTest | undefined>;
const HasFile: CreatePushTest = async test => {
if (!!test.hasFile) {
return hasFile(test.hasFile);
}
return undefined;
};
const IsRepo: CreatePushTest = async test => {
if (!!test.isRepo) {
return isRepo(typeof test.isRepo === "string" ? new RegExp(test.isRepo) : test.isRepo);
}
return undefined;
};
const IsBranch: CreatePushTest = async test => {
if (!!test.isBranch) {
return isBranch(typeof test.isBranch === "string" ? new RegExp(test.isBranch) : test.isBranch);
}
return undefined;
};
const IsDefaultBranch: CreatePushTest = async test => {
if (["isDefaultBranch", "toDefaultBranch"].includes(changeCase.camel(test))) {
return ToDefaultBranch;
}
return undefined;
};
const IsSkillConfigured: CreatePushTest = async test => {
if (!!test.isSkillConfigured) {
const sc = test.isSkillConfigured;
return isSkillConfigured({
hasCommit: sc.hasCommit,
hasFile: sc.hasFile,
isBranch: sc.isBranch,
isDefaultBranch: sc.isDefaultBranch,
});
}
return undefined;
};
const IsGoal: CreatePushTest = async (test, additionalTests, extensionTests) => {
const onGoal = test.isGoal || test.onGoal;
if (!!onGoal) {
return isGoal({
name: getStringOrRegexp(onGoal.name),
state: onGoal.state || SdmGoalState.success,
pushTest: onGoal.test ? await mapTest(onGoal.test, additionalTests, extensionTests) : undefined,
output: getStringOrRegexp(onGoal.output),
data: getStringOrRegexp(onGoal.data),
});
}
return undefined;
};
const IsMaterialChange: CreatePushTest = async test => {
if (!!test.isMaterialChange) {
return isMaterialChange({
directories: toArray(test.isMaterialChange.directories),
extensions: toArray(test.isMaterialChange.extensions),
files: toArray(test.isMaterialChange.files),
globs: getGlobPatterns(test.isMaterialChange),
});
}
return undefined;
};
const HasFileContaining: CreatePushTest = async test => {
if (!!test.hasFileContaining) {
if (!test.hasFileContaining.content) {
throw new Error("Push test 'hasFileContaining' can't be used without 'content' property");
}
return hasFileContaining(
getGlobPatterns(test.hasFileContaining) || "**/*",
typeof test.hasFileContaining.content === "string" ? new RegExp(test.hasFileContaining.content) : test.hasFileContaining.content);
}
return undefined;
};
const HasResourceProvider: CreatePushTest = async test => {
if (!!test.hasResourceProvider) {
if (!test.hasResourceProvider.type) {
throw new Error("Push test 'hasResourceProvider' can't be used without 'type' property");
}
return hasResourceProvider(test.hasResourceProvider.type, test.hasResourceProvider.name);
}
return undefined;
};
const HasCommit: CreatePushTest = async test => {
if (!!test.hasCommit) {
return hasCommit(typeof test.hasCommit === "string" ? new RegExp(test.hasCommit) : test.hasCommit);
}
return undefined;
};
const Not: CreatePushTest = async (test, additionalTests, extensionTests) => {
if (!!test.not) {
return not(await mapTest(test.not, additionalTests, extensionTests));
}
return undefined;
};
const And: CreatePushTest = async (test, additionalTests, extensionTests) => {
if (!!test.and) {
return and(...toArray(await mapTests(test.and, additionalTests, extensionTests)));
}
return undefined;
};
const Or: CreatePushTest = async (test, additionalTests, extensionTests) => {
if (!!test.or) {
return or(...toArray(await mapTests(test.or, additionalTests, extensionTests)));
}
return undefined;
};
const AdditionalTest: CreatePushTest = async (test, additionalTests) => {
if (!!test.use && !!additionalTests[test.use]) {
return additionalTests[test.use];
}
return undefined;
};
const FunctionTest: CreatePushTest = async test => {
if (typeof test === "function") {
return pushTest(test.toString(), test);
}
return undefined;
};
const ExtensionTest = async (test, additionalTests, extensionTests) => {
for (const extTestName in extensionTests) {
if (test.use === extTestName) {
const extTest = await extensionTests[extTestName](test.parameters || {});
if (!!extTest.name && !!extTest.mapping) {
return extTest;
} else {
return pushTest(extTestName, extTest);
}
}
}
return undefined;
};
export const CreatePushTests = [
HasFile,
IsRepo,
IsBranch,
IsDefaultBranch,
IsGoal,
IsSkillConfigured,
IsMaterialChange,
HasFileContaining,
HasResourceProvider,
HasCommit,
Not,
And,
Or,
AdditionalTest,
FunctionTest,
ExtensionTest,
];
export async function mapTest(test: any,
additionalTests: Record<string, PushTest>,
extensionTests: Record<string, PushTestMaker>): Promise<PushTest> {
for (const createPushTest of CreatePushTests) {
const pt = await createPushTest(test, additionalTests, extensionTests);
if (!!pt) {
return pt;
}
}
throw new Error(`Unable to construct push test from '${JSON.stringify(test)}'`);
}
function getGlobPatterns(test: any): string[] {
const pattern = test.globPattern || test.pattern || test.globPatterns || test.patterns;
return toArray(pattern);
}
function getStringOrRegexp(toTest: string | { regexp: string }): string | RegExp | undefined {
if (!toTest) {
return undefined;
} else if (typeof toTest === "string") {
return toTest;
} else if (toTest.regexp !== undefined) {
return new RegExp(toTest.regexp);
} else {
return undefined;
}
}