UNPKG

22 kBTypeScriptView Raw
1import * as exp from "express";
2import { HandleCommand } from "./HandleCommand";
3import { HandleEvent } from "./HandleEvent";
4import { ExpressServerOptions } from "./internal/transport/express/ExpressServer";
5import { AutomationEventListener } from "./server/AutomationEventListener";
6import { AutomationMetadataProcessor } from "./spi/env/MetadataProcessor";
7import { SecretResolver } from "./spi/env/SecretResolver";
8import { GraphClientFactory } from "./spi/graph/GraphClientFactory";
9import { HttpClientFactory } from "./spi/http/httpClient";
10import { WebSocketFactory } from "./spi/http/wsClient";
11import { StatsDClientFactory } from "./spi/statsd/statsdClient";
12import { Maker } from "./util/constructionUtils";
13/**
14 * Customize the express server configuration: For example to add custom routes
15 *
16 * Example:
17 *
18 * const newRouteCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => {
19 * express.get("/new-route", ...handlers, (req, res) => {
20 * res.json({ key: "value" });
21 * });
22 * }
23 */
24export declare type ExpressCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => void;
25/**
26 * Post process the configuration after is has been merged from the various locations, but
27 * before starting the automation client.
28 */
29export declare type ConfigurationPostProcessor<T = AnyOptions> = (configuration: Configuration) => Promise<Configuration & T>;
30/**
31 * A computed banner
32 */
33export interface Banner {
34 /**
35 * Banner content
36 */
37 banner: string;
38 /**
39 * Whether or not the banner content should be asciified
40 */
41 asciify: boolean;
42 color: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray";
43}
44/**
45 * A section that should be displayed in the banner.
46 */
47export interface BannerSection {
48 title: string;
49 body: string;
50}
51/**
52 * Custom configuration you can abuse to your benefit
53 */
54export interface AnyOptions {
55 /** Abuse goes here */
56 [key: string]: any;
57}
58/**
59 * Options for an automation node.
60 */
61export interface AutomationOptions extends AnyOptions {
62 /**
63 * Automation name. If not given, the name is extracted from
64 * the package.json.
65 */
66 name?: string;
67 /**
68 * Automation version. Must be a valid semantic version,
69 * https://semver.org/. If not given, the version is extracted
70 * from the package.json.
71 */
72 version?: string;
73 /**
74 * Atomist workspaces this automation will be registered with. Must be
75 * specified if groups is not specified. Cannot be specified if
76 * groups is specified.
77 */
78 workspaceIds?: string[];
79 /**
80 * DO NOT USE. Groups this automation will be registered with.
81 * Must be specified if teams is not specified. Cannot be
82 * specified if teams is specified. Providing groups indicates
83 * this is a global automation, which can only successfully be
84 * registered by Atomist.
85 */
86 groups?: string[];
87 /**
88 * If events should be queued when the registration is not
89 * connected to the websocket, specificy "durable". "ephemeral"
90 * is suited for testing and running locally and is the default.
91 */
92 policy?: "ephemeral" | "durable";
93 /**
94 * Atomist API Key used to authenticate the user starting the client.
95 */
96 apiKey?: string;
97 /** HTTP configuration, useful for health checks */
98 http?: {
99 enabled?: boolean;
100 client?: {
101 factory?: HttpClientFactory;
102 };
103 } & Partial<ExpressServerOptions>;
104 /** websocket configuration */
105 ws?: {
106 enabled?: boolean;
107 client?: {
108 factory?: WebSocketFactory;
109 };
110 termination?: {
111 /**
112 * If true, wait for up to `gracePeriod` milliseconds to
113 * process in-flight and queued requests.
114 */
115 graceful?: boolean;
116 /**
117 * Grace period in milliseconds. Note the actual time to
118 * shutdown gracefully may be more than twice this, as
119 * this period is used to first wait for requests and then
120 * used again to wait for any cluster workers to shutdown.
121 * If some part of the shutdown hangs, it could take up to
122 * ten times this period for all processes to exit.
123 */
124 gracePeriod?: number;
125 };
126 /** compress messages over websocket */
127 compress?: boolean;
128 /** timeout in milliseconds */
129 timeout?: number;
130 /** Configure backoff behavior on the WS connection */
131 backoff?: {
132 /**
133 * Max number of the pending messages in the queue before
134 * initiating backoff
135 */
136 threshold?: number;
137 /** Interval in ms to check threshold */
138 interval?: number;
139 /**
140 * Duration in ms the backend should backoff before sending
141 * any more messages
142 */
143 duration?: number;
144 /**
145 * Factor (0 < x <= 1) multiply threshold to get to the lower backoff
146 * boundary
147 */
148 factor?: number;
149 };
150 };
151 graphql?: {
152 client?: {
153 factory: GraphClientFactory;
154 };
155 };
156 /** Atomist API endpoints */
157 endpoints?: {
158 graphql?: string;
159 api?: string;
160 auth?: string;
161 };
162 /**
163 * Post-processors can be used to modify the configuration after
164 * all standard configuration loading has been done and before the
165 * client is started. Post-processors return a configuration
166 * promise so they can be asynchronous.
167 */
168 postProcessors?: ConfigurationPostProcessor[];
169}
170/**
171 * Options useful when running an automation client in server mode.
172 */
173export interface AutomationServerOptions extends AutomationOptions {
174 /** environment automation is running in, e.g., "production" or "testing" */
175 environment?: string;
176 /**
177 * Application identifier used for metrics send to statsd. If not
178 * set, the automation client package name with any namespace
179 * prefix removed is used.
180 */
181 application?: string;
182 /** keywords useful for discovery */
183 keywords?: string[];
184 /** Whether and where to send application start and stop events to Atomist. */
185 applicationEvents?: {
186 enabled?: boolean;
187 workspaceId?: string;
188 };
189 /**
190 * Whether and how many workers to start up. If enabled is true
191 * and workers is false, a number of workers equal to the number
192 * of available CPUs will be started.
193 */
194 cluster?: {
195 enabled?: boolean;
196 workers?: number;
197 maxConcurrentPerWorker?: number;
198 };
199 /** Logging configuration */
200 logging?: {
201 /** Log level, default is "info" */
202 level?: "silly" | "debug" | "verbose" | "info" | "warn" | "error";
203 /**
204 * Custom log configuration, useful if your logging solution
205 * requires host, port, token, etc. configuration.
206 */
207 custom?: any;
208 /**
209 * Print welcome banner; set to an arbitrary string to display,
210 * default is name of automation-client
211 */
212 banner?: {
213 enabled?: boolean;
214 /** Message or Banner to be printed at the top of the banner */
215 message?: string | ((configuration: Configuration) => Banner);
216 /**
217 * Add content to the banner which shows up between handlers and
218 * footer
219 */
220 contributors?: Array<(configuration: Configuration) => string | BannerSection>;
221 };
222 /**
223 * Log to file; set to file path to overwrite location and name of logfile,
224 * defaults to ./log/automation-client.log in current working directory
225 */
226 file?: {
227 enabled?: boolean;
228 name?: string;
229 level?: "silly" | "debug" | "verbose" | "info" | "warn" | "error";
230 };
231 };
232 /** Redaction configuration */
233 redact?: {
234 /** Redact log messages */
235 log?: boolean;
236 /** Redact messages send via the message client */
237 messages?: boolean;
238 /** Register patterns to look for and optional replacements */
239 patterns?: Array<{
240 regexp: RegExp | string;
241 replacement?: string;
242 }>;
243 };
244 /** statsd config */
245 statsd?: {
246 /** Whether to send metrics statsd, default is false */
247 enabled?: boolean;
248 /**
249 * statsd host. If not set, use the host-shots default,
250 * "localhost" at the time of this writing.
251 */
252 host?: string;
253 /**
254 * statsd port. If not set, use the hot-shots default, 8125
255 * at the time of this writing.
256 */
257 port?: number;
258 /**
259 * statsd client factory to create instances of StatsDClient
260 */
261 client?: {
262 factory: StatsDClientFactory;
263 };
264 };
265 /** Register a custom secret resolver */
266 secretResolver?: SecretResolver;
267 /** Register a custom AutomationMetadataProcessor */
268 metadataProcessor?: AutomationMetadataProcessor;
269}
270/**
271 * Atomist automation configuration.
272 */
273export interface Configuration extends AutomationServerOptions {
274 /**
275 * Automation commands this package provides. If empty or null,
276 * the package will be scanned for commands, which must be under a
277 * directory named "commands".
278 */
279 commands?: Array<Maker<HandleCommand>>;
280 /**
281 * Automation event handlers this package provides. If empty or
282 * null, the package will be scanned for event handlers, which
283 * must be under a directory named "events".
284 */
285 events?: Array<Maker<HandleEvent>>;
286 /** Custom event ingester */
287 ingesters?: string[];
288 /** Log and metric sinks */
289 listeners?: AutomationEventListener[];
290}
291/**
292 * User per-automation configuration
293 */
294export interface ModuleOptions extends AutomationServerOptions {
295 /** Automation name this configuration applies to. */
296 name: string;
297 /**
298 * A valid version or version range, as defined by
299 * https://www.npmjs.com/package/semver, this configuration
300 * applies to. If not provided, it applies to all versions of the
301 * named automation.
302 */
303 version?: string;
304}
305/**
306 * User-wide configuration and user per-automation configuration
307 */
308export interface UserConfig extends AutomationServerOptions {
309 modules?: ModuleOptions[];
310}
311/**
312 * Generate defaults for various configuration option values. These
313 * will only be used if values are not provided by any source. Values
314 * not provided here will be `undefined`.
315 *
316 * @return default configuration
317 */
318export declare function defaultConfiguration(): Configuration;
319/**
320 * Exposes the configuration for lookup of configuration values.
321 * This is useful for components to obtain values eg. from configuration.custom
322 * like user provided secrets etc.
323 * @param {string} path the property path evaluated against the configuration instance
324 * @returns {T}
325 */
326export declare function configurationValue<T>(path?: string, defaultValue?: T): T;
327/**
328 * Return user automation client configuration path.
329 */
330export declare function userConfigPath(): string;
331/**
332 * Return user automation client configuration paths including
333 * such referenced by configuration profiles.
334 */
335export declare function userConfigPaths(): string[];
336/**
337 * Write user config securely, creating directories as necessary.
338 */
339export declare function writeUserConfig(cfg: UserConfig): Promise<void>;
340/**
341 * Read and return user config from UserConfigFile.
342 */
343export declare function getUserConfig(): UserConfig;
344/**
345 * Overwrite values in the former configuration with values in the
346 * latter. The start object is modified.
347 *
348 * @param obj starting configuration
349 * @param override configuration values to add/override those in start
350 * @return resulting merged configuration
351 */
352export declare function mergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration;
353/**
354 * Overwrite values in the former configuration with values in the
355 * latter. The start object is modified. Arrays are concatenated.
356 *
357 * @param obj starting configuration
358 * @param override configuration values to add/override those in start
359 * @return resulting merged configuration
360 */
361export declare function deepMergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration;
362/**
363 * Merge a user's global and proper per-module configuration, if it
364 * exists. Values from the per-module configuration take precedence
365 * over the user-wide values. Per-module configuration is gotten from
366 * the first per-module configuration that matches name and,
367 * optionally, the version is within the per-module configuration's
368 * version range. A module configuration without a version range
369 * matches the named module with any version. If no version is
370 * provided, any version range is satisfied, meaning the first
371 * per-module configuration with a matching name is used. If no name
372 * is provide, only the user configuration is loaded. The first
373 * per-module match is used. This means if you have multiple
374 * configurations for the same named module and you want to include a
375 * default configuration for that module, put a configuration without
376 * a version range _after_ all the configurations with version ranges.
377 * Note that only values from the first per-module match are used.
378 *
379 * @param userConfig the user's configuration, which may include per-module configuration
380 * @param name automation client package name to load as module config if it exists
381 * @param version automation client package version to load as module config if
382 * version satifies module config version range
383 * @return the merged module and user configuration
384 */
385export declare function resolveModuleConfig(userConfig: UserConfig, name?: string, version?: string): AutomationServerOptions;
386/**
387 * Try to read user config, overriding its values with a per-module
388 * configuration that matches this automation.
389 *
390 * @param name automation client package name to load as module config if it exists
391 * @param version automation client package version to load as module config if
392 * version satifies module config version range
393 * @return module-specific config with user config supplying defaults
394 */
395export declare function loadUserConfiguration(name?: string, version?: string): AutomationServerOptions;
396/**
397 * Load the automation configuration from the configuration object
398 * exported from cfgPath and return it. If no configuration path is
399 * provided, the package will be searched for a file named
400 * atomist.config.js. If no atomist.config.js is found, an empty
401 * object is returned. If more than one is found, an exception is
402 * thrown.
403 *
404 * @param cfgPath location of automation configuration
405 * @return automation configuration or undefined
406 */
407export declare function loadAutomationConfig(configPath?: string): Promise<Configuration | undefined>;
408/**
409 * Load the automation configuration from the configuration objects
410 * exported and merged by all index.js files in the automation client.
411 *
412 * @return automation configuration
413 */
414export declare function loadIndexConfig(): Promise<Configuration>;
415/**
416 * Load configuration from the file defined by the ATOMIST_CONFIG_PATH
417 * environment variable, if it the variable is defined and the file
418 * exists, and return it. The contents of the ATOMIST_CONFIG_PATH
419 * file should be serialized JSON of AutomationServerOptions. If the
420 * environment variable is not defined or the file path specified by
421 * its value cannot be read as JSON, an empty object is returned.
422 *
423 * @return automation server options
424 */
425export declare function loadAtomistConfigPath(): AutomationServerOptions;
426/**
427 * Load configuration from the ATOMIST_CONFIG environment variable, if
428 * it the variable is defined, and merge it into the passed in
429 * configuration. The value of the ATOMIST_CONFIG environment
430 * variable should be serialized JSON of AutomationServerOptions. The
431 * values from the environment variable will override values in the
432 * passed in configuration. If the environment variable is not
433 * defined, the passed in configuration is returned unchanged.
434 *
435 * @return automation server options
436 */
437export declare function loadAtomistConfig(): AutomationServerOptions;
438/**
439 * Examine environment, config, and cfg for Atomist workspace IDs.
440 * The ATOMIST_WORKSPACES environment variable takes precedence over
441 * the configuration "workspaceIds", which takes precedence over
442 * cfg.workspaceId, which may be undefined, null, or an empty array.
443 * If the ATOMIST_WORKSPACES environment variable is not set,
444 * workspaceIds is not set in config, and workspaceIds is falsey in
445 * cfg and teamIds is resolvable from the configuration, workspaceIds
446 * is set to teamIds.
447 *
448 * @param cfg current configuration, whose workspaceIds and teamIds
449 * properties may be modified by this function
450 * @return the resolved workspace IDs
451 */
452export declare function resolveWorkspaceIds(cfg: Configuration): string[];
453/**
454 * Resolve a value from a environment variables or configuration keys.
455 * The environment variables are checked in order and take precedence
456 * over the configuration key, which are also checked in order. If
457 * no truthy values are found, undefined is returned.
458 *
459 * @param environmentVariables environment variables to check
460 * @param configKeyPaths configuration keys, as JSON paths, to check
461 * @param defaultValue value to use if no environment variables or config keys have values
462 * @return first truthy value found, or defaultValue
463 */
464export declare function resolveConfigurationValue(environmentVariables: string[], configKeyPaths: string[], defaultValue?: string): string;
465/**
466 * Resolve the HTTP port from the environment and configuration. The
467 * PORT environment variable takes precedence over the config value.
468 */
469export declare function resolvePort(cfg: Configuration): number;
470/**
471 * Resolve ATOMIST_ environment variables and add them to config.
472 * Variables of like ATOMIST_custom_foo_bar will be converted to
473 * a json path of custom.foo.bar.
474 * @param {Configuration} cfg
475 */
476export declare function resolveEnvironmentVariables(cfg: Configuration): void;
477/**
478 * Resolve placeholders against the process.env.
479 * Placeholders should be of form ${ENV_VAR}. Placeholders support default values
480 * in case they aren't defined: ${ENV_VAR:default value}
481 */
482export declare function resolvePlaceholders(cfg: Configuration, replacer?: (value: string) => Promise<string>): Promise<void>;
483/**
484 * Invoke postProcessors on the provided configuration.
485 */
486export declare function invokePostProcessors(cfg: Configuration): Promise<Configuration>;
487/**
488 * Make sure final configuration has the minimum configuration it
489 * needs. It will throw an error if required properties are missing.
490 *
491 * @param cfg final configuration
492 */
493export declare function validateConfiguration(cfg: Configuration): void;
494/**
495 * Load and populate the automation configuration. The configuration
496 * is loaded from several locations with the following precedence from
497 * highest to lowest.
498 *
499 * 0. Recognized environment variables (see below)
500 * 1. The value of the ATOMIST_CONFIG environment variable, parsed as
501 * JSON and cast to AutomationServerOptions
502 * 2. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions
503 * 3. The contents of the user's client.config.json as UserConfig
504 * resolving user and per-module configuration into Configuration
505 * 4. The automation's index.js (or atomist.config.js) exported configuration as
506 * Configuration
507 * 5. ProductionDefaultConfiguration if ATOMIST_ENV or NODE_ENV is set
508 * to "production" or TestingDefaultConfiguration if ATOMIST_ENV or
509 * NODE_ENV is set to "staging" or "testing", with ATOMIST_ENV
510 * taking precedence over NODE_ENV.
511 * 6. LocalDefaultConfiguration
512 *
513 * If any of the sources are missing, they are ignored. Any truthy
514 * configuration values specified by sources of higher precedence
515 * cause any values provided by sources of lower precedence to be
516 * ignored. Arrays are replaced, not merged. Typically the only
517 * required values in the configuration for a successful registration
518 * are the apiKey and non-empty workspaceIds.
519 *
520 * Placeholder of the form `${ENV_VARIABLE}` in string configuration
521 * values will get resolved against the environment. The resolution
522 * happens at the very end when all configs have been merged.
523 *
524 * The configuration exported from the index.js (or atomist.config.js) is modified
525 * to contain the final configuration values and returned from this
526 * function.
527 *
528 * @param cfgPath path to file exporting the configuration object, if
529 * not provided the package is searched for one
530 * @return merged configuration object
531 */
532export declare function loadConfiguration(cfgPath?: string): Promise<Configuration>;
533/**
534 * Default set of regular expressions used to remove sensitive
535 * information from messages and logs. The entries are applied in
536 * order, so more specific regular expressions should be placed
537 * earlier in the list to avoid a shorter replacement preventing a
538 * longer replacement from being applied.
539 */
540export declare const DEFAULT_REDACTION_PATTERNS: {
541 regexp: RegExp;
542 replacement: string;
543}[];
544/**
545 * Default configuration when running in neither testing or
546 * production.
547 */
548export declare const LocalDefaultConfiguration: Configuration;
549/**
550 * Configuration defaults for production environments.
551 */
552export declare const ProductionDefaultConfiguration: Partial<Configuration>;
553/**
554 * Configuration defaults for pre-production environments.
555 */
556export declare const TestingDefaultConfiguration: Partial<Configuration>;
557//# sourceMappingURL=configuration.d.ts.map
\No newline at end of file