UNPKG

9.51 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 && opts.workspaceIds && opts.workspaceIds.length > 0 && opts.apiKey) {
37 this.graphClient = new ApolloGraphClient_1.ApolloGraphClient(`${opts.endpoints.graphql}/${opts.workspaceIds[0]}`, { Authorization: `Bearer ${opts.apiKey}` });
38 }
39 }
40 registerCommandHandler(chm) {
41 const factory = constructionUtils_1.toFactory(chm);
42 const instanceToInspect = factory();
43 if (instanceToInspect) {
44 const md = metadataReading_1.metadataFromInstance(instanceToInspect);
45 if (!md) {
46 throw new Error(`Cannot get metadata from handler '${stringify(instanceToInspect)}'`);
47 }
48 this.commandHandlers.push({
49 metadata: this.metadataProcessor.process(md, this.opts),
50 invoke: (i, ctx) => {
51 const newHandler = factory();
52 const params = !!newHandler.freshParametersInstance ? newHandler.freshParametersInstance() : newHandler;
53 return this.invokeCommandHandlerWithFreshParametersInstance(newHandler, md, params, i, ctx);
54 },
55 });
56 }
57 return this;
58 }
59 fromCommandHandler(hc) {
60 const md = metadata_1.isCommandHandlerMetadata(hc) ? hc : metadataReading_1.metadataFromInstance(hc);
61 this.commandHandlers.push({
62 metadata: this.metadataProcessor.process(md, this.opts),
63 invoke: (i, ctx) => {
64 const freshParams = !!hc.freshParametersInstance ? hc.freshParametersInstance() : hc;
65 return this.invokeCommandHandlerWithFreshParametersInstance(hc, md, freshParams, i, ctx);
66 },
67 });
68 return this;
69 }
70 registerEventHandler(maker) {
71 const factory = constructionUtils_1.toFactory(maker);
72 const instanceToInspect = factory();
73 if (instanceToInspect) {
74 const md = metadataReading_1.metadataFromInstance(instanceToInspect);
75 if (!md) {
76 throw new Error(`Cannot get metadata from event handler '${stringify(instanceToInspect)}'`);
77 }
78 this.eventHandlers.push({
79 metadata: this.metadataProcessor.process(md, this.opts),
80 invoke: (e, ctx) => this.invokeFreshEventHandlerInstance(factory(), md, e, ctx),
81 });
82 }
83 return this;
84 }
85 registerIngester(ingester) {
86 this.ingesters.push(ingester);
87 return this;
88 }
89 invokeCommandHandler(invocation, metadata, ctx) {
90 const handler = this.commandHandlers.find(a => a.metadata.name === invocation.name);
91 logger_1.logger.info("Invoking command handler '%s'", metadata.name);
92 return handler.invoke(invocation, ctx);
93 }
94 invokeEventHandler(e, metadata, ctx) {
95 const handler = this.eventHandlers.find(a => a.metadata.name === metadata.name);
96 logger_1.logger.info("Invoking event handler '%s'", metadata.name);
97 return handler.invoke(e, ctx);
98 }
99 /**
100 * Populate handler parameters
101 */
102 invokeCommandHandlerWithFreshParametersInstance(h, md, params, invocation, ctx) {
103 parameterPopulation_1.populateParameters(params, md, invocation.args);
104 parameterPopulation_1.populateValues(params, md, this.opts);
105 this.populateMappedParameters(params, md, invocation);
106 this.populateSecrets(params, md, invocation.secrets);
107 const bindAndValidate = SmartParameters_1.isSmartParameters(params) ?
108 Promise.resolve(params.bindAndValidate()) :
109 Promise.resolve();
110 return bindAndValidate
111 .then(vr => {
112 if (SmartParameters_1.isValidationError(vr)) {
113 return Promise.reject(`Validation failure invoking command handler '${md.name}': [${vr.message}]`);
114 }
115 const handlerResult = h.handle(this.enrichContext(ctx), params);
116 if (!handlerResult) {
117 return HandlerResult_1.SuccessPromise;
118 }
119 return handlerResult
120 .then(result => {
121 if (result) {
122 return result;
123 }
124 else {
125 return HandlerResult_1.SuccessPromise;
126 }
127 });
128 });
129 }
130 invokeFreshEventHandlerInstance(h, metadata, e, ctx) {
131 this.populateSecrets(h, metadata, e.secrets);
132 parameterPopulation_1.populateValues(h, metadata, this.opts);
133 const handlerResult = h.handle(e, this.enrichContext(ctx), h);
134 if (!handlerResult) {
135 return HandlerResult_1.SuccessPromise;
136 }
137 return handlerResult
138 .then(result => {
139 if (result) {
140 return result;
141 }
142 else {
143 return HandlerResult_1.SuccessPromise;
144 }
145 });
146 }
147 enrichContext(ctx) {
148 ctx.graphClient = ctx.graphClient || this.graphClient;
149 return ctx;
150 }
151 populateMappedParameters(h, metadata, invocation) {
152 // Resolve from the invocation, otherwise from our fallback
153 class InvocationSecretResolver {
154 constructor(mp) {
155 this.mp = mp;
156 }
157 resolve(key) {
158 const value = this.mp.find(a => a.name === key);
159 if (value) {
160 return String(value.value);
161 }
162 throw new Error(`Cannot resolve mapped parameter '${key}'`);
163 }
164 }
165 // if the bot sends any of them, then only use those?
166 // it does not fallback for each parameter; all or nothing.
167 // this is probably by design ... is there a test/dev circumstance where
168 // mappedParameters is not populated?
169 const mrResolver = invocation.mappedParameters ?
170 new InvocationSecretResolver(invocation.mappedParameters) :
171 this.secretResolver;
172 // logger.debug("Applying mapped parameters");
173 const mappedParameters = metadata.mapped_parameters || [];
174 const invMps = invocation.mappedParameters || [];
175 mappedParameters.forEach(mp => {
176 if (invMps.some(im => im.name === mp.name) || mp.required) {
177 _.update(h, mp.name, () => mrResolver.resolve(mp.name));
178 }
179 });
180 }
181 populateSecrets(h, metadata, invocationSecrets) {
182 // Resolve from the invocation, otherwise from our fallback
183 class InvocationSecretResolver {
184 constructor(sec) {
185 this.sec = sec;
186 }
187 resolve(key) {
188 const value = this.sec.find(a => a.uri === key);
189 if (value) {
190 return String(value.value);
191 }
192 throw new Error(`Cannot resolve secret '${key}'`);
193 }
194 }
195 const secretResolver = invocationSecrets ? new InvocationSecretResolver(invocationSecrets) :
196 this.secretResolver;
197 // logger.debug("Applying secrets");
198 const secrets = metadata.secrets || [];
199 secrets.forEach(s => {
200 _.update(h, s.name, () => secretResolver.resolve(s.uri));
201 });
202 }
203 get automations() {
204 return {
205 name: this.opts.name,
206 version: this.opts.version,
207 policy: this.opts.policy,
208 team_ids: this.opts.workspaceIds,
209 groups: string_1.toStringArray(this.opts.groups),
210 keywords: this.opts.keywords,
211 commands: this.commandHandlers.map(e => e.metadata),
212 events: this.eventHandlers.map(e => e.metadata),
213 ingesters: this.ingesters,
214 };
215 }
216}
217exports.BuildableAutomationServer = BuildableAutomationServer;
218//# sourceMappingURL=BuildableAutomationServer.js.map
\No newline at end of file