UNPKG

10.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const stringify = require("json-stringify-safe");
4const _ = require("lodash");
5const ApolloGraphClient_1 = require("../graph/ApolloGraphClient");
6const HandlerResult_1 = require("../HandlerResult");
7const NodeConfigSecretResolver_1 = require("../internal/env/NodeConfigSecretResolver");
8const metadata_1 = require("../internal/metadata/metadata");
9const metadataReading_1 = require("../internal/metadata/metadataReading");
10const parameterPopulation_1 = require("../internal/parameterPopulation");
11const logger_1 = require("../internal/util/logger");
12const string_1 = require("../internal/util/string");
13const SmartParameters_1 = require("../SmartParameters");
14const MetadataProcessor_1 = require("../spi/env/MetadataProcessor");
15const constructionUtils_1 = require("../util/constructionUtils");
16const AbstractAutomationServer_1 = require("./AbstractAutomationServer");
17/**
18 * Simple automation server that offers building style
19 * configuration
20 */
21class BuildableAutomationServer extends AbstractAutomationServer_1.AbstractAutomationServer {
22 constructor(opts) {
23 super();
24 this.opts = opts;
25 this.commandHandlers = [];
26 this.eventHandlers = [];
27 this.ingesters = [];
28 this.secretResolver = new NodeConfigSecretResolver_1.NodeConfigSecretResolver();
29 this.metadataProcessor = new MetadataProcessor_1.PassThroughMetadataProcessor();
30 if (opts.secretResolver) {
31 this.secretResolver = opts.secretResolver;
32 }
33 if (opts.metadataProcessor) {
34 this.metadataProcessor = opts.metadataProcessor;
35 }
36 if (opts.endpoints && opts.endpoints.graphql) {
37 if (opts.workspaceIds || opts.teamIds) {
38 let workspaceIds;
39 if (opts.workspaceIds.length === 1) {
40 workspaceIds = opts.workspaceIds[0];
41 }
42 else if (opts.workspaceIds.length > 1) {
43 workspaceIds = opts.workspaceIds[0];
44 }
45 else if (opts.teamIds.length === 1) {
46 workspaceIds = opts.teamIds[0];
47 }
48 else if (opts.teamIds.length > 1) {
49 workspaceIds = opts.teamIds[0];
50 }
51 if (workspaceIds) {
52 if (opts.token) {
53 this.graphClient = new ApolloGraphClient_1.ApolloGraphClient(`${opts.endpoints.graphql}/${workspaceIds}`, { Authorization: `token ${opts.token}` });
54 }
55 else if (opts.apiKey) {
56 this.graphClient = new ApolloGraphClient_1.ApolloGraphClient(`${opts.endpoints.graphql}/${workspaceIds}`, { Authorization: `Bearer ${opts.apiKey}` });
57 }
58 }
59 }
60 }
61 }
62 registerCommandHandler(chm) {
63 const factory = constructionUtils_1.toFactory(chm);
64 const instanceToInspect = factory();
65 if (instanceToInspect) {
66 const md = metadataReading_1.metadataFromInstance(instanceToInspect);
67 if (!md) {
68 throw new Error(`Cannot get metadata from handler '${stringify(instanceToInspect)}'`);
69 }
70 this.commandHandlers.push({
71 metadata: this.metadataProcessor.process(md, this.opts),
72 invoke: (i, ctx) => {
73 const newHandler = factory();
74 const params = !!newHandler.freshParametersInstance ? newHandler.freshParametersInstance() : newHandler;
75 return this.invokeCommandHandlerWithFreshParametersInstance(newHandler, md, params, i, ctx);
76 },
77 });
78 }
79 return this;
80 }
81 fromCommandHandler(hc) {
82 const md = metadata_1.isCommandHandlerMetadata(hc) ? hc : metadataReading_1.metadataFromInstance(hc);
83 this.commandHandlers.push({
84 metadata: this.metadataProcessor.process(md, this.opts),
85 invoke: (i, ctx) => {
86 const freshParams = !!hc.freshParametersInstance ? hc.freshParametersInstance() : hc;
87 return this.invokeCommandHandlerWithFreshParametersInstance(hc, md, freshParams, i, ctx);
88 },
89 });
90 return this;
91 }
92 registerEventHandler(maker) {
93 const factory = constructionUtils_1.toFactory(maker);
94 const instanceToInspect = factory();
95 if (instanceToInspect) {
96 const md = metadataReading_1.metadataFromInstance(instanceToInspect);
97 if (!md) {
98 throw new Error(`Cannot get metadata from event handler '${stringify(instanceToInspect)}'`);
99 }
100 this.eventHandlers.push({
101 metadata: this.metadataProcessor.process(md, this.opts),
102 invoke: (e, ctx) => this.invokeFreshEventHandlerInstance(factory(), md, e, ctx),
103 });
104 }
105 return this;
106 }
107 registerIngester(ingester) {
108 this.ingesters.push(ingester);
109 return this;
110 }
111 invokeCommandHandler(invocation, metadata, ctx) {
112 const handler = this.commandHandlers.find(a => a.metadata.name === invocation.name);
113 logger_1.logger.info("Invoking command handler '%s'", metadata.name);
114 return handler.invoke(invocation, ctx);
115 }
116 invokeEventHandler(e, metadata, ctx) {
117 const handler = this.eventHandlers.find(a => a.metadata.name === metadata.name);
118 logger_1.logger.info("Invoking event handler '%s'", metadata.name);
119 return handler.invoke(e, ctx);
120 }
121 /**
122 * Populate handler parameters
123 */
124 invokeCommandHandlerWithFreshParametersInstance(h, md, params, invocation, ctx) {
125 parameterPopulation_1.populateParameters(params, md, invocation.args);
126 parameterPopulation_1.populateValues(params, md, this.opts);
127 this.populateMappedParameters(params, md, invocation);
128 this.populateSecrets(params, md, invocation.secrets);
129 const bindAndValidate = SmartParameters_1.isSmartParameters(params) ?
130 Promise.resolve(params.bindAndValidate()) :
131 Promise.resolve();
132 return bindAndValidate
133 .then(vr => {
134 if (SmartParameters_1.isValidationError(vr)) {
135 return Promise.reject(`Validation failure invoking command handler '${md.name}': [${vr.message}]`);
136 }
137 const handlerResult = h.handle(this.enrichContext(ctx), params);
138 if (!handlerResult) {
139 return HandlerResult_1.SuccessPromise;
140 }
141 return handlerResult
142 .then(result => {
143 if (result) {
144 return result;
145 }
146 else {
147 return HandlerResult_1.SuccessPromise;
148 }
149 });
150 });
151 }
152 invokeFreshEventHandlerInstance(h, metadata, e, ctx) {
153 this.populateSecrets(h, metadata, e.secrets);
154 parameterPopulation_1.populateValues(h, metadata, this.opts);
155 const handlerResult = h.handle(e, this.enrichContext(ctx), h);
156 if (!handlerResult) {
157 return HandlerResult_1.SuccessPromise;
158 }
159 return handlerResult
160 .then(result => {
161 if (result) {
162 return result;
163 }
164 else {
165 return HandlerResult_1.SuccessPromise;
166 }
167 });
168 }
169 enrichContext(ctx) {
170 ctx.graphClient = ctx.graphClient || this.graphClient;
171 return ctx;
172 }
173 populateMappedParameters(h, metadata, invocation) {
174 // Resolve from the invocation, otherwise from our fallback
175 class InvocationSecretResolver {
176 constructor(mp) {
177 this.mp = mp;
178 }
179 resolve(key) {
180 const value = this.mp.find(a => a.name === key);
181 if (value) {
182 return String(value.value);
183 }
184 throw new Error(`Cannot resolve mapped parameter '${key}'`);
185 }
186 }
187 // if the bot sends any of them, then only use those?
188 // it does not fallback for each parameter; all or nothing.
189 // this is probably by design ... is there a test/dev circumstance where
190 // mappedParameters is not populated?
191 const mrResolver = invocation.mappedParameters ?
192 new InvocationSecretResolver(invocation.mappedParameters) :
193 this.secretResolver;
194 // logger.debug("Applying mapped parameters");
195 const mappedParameters = metadata.mapped_parameters || [];
196 const invMps = invocation.mappedParameters || [];
197 mappedParameters.forEach(mp => {
198 if (invMps.some(im => im.name === mp.name) || mp.required) {
199 _.update(h, mp.name, () => mrResolver.resolve(mp.name));
200 }
201 });
202 }
203 populateSecrets(h, metadata, invocationSecrets) {
204 // Resolve from the invocation, otherwise from our fallback
205 class InvocationSecretResolver {
206 constructor(sec) {
207 this.sec = sec;
208 }
209 resolve(key) {
210 const value = this.sec.find(a => a.uri === key);
211 if (value) {
212 return String(value.value);
213 }
214 throw new Error(`Cannot resolve secret '${key}'`);
215 }
216 }
217 const secretResolver = invocationSecrets ? new InvocationSecretResolver(invocationSecrets) :
218 this.secretResolver;
219 // logger.debug("Applying secrets");
220 const secrets = metadata.secrets || [];
221 secrets.forEach(s => {
222 _.update(h, s.name, () => secretResolver.resolve(s.uri));
223 });
224 }
225 get automations() {
226 return {
227 name: this.opts.name,
228 version: this.opts.version,
229 policy: this.opts.policy,
230 team_ids: this.opts.workspaceIds && this.opts.workspaceIds.length > 0 ? this.opts.workspaceIds : this.opts.teamIds,
231 groups: string_1.toStringArray(this.opts.groups),
232 keywords: this.opts.keywords,
233 commands: this.commandHandlers.map(e => e.metadata),
234 events: this.eventHandlers.map(e => e.metadata),
235 ingesters: this.ingesters,
236 };
237 }
238}
239exports.BuildableAutomationServer = BuildableAutomationServer;
240//# sourceMappingURL=BuildableAutomationServer.js.map
\No newline at end of file