@atomist/automation-client
Version:
Atomist API for software low-level client
469 lines • 18.7 kB
TypeScript
import * as exp from "express";
import { HandleCommand } from "./HandleCommand";
import { HandleEvent } from "./HandleEvent";
import { ExpressServerOptions } from "./internal/transport/express/ExpressServer";
import { AutomationEventListener } from "./server/AutomationEventListener";
import { AutomationMetadataProcessor } from "./spi/env/MetadataProcessor";
import { SecretResolver } from "./spi/env/SecretResolver";
import { HttpClientFactory } from "./spi/http/httpClient";
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<T = AnyOptions> = (configuration: Configuration) => Promise<Configuration & T>;
/**
* 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 */
[]: 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.
* If apiKey is specified it will be used over a provided token.
*/
apiKey?: string;
/** HTTP configuration, useful for health checks */
http?: {
enabled?: boolean;
client?: {
factory?: HttpClientFactory;
};
} & Partial<ExpressServerOptions>;
/** websocket configuration */
ws?: {
enabled?: boolean;
termination?: {
/**
* if true, give in-flight transactions `gracePeriod`
* milliseconds to complete when shutting down
*/
graceful?: boolean;
/** grace period in millisends */
gracePeriod?: number;
};
/** compress messages over websocket */
compress?: boolean;
/** timeout in milliseconds */
timeout?: number;
};
/** Atomist API endpoints */
endpoints?: {
graphql?: string;
api?: 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[];
}
/**
* 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;
};
/** Logging configuration */
logging?: {
/** Log level, default is "info" */
level?: "debug" | "info" | "warn" | "error";
/**
* 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?: 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;
};
/** 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<Maker<HandleCommand>>;
/**
* 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<Maker<HandleEvent>>;
/** 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<T>(path: string, defaultValue?: T): T;
/**
* Return user automation client configuration path.
*/
export declare function userConfigPath(): string;
/**
* Write user config securely, creating directories as necessary.
*/
export declare function writeUserConfig(cfg: UserConfig): Promise<void>;
/**
* 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;
/**
* 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
*/
export declare function loadAutomationConfig(cfgPath?: string): Configuration;
/**
* 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 configuration "workspaceIds", which takes precedence over
* cfg.workspaceId, which may be undefined, null, or an empty array.
* If the ATOMIST_WORKSPACES environment variable is not set,
* workspaceIds is not set in config, and workspaceIds is falsey in
* cfg and teamIds is resolvable from the configuration, workspaceIds
* is set to teamIds.
*
* @param cfg current configuration, whose workspaceIds and teamIds
* properties may be modified by this function
* @return the resolved workspace IDs
*/
export declare function resolveWorkspaceIds(cfg: Configuration): string[];
/**
* Resolve a value from a environment variables or configuration keys.
* The environment variables are checked in order and take precedence
* over the configuration key, which are also checked in order. If
* no truthy values are found, undefined is returned.
*
* @param environmentVariables environment variables to check
* @param configKeyPaths configuration keys, as JSON paths, to check
* @param defaultValue value to use if no environment variables or config keys have values
* @return first truthy value found, or defaultValue
*/
export declare function resolveConfigurationValue(environmentVariables: string[], configKeyPaths: string[], defaultValue?: string): 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}
* @param {Configuration} config
*/
export declare function resolvePlaceholders(cfg: Configuration): void;
/**
* Invoke postProcessors on the provided configuration.
*/
export declare function invokePostProcessors(cfg: Configuration): Promise<Configuration>;
/**
* 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.
*
* 0. Recognized environment variables (see below)
* 1. The value of the ATOMIST_CONFIG environment variable, parsed as
* JSON and cast to AutomationServerOptions
* 2. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions
* 3. The automation's atomist.config.js exported configuration as
* Configuration
* 4. The contents of the user's client.config.json as UserConfig
* resolving user and per-module configuration into Configuration
* 5. 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.
* 6. 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 or token and non-empty workspaceIds.
*
* Placeholder of the form `${ENV_VARIABLE}` in string configuration
* values will get resolved against the environment. The resolution
* happens at the very end when all configs have been merged.
*
* The configuration exported from the atomist.config.js is modified
* to contain the final configuration values and returned from this
* function.
*
* @param cfgPath path to file exporting the configuration object, if
* not provided the package is searched for one
* @return merged configuration object
*/
export declare function loadConfiguration(cfgPath?: string): Promise<Configuration>;
/**
* 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>;
/**
* Configuration defaults for pre-production environments.
*/
export declare const TestingDefaultConfiguration: Partial<Configuration>;
//# sourceMappingURL=configuration.d.ts.map