import * as exp from "express"; import { GraphClientListener } from "./graph/ApolloGraphClient"; import { HandleCommand } from "./HandleCommand"; import { HandleEvent } from "./HandleEvent"; import { ExpressServerOptions } from "./internal/transport/express/ExpressServer"; import { RequestProcessor } from "./internal/transport/RequestProcessor"; import { AutomationEventListener } from "./server/AutomationEventListener"; import { AutomationServer } from "./server/AutomationServer"; import { AutomationMetadataProcessor } from "./spi/env/MetadataProcessor"; import { SecretResolver } from "./spi/env/SecretResolver"; import { GraphClientFactory } from "./spi/graph/GraphClientFactory"; import { HttpClientFactory } from "./spi/http/httpClient"; import { WebSocketFactory } from "./spi/http/wsClient"; import { StatsDClientFactory } from "./spi/statsd/statsdClient"; import { Maker } from "./util/constructionUtils"; /** * Customize the express server configuration: For example to add custom routes * * Example: * * const newRouteCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => { * express.get("/new-route", ...handlers, (req, res) => { * res.json({ key: "value" }); * }); * } */ export declare type ExpressCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => void; /** * Post process the configuration after is has been merged from the various locations, but * before starting the automation client. */ export declare type ConfigurationPostProcessor = (configuration: Configuration) => Promise; /** * A computed banner */ export interface Banner { /** * Banner content */ banner: string; /** * Whether or not the banner content should be asciified */ asciify: boolean; color: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray"; } /** * A section that should be displayed in the banner. */ export interface BannerSection { title: string; body: string; } /** * Custom configuration you can abuse to your benefit */ export interface AnyOptions { /** Abuse goes here */ [key: string]: any; } /** * Options for an automation node. */ export interface AutomationOptions extends AnyOptions { /** * Automation name. If not given, the name is extracted from * the package.json. */ name?: string; /** * Automation version. Must be a valid semantic version, * https://semver.org/. If not given, the version is extracted * from the package.json. */ version?: string; /** * Atomist workspaces this automation will be registered with. Must be * specified if groups is not specified. Cannot be specified if * groups is specified. */ workspaceIds?: string[]; /** * DO NOT USE. Groups this automation will be registered with. * Must be specified if teams is not specified. Cannot be * specified if teams is specified. Providing groups indicates * this is a global automation, which can only successfully be * registered by Atomist. */ groups?: string[]; /** * If events should be queued when the registration is not * connected to the websocket, specificy "durable". "ephemeral" * is suited for testing and running locally and is the default. */ policy?: "ephemeral" | "durable"; /** * Atomist API Key used to authenticate the user starting the client. */ apiKey?: string; /** HTTP configuration, useful for health checks */ http?: { enabled?: boolean; client?: { factory?: HttpClientFactory; }; } & Partial; /** websocket configuration */ ws?: { enabled?: boolean; client?: { factory?: WebSocketFactory; }; termination?: { /** * If true, wait for up to `gracePeriod` milliseconds to * process in-flight and queued requests. */ graceful?: boolean; /** * Grace period in milliseconds. Note the actual time to * shutdown gracefully may be more than twice this, as * this period is used to first wait for requests and then * used again to wait for any cluster workers to shutdown. * If some part of the shutdown hangs, it could take up to * ten times this period for all processes to exit. */ gracePeriod?: number; }; /** compress messages over websocket */ compress?: boolean; /** timeout in milliseconds */ timeout?: number; /** Configure backoff behavior on the WS connection */ backoff?: { /** * Max number of the pending messages in the queue before * initiating backoff */ threshold?: number; /** Interval in ms to check threshold */ interval?: number; /** * Duration in ms the backend should backoff before sending * any more messages */ duration?: number; /** * Factor (0 < x <= 1) multiply threshold to get to the lower backoff * boundary */ factor?: number; }; }; graphql?: { client?: { factory: GraphClientFactory; }; listeners?: GraphClientListener[]; }; /** Atomist API endpoints */ endpoints?: { graphql?: string; api?: string; auth?: string; }; /** * Post-processors can be used to modify the configuration after * all standard configuration loading has been done and before the * client is started. Post-processors return a configuration * promise so they can be asynchronous. */ postProcessors?: ConfigurationPostProcessor[]; requestProcessorFactory?: (automations: AutomationServer, configuration: Configuration, listeners: AutomationEventListener[]) => RequestProcessor; } /** * Options useful when running an automation client in server mode. */ export interface AutomationServerOptions extends AutomationOptions { /** environment automation is running in, e.g., "production" or "testing" */ environment?: string; /** * Application identifier used for metrics send to statsd. If not * set, the automation client package name with any namespace * prefix removed is used. */ application?: string; /** keywords useful for discovery */ keywords?: string[]; /** Whether and where to send application start and stop events to Atomist. */ applicationEvents?: { enabled?: boolean; workspaceId?: string; }; /** * Whether and how many workers to start up. If enabled is true * and workers is false, a number of workers equal to the number * of available CPUs will be started. */ cluster?: { enabled?: boolean; workers?: number; maxConcurrentPerWorker?: number; }; /** Logging configuration */ logging?: { /** Log level, default is "info" */ level?: "silly" | "debug" | "verbose" | "info" | "warn" | "error"; /** Log the file name and line number of the JS file calling the log method */ callsite?: boolean; /** Enable color output in log output */ color?: boolean; /** * Custom log configuration, useful if your logging solution * requires host, port, token, etc. configuration. */ custom?: any; /** * Print welcome banner; set to an arbitrary string to display, * default is name of automation-client */ banner?: { enabled?: boolean; /** Message or Banner to be printed at the top of the banner */ message?: string | ((configuration: Configuration) => Banner); /** * Add content to the banner which shows up between handlers and * footer */ contributors?: Array<(configuration: Configuration) => string | BannerSection>; }; /** * Log to file; set to file path to overwrite location and name of logfile, * defaults to ./log/automation-client.log in current working directory */ file?: { enabled?: boolean; name?: string; level?: "silly" | "debug" | "verbose" | "info" | "warn" | "error"; }; }; /** Redaction configuration */ redact?: { /** Redact log messages */ log?: boolean; /** Redact messages send via the message client */ messages?: boolean; /** Register patterns to look for and optional replacements */ patterns?: Array<{ regexp: RegExp | string; replacement?: string; }>; }; /** statsd config */ statsd?: { /** Whether to send metrics statsd, default is false */ enabled?: boolean; /** * statsd host. If not set, use the host-shots default, * "localhost" at the time of this writing. */ host?: string; /** * statsd port. If not set, use the hot-shots default, 8125 * at the time of this writing. */ port?: number; /** * statsd client factory to create instances of StatsDClient */ client?: { factory: StatsDClientFactory; }; }; /** Register a custom secret resolver */ secretResolver?: SecretResolver; /** Register a custom AutomationMetadataProcessor */ metadataProcessor?: AutomationMetadataProcessor; } /** * Atomist automation configuration. */ export interface Configuration extends AutomationServerOptions { /** * Automation commands this package provides. If empty or null, * the package will be scanned for commands, which must be under a * directory named "commands". */ commands?: Array>; /** * Automation event handlers this package provides. If empty or * null, the package will be scanned for event handlers, which * must be under a directory named "events". */ events?: Array>; /** Custom event ingester */ ingesters?: string[]; /** Log and metric sinks */ listeners?: AutomationEventListener[]; } /** * User per-automation configuration */ export interface ModuleOptions extends AutomationServerOptions { /** Automation name this configuration applies to. */ name: string; /** * A valid version or version range, as defined by * https://www.npmjs.com/package/semver, this configuration * applies to. If not provided, it applies to all versions of the * named automation. */ version?: string; } /** * User-wide configuration and user per-automation configuration */ export interface UserConfig extends AutomationServerOptions { modules?: ModuleOptions[]; } /** * Generate defaults for various configuration option values. These * will only be used if values are not provided by any source. Values * not provided here will be `undefined`. * * @return default configuration */ export declare function defaultConfiguration(): Configuration; /** * Exposes the configuration for lookup of configuration values. * This is useful for components to obtain values eg. from configuration.custom * like user provided secrets etc. * @param {string} path the property path evaluated against the configuration instance * @returns {T} */ export declare function configurationValue(path?: string, defaultValue?: T): T; /** * Return user automation client configuration path. */ export declare function userConfigPath(): string; /** * Return user automation client configuration paths including * such referenced by configuration profiles. */ export declare function userConfigPaths(): string[]; /** * Write user config securely, creating directories as necessary. */ export declare function writeUserConfig(cfg: UserConfig): Promise; /** * Read and return user config from UserConfigFile. */ export declare function getUserConfig(): UserConfig; /** * Overwrite values in the former configuration with values in the * latter. The start object is modified. * * @param obj starting configuration * @param override configuration values to add/override those in start * @return resulting merged configuration */ export declare function mergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration; /** * Overwrite values in the former configuration with values in the * latter. The start object is modified. Arrays are concatenated. * * @param obj starting configuration * @param override configuration values to add/override those in start * @return resulting merged configuration */ export declare function deepMergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration; /** * Merge a user's global and proper per-module configuration, if it * exists. Values from the per-module configuration take precedence * over the user-wide values. Per-module configuration is gotten from * the first per-module configuration that matches name and, * optionally, the version is within the per-module configuration's * version range. A module configuration without a version range * matches the named module with any version. If no version is * provided, any version range is satisfied, meaning the first * per-module configuration with a matching name is used. If no name * is provide, only the user configuration is loaded. The first * per-module match is used. This means if you have multiple * configurations for the same named module and you want to include a * default configuration for that module, put a configuration without * a version range _after_ all the configurations with version ranges. * Note that only values from the first per-module match are used. * * @param userConfig the user's configuration, which may include per-module configuration * @param name automation client package name to load as module config if it exists * @param version automation client package version to load as module config if * version satifies module config version range * @return the merged module and user configuration */ export declare function resolveModuleConfig(userConfig: UserConfig, name?: string, version?: string): AutomationServerOptions; /** * Try to read user config, overriding its values with a per-module * configuration that matches this automation. * * @param name automation client package name to load as module config if it exists * @param version automation client package version to load as module config if * version satifies module config version range * @return module-specific config with user config supplying defaults */ export declare function loadUserConfiguration(name?: string, version?: string): AutomationServerOptions; /** * Load the automation configuration from the configuration object * exported from cfgPath and return it. If no configuration path is * provided, the package will be searched for a file named * atomist.config.js. If no atomist.config.js is found, an empty * object is returned. If more than one is found, an exception is * thrown. * * @param cfgPath location of automation configuration * @return automation configuration or undefined */ export declare function loadAutomationConfig(configPath?: string): Promise; /** * Load the automation configuration from the configuration objects * exported and merged by all index.js files in the automation client. * * @return automation configuration */ export declare function loadIndexConfig(): Promise; /** * Load configuration from the file defined by the ATOMIST_CONFIG_PATH * environment variable, if it the variable is defined and the file * exists, and return it. The contents of the ATOMIST_CONFIG_PATH * file should be serialized JSON of AutomationServerOptions. If the * environment variable is not defined or the file path specified by * its value cannot be read as JSON, an empty object is returned. * * @return automation server options */ export declare function loadAtomistConfigPath(): AutomationServerOptions; /** * Load configuration from the ATOMIST_CONFIG environment variable, if * it the variable is defined, and merge it into the passed in * configuration. The value of the ATOMIST_CONFIG environment * variable should be serialized JSON of AutomationServerOptions. The * values from the environment variable will override values in the * passed in configuration. If the environment variable is not * defined, the passed in configuration is returned unchanged. * * @return automation server options */ export declare function loadAtomistConfig(): AutomationServerOptions; /** * Examine environment, config, and cfg for Atomist workspace IDs. * The ATOMIST_WORKSPACES environment variable takes precedence over * the config "workspaceIds", which takes precedence over * cfg.workspaceId, which may be undefined, null, or an empty array. * * @param cfg current configuration, whose workspaceIds * properties may be modified by this function * @return the resolved workspace IDs */ export declare function resolveWorkspaceIds(cfg: Configuration): string[]; /** * Resolve the HTTP port from the environment and configuration. The * PORT environment variable takes precedence over the config value. */ export declare function resolvePort(cfg: Configuration): number; /** * Resolve ATOMIST_ environment variables and add them to config. * Variables of like ATOMIST_custom_foo_bar will be converted to * a json path of custom.foo.bar. * @param {Configuration} cfg */ export declare function resolveEnvironmentVariables(cfg: Configuration): void; /** * Resolve placeholders against the process.env. * Placeholders should be of form ${ENV_VAR}. Placeholders support default values * in case they aren't defined: ${ENV_VAR:default value} */ export declare function resolvePlaceholders(cfg: Configuration, replacer?: (value: string) => Promise, visited?: any[]): Promise; /** * Invoke postProcessors on the provided configuration. */ export declare function invokePostProcessors(cfg: Configuration): Promise; /** * Make sure final configuration has the minimum configuration it * needs. It will throw an error if required properties are missing. * * @param cfg final configuration */ export declare function validateConfiguration(cfg: Configuration): void; /** * Load and populate the automation configuration. The configuration * is loaded from several locations with the following precedence from * highest to lowest. * * 1. `ATOMIST_` environment variables, see [[resolveEnvironmentVariables]] * 2. Configuration returned from the post-processors. * 3. Recognized environment variables, see [[resolveWorkspaceIds]], * [[resolvePort]] * 4. The value of the ATOMIST_CONFIG environment variable, parsed as * JSON and cast to AutomationServerOptions * 5. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions * 6. The contents of the user's client.config.json as UserConfig * resolving user and per-module configuration into Configuration * 7. The automation atomist.config.js or _all_ index.js files' * configurations exported as `configuration` from those files * 8. ProductionDefaultConfiguration if ATOMIST_ENV or NODE_ENV is set * to "production" or TestingDefaultConfiguration if ATOMIST_ENV or * NODE_ENV is set to "staging" or "testing", with ATOMIST_ENV * taking precedence over NODE_ENV. * 9. LocalDefaultConfiguration * * If any of the sources are missing, they are ignored. Any truthy * configuration values specified by sources of higher precedence * cause any values provided by sources of lower precedence to be * ignored. Arrays are replaced, not merged. Typically the only * required values in the configuration for a successful registration * are the apiKey and non-empty workspaceIds. * * Placeholder of the form `${ENV_VARIABLE}` in string configuration * values will get resolved against the environment. The resolution * happens after all of the above configuration sources have been * merged. * * After all sources are merged and the resulting configuration * processed for placeholders and environment variables, the * configuration is validated using [[validateConfiguration]]. * * The configuration exported from the index.js (or atomist.config.js) * is modified to contain the final configuration values and returned * from this function. * * @param base path to file exporting the configuration object, if * not provided the package is searched for one * @return merged configuration object */ export declare function loadConfiguration(base?: string | Promise): Promise; /** * Default set of regular expressions used to remove sensitive * information from messages and logs. The entries are applied in * order, so more specific regular expressions should be placed * earlier in the list to avoid a shorter replacement preventing a * longer replacement from being applied. */ export declare const DEFAULT_REDACTION_PATTERNS: { regexp: RegExp; replacement: string; }[]; /** * Default configuration when running in neither testing or * production. */ export declare const LocalDefaultConfiguration: Configuration; /** * Configuration defaults for production environments. */ export declare const ProductionDefaultConfiguration: Partial; /** * Configuration defaults for pre-production environments. */ export declare const TestingDefaultConfiguration: Partial; //# sourceMappingURL=configuration.d.ts.map