UNPKG

7.13 kBPlain TextView Raw
1/*
2 * Copyright © 2019 Atomist, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {
18 and,
19 hasFile,
20 hasFileContaining,
21 hasResourceProvider,
22 isBranch,
23 isGoal,
24 isMaterialChange,
25 isRepo,
26 not,
27 or,
28 pushTest,
29 PushTest,
30 SdmGoalState,
31 StatefulPushListenerInvocation,
32 ToDefaultBranch,
33} from "@atomist/sdm";
34import * as camelcaseKeys from "camelcase-keys";
35import * as changeCase from "change-case";
36import { toArray } from "../../util/misc/array";
37
38export type PushTestMaker<G extends Record<string, any> = any> =
39 (params: G) => ((pli: StatefulPushListenerInvocation) => Promise<boolean>) | Promise<PushTest> | PushTest;
40
41export async function mapTests(tests: any,
42 additionalTests: Record<string, PushTest>,
43 extensionTests: Record<string, PushTestMaker>): Promise<PushTest | PushTest[]> {
44 const newTests = [];
45 for (const t of toArray(tests || [])) {
46 const test = typeof t !== "string" && !Array.isArray(t) ? camelcaseKeys(t, { deep: true }) : t as any;
47 newTests.push(await mapTest(test, additionalTests, extensionTests));
48 }
49 return newTests;
50}
51
52type CreatePushTest = (test: any,
53 additionalTests: Record<string, PushTest>,
54 extensionTests: Record<string, PushTestMaker>) => Promise<PushTest | undefined>;
55
56const HasFile: CreatePushTest = async test => {
57 if (test.hasFile) {
58 return hasFile(test.hasFile);
59 }
60 return undefined;
61};
62
63const IsRepo: CreatePushTest = async test => {
64 if (test.isRepo) {
65 return isRepo(typeof test.isRepo === "string" ? new RegExp(test.isRepo) : test.isRepo);
66 }
67 return undefined;
68};
69
70const IsBranch: CreatePushTest = async test => {
71 if (test.isBranch) {
72 return isBranch(typeof test.isBranch === "string" ? new RegExp(test.isBranch) : test.isBranch);
73 }
74 return undefined;
75};
76
77const IsDefaultBranch: CreatePushTest = async test => {
78 if (["isDefaultBranch", "toDefaultBranch"].includes(changeCase.camel(test))) {
79 return ToDefaultBranch;
80 }
81 return undefined;
82};
83
84const IsGoal: CreatePushTest = async (test, additionalTests, extensionTests) => {
85 if (test.isGoal) {
86 return isGoal(
87 {
88 name: typeof test.isGoal.name === "string" ? new RegExp(test.isGoal.name) : test.isGoal.name,
89 state: test.isGoal.state || SdmGoalState.success,
90 pushTest: test.isGoal.test ? await mapTest(test.isGoal.test, additionalTests, extensionTests) : undefined,
91 output: typeof test.isGoal.output === "string" ? new RegExp(test.isGoal.output) : test.isGoal.output,
92 data: typeof test.isGoal.data === "string" ? new RegExp(test.isGoal.data) : test.isGoal.data,
93 });
94 }
95 return undefined;
96};
97
98const IsMaterialChange: CreatePushTest = async test => {
99 if (test.isMaterialChange) {
100 return isMaterialChange({
101 directories: toArray(test.isMaterialChange.directories),
102 extensions: toArray(test.isMaterialChange.extensions),
103 files: toArray(test.isMaterialChange.files),
104 globs: getGlobPatterns(test.isMaterialChange),
105 });
106 }
107 return undefined;
108};
109
110const HasFileContaining: CreatePushTest = async test => {
111 if (test.hasFileContaining) {
112 if (!test.hasFileContaining.content) {
113 throw new Error("Push test 'hasFileContaining' can't be used without 'content' property");
114 }
115 return hasFileContaining(
116 getGlobPatterns(test.hasFileContaining) || "**/*",
117 typeof test.hasFileContaining.content === "string" ? new RegExp(test.hasFileContaining.content) : test.hasFileContaining.content);
118 }
119 return undefined;
120};
121
122const HasResourceProvider: CreatePushTest = async test => {
123 if (test.hasResourceProvider) {
124 if (!test.hasResourceProvider.type) {
125 throw new Error("Push test 'hasResourceProvider' can't be used without 'type' property");
126 }
127 return hasResourceProvider(test.hasResourceProvider.type, test.hasResourceProvider.name);
128 }
129 return undefined;
130};
131
132const Not: CreatePushTest = async (test, additionalTests, extensionTests) => {
133 if (test.not) {
134 return not(await mapTest(test.not, additionalTests, extensionTests));
135 }
136 return undefined;
137};
138
139const And: CreatePushTest = async (test, additionalTests, extensionTests) => {
140 if (test.and) {
141 return and(...toArray(await mapTests(test.and, additionalTests, extensionTests)));
142 }
143 return undefined;
144};
145
146const Or: CreatePushTest = async (test, additionalTests, extensionTests) => {
147 if (test.or) {
148 return or(...toArray(await mapTests(test.or, additionalTests, extensionTests)));
149 }
150 return undefined;
151};
152
153const AdditionalTest: CreatePushTest = async (test, additionalTests) => {
154 if (!!test.use && !!additionalTests[test.use]) {
155 return additionalTests[test.use];
156 }
157 return undefined;
158};
159
160const FunctionTest: CreatePushTest = async test => {
161 if (typeof test === "function") {
162 return pushTest(test.toString(), test);
163 }
164 return undefined;
165};
166
167const ExtensionTest = async (test, additionalTests, extensionTests) => {
168 for (const extTestName in extensionTests) {
169 if (test.use === extTestName) {
170 const extTest = await extensionTests[extTestName](test.parameters || {});
171 if (!!extTest.name && !!extTest.mapping) {
172 return extTest;
173 } else {
174 return pushTest(extTestName, extTest);
175 }
176 }
177 }
178 return undefined;
179};
180
181export const CreatePushTests = [
182 HasFile,
183 IsRepo,
184 IsBranch,
185 IsDefaultBranch,
186 IsGoal,
187 IsMaterialChange,
188 HasFileContaining,
189 HasResourceProvider,
190 Not,
191 And,
192 Or,
193 AdditionalTest,
194 FunctionTest,
195 ExtensionTest,
196];
197
198export async function mapTest(test: any,
199 additionalTests: Record<string, PushTest>,
200 extensionTests: Record<string, PushTestMaker>): Promise<PushTest> {
201 for (const createPushTest of CreatePushTests) {
202 const pt = await createPushTest(test, additionalTests, extensionTests);
203 if (!!pt) {
204 return pt;
205 }
206 }
207 throw new Error(`Unable to construct push test from '${JSON.stringify(test)}'`);
208}
209
210function getGlobPatterns(test: any): string[] {
211 const pattern = test.globPattern || test.pattern || test.globPatterns || test.patterns;
212 return toArray(pattern);
213}