@backstage/backend-plugin-api
Version:
Core API used by Backstage backend plugins
1,424 lines (1,405 loc) • 74.1 kB
TypeScript
import { JsonObject, HumanDuration, JsonValue } from '@backstage/types';
import { Request, Response, Handler } from 'express';
import { PermissionAttributes, EvaluatorRequestOptions, PermissionEvaluator, AuthorizePermissionRequest, AuthorizePermissionResponse, QueryPermissionRequest, QueryPermissionResponse, Permission } from '@backstage/plugin-permission-common';
import { Knex } from 'knex';
import { PermissionResourceRef, PermissionRule, PermissionRuleset } from '@backstage/plugin-permission-node';
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
import { Readable } from 'stream';
export { isChildPath } from '@backstage/cli-common';
/**
* low (default): normal usage
* medium: accessing write endpoints
* high: non-root permission changes
* critical: root permission changes
* @public
*/
type AuditorServiceEventSeverityLevel = 'low' | 'medium' | 'high' | 'critical';
/** @public */
type AuditorServiceCreateEventOptions = {
/**
* Use kebab-case to name audit events (e.g., "user-login", "file-download", "fetch"). Represents a logical group of similar events or operations. For example, "fetch" could be used as an eventId encompassing various fetch methods like "by-id" or "by-location".
*
* The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.
*/
eventId: string;
/** (Optional) The severity level for the audit event. */
severityLevel?: AuditorServiceEventSeverityLevel;
/** (Optional) The associated HTTP request, if applicable. */
request?: Request<any, any, any, any, any>;
/**
* (Optional) Additional metadata relevant to the event, structured as a JSON object.
* This could include a `queryType` field, using kebab-case, for variations within the main event (e.g., "by-id", "by-user").
* For example, if the `eventId` is "fetch", the `queryType` in `meta` could be "by-id" or "by-location".
*/
meta?: JsonObject;
};
/** @public */
type AuditorServiceEvent = {
success(options?: {
meta?: JsonObject;
}): Promise<void>;
fail(options: {
meta?: JsonObject;
error: Error;
}): Promise<void>;
};
/**
* A service that provides an auditor facility.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/auditor | service documentation} for more details.
*
* @public
*/
interface AuditorService {
createEvent(options: AuditorServiceCreateEventOptions): Promise<AuditorServiceEvent>;
}
/**
* Represents a user principal (for example when a user Backstage token issued
* by the auth backend was given to a request).
*
* @remarks
*
* Additional information about the user can be fetched using the
* {@link UserInfoService}.
*
* @public
*/
type BackstageUserPrincipal = {
type: 'user';
/**
* The entity ref of the user entity that this principal represents.
*/
userEntityRef: string;
/**
* The service principal that issued the token on behalf of the user.
*
* @remarks
*
* This field is present in scenarios where a backend service acts on behalf
* of a user. It provides context about the intermediary service that
* facilitated the authentication.
*/
actor?: BackstageServicePrincipal;
};
/**
* Represents a principal that is not authenticated (for example when no token
* at all was given to a request).
*
* @public
*/
type BackstageNonePrincipal = {
type: 'none';
};
/**
* Represents a service principal (for example when an external access method
* token was given to a request, or the caller was a Backstage backend plugin).
* @public
*/
type BackstageServicePrincipal = {
type: 'service';
/**
* A string that represents the service.
*
* @remarks
*
* This string is only informational, has no well defined semantics, and
* should never be used to drive actual logic in code.
*/
subject: string;
/**
* The access restrictions that apply to this principal.
*
* @remarks
*
* If no access restrictions are provided the principal is assumed to have
* unlimited access, at a framework level. The permissions system and
* individual plugins may or may not still apply additional access controls on
* top of this.
*/
accessRestrictions?: BackstagePrincipalAccessRestrictions;
};
/**
* The access restrictions that apply to a given principal.
*
* @public
*/
type BackstagePrincipalAccessRestrictions = {
/**
* If given, the principal is limited to only performing actions with these
* named permissions.
*
* Note that this only applies where permissions checks are enabled in the
* first place. Endpoints that are not protected by the permissions system at
* all, are not affected by this setting.
*
* This array always has at least one element, or is missing entirely.
*/
permissionNames?: string[];
/**
* If given, the principal is limited to only performing actions whose
* permissions have these attributes.
*
* Note that this only applies where permissions checks are enabled in the
* first place. Endpoints that are not protected by the permissions system at
* all, are not affected by this setting.
*
* This object always has at least one key, or is missing entirely.
*/
permissionAttributes?: {
/**
* Match any of these action values. This array always has at least one
* element, or is missing entirely.
*/
action?: Array<Required<PermissionAttributes>['action']>;
};
};
/**
* An opaque representation of credentials, for example as passed in a
* request-response flow.
*
* @public
*/
type BackstageCredentials<TPrincipal = unknown> = {
$$type: '@backstage/BackstageCredentials';
/**
* If the credentials have a limited lifetime, this is the time at which they
* expire and may no longer be accepted by a receiver.
*/
expiresAt?: Date;
/**
* The principal (originator) of the request.
*
* @remarks
*
* This is semantically the originator of a request chain, and may or may not
* represent the immediate caller of your service. For example, in
* on-behalf-of scenarios, the immediate caller may be an intermediary backend
* service, but the principal may still be a user that was the original
* caller.
*/
principal: TPrincipal;
};
/**
* The types of principal that can be represented in a
* {@link BackstageCredentials} object.
*
* @public
*/
type BackstagePrincipalTypes = {
user: BackstageUserPrincipal;
service: BackstageServicePrincipal;
none: BackstageNonePrincipal;
unknown: unknown;
};
/**
* Provides token authentication and credentials management.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/auth | service documentation} for more details.
*
* @public
*/
interface AuthService {
/**
* Verifies a token and returns the associated credentials.
*/
authenticate(token: string, options?: {
/**
* If set to true, allow limited access tokens (such as cookies).
*
* If this flag is not set, or is set to false, calls with limited access
* tokens will lead to a {@link @backstage/errors#NotAllowedError} being
* thrown.
*/
allowLimitedAccess?: boolean;
}): Promise<BackstageCredentials>;
/**
* Checks if the given credentials are of the given type, and narrows the
* TypeScript type accordingly if there's a match.
*/
isPrincipal<TType extends keyof BackstagePrincipalTypes>(credentials: BackstageCredentials, type: TType): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]>;
/**
* Create a credentials object that represents an unauthenticated caller.
*/
getNoneCredentials(): Promise<BackstageCredentials<BackstageNonePrincipal>>;
/**
* Create a credentials object that represents the current service itself.
*/
getOwnServiceCredentials(): Promise<BackstageCredentials<BackstageServicePrincipal>>;
/**
* Issue a token that can be used for authenticating calls towards other
* backend plugins.
*
* @remarks
*
* This method should be called before each request. Do not hold on to the
* issued token and reuse it for future calls.
*/
getPluginRequestToken(options: {
/**
* The credentials of the originator of the request.
*
* @remarks
*
* This is most commonly the result of
* {@link AuthService.getOwnServiceCredentials} when the current service is
* the originator, or the output of {@link HttpAuthService.credentials} when
* performing requests on behalf of an incoming request identity.
*/
onBehalfOf: BackstageCredentials;
/**
* The ID of the plugin that the request is being made to.
*/
targetPluginId: string;
}): Promise<{
token: string;
}>;
/**
* Issue a limited user token that can be used e.g. in cookie flows.
*/
getLimitedUserToken(
/**
* The credentials that this token should represent. Must be a user
* principal. Commonly the output of {@link HttpAuthService.credentials} is
* used as the input.
*/
credentials: BackstageCredentials<BackstageUserPrincipal>): Promise<{
token: string;
expiresAt: Date;
}>;
/**
* Retrieve the public keys that have been used to sign tokens that were
* issued by this service. This list is periodically pruned from keys that are
* significantly past their expiry.
*/
listPublicServiceKeys(): Promise<{
keys: JsonObject[];
}>;
}
/**
* Options passed to {@link CacheService.set}.
*
* @public
*/
type CacheServiceSetOptions = {
/**
* Optional TTL (in milliseconds if given as a number). Defaults to the TTL provided when the client
* was set up (or no TTL if none are provided).
*/
ttl?: number | HumanDuration;
};
/**
* Options passed to {@link CacheService.withOptions}.
*
* @public
*/
type CacheServiceOptions = {
/**
* An optional default TTL (in milliseconds if given as a number) to be set when getting a client
* instance. If not provided, data will persist indefinitely by default (or
* can be configured per entry at set-time).
*/
defaultTtl?: number | HumanDuration;
};
/**
* A pre-configured, storage agnostic cache service suitable for use by
* Backstage plugins.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/cache | service documentation} for more details.
*
* @public
*/
interface CacheService {
/**
* Reads data from a cache store for the given key. If no data was found,
* returns undefined.
*/
get<TValue extends JsonValue>(key: string): Promise<TValue | undefined>;
/**
* Writes the given data to a cache store, associated with the given key. An
* optional TTL may also be provided, otherwise it defaults to the TTL that
* was provided when the client was instantiated.
*/
set(key: string, value: JsonValue, options?: CacheServiceSetOptions): Promise<void>;
/**
* Removes the given key from the cache store.
*/
delete(key: string): Promise<void>;
/**
* Creates a new {@link CacheService} instance with the given options.
*/
withOptions(options: CacheServiceOptions): CacheService;
}
/**
* Manages access to databases that plugins get.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/database | service documentation} for more details.
*
* @public
*/
interface DatabaseService {
/**
* getClient provides backend plugins database connections for itself.
*
* The purpose of this method is to allow plugins to get isolated data
* stores so that plugins are discouraged from database integration.
*/
getClient(): Promise<Knex>;
/**
* This property is used to control the behavior of database migrations.
*/
migrations?: {
/**
* skip database migrations. Useful if connecting to a read-only database.
*
* @defaultValue false
*/
skip?: boolean;
};
}
/**
* The DiscoveryService is used to provide a mechanism for backend
* plugins to discover the endpoints for itself or other backend plugins.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/discovery | service documentation} for more details.
*
* @remarks
*
* The purpose of the discovery API is to allow for many different deployment
* setups and routing methods through a central configuration, instead
* of letting each individual plugin manage that configuration.
*
* Implementations of the discovery API can be as simple as a URL pattern
* using the pluginId, but could also have overrides for individual plugins,
* or query a separate discovery service.
*
* @public
*/
interface DiscoveryService {
/**
* Returns the internal HTTP base URL for a given plugin, without a trailing slash.
*
* @remarks
*
* The returned URL should point to an internal endpoint for the plugin, with
* the shortest route possible. The URL should be used for service-to-service
* communication within a Backstage backend deployment.
*
* This method must always be called just before making each request, as opposed to
* fetching the URL once when constructing an API client. That is to ensure that more
* flexible routing patterns can be supported where a different result might be returned each time.
*
* For example, asking for the URL for `catalog` may return something
* like `http://10.1.2.3/api/catalog`
*/
getBaseUrl(pluginId: string): Promise<string>;
/**
* Returns the external HTTP base backend URL for a given plugin, without a trailing slash.
*
* @remarks
*
* The returned URL should point to an external endpoint for the plugin, such that
* it is reachable from the Backstage frontend and other external services. The returned
* URL should be usable for example as a callback / webhook URL.
*
* The returned URL should be stable and in general not change unless other static
* or external configuration is changed. Changes should not come as a surprise
* to an operator of the Backstage backend.
*
* For example, asking for the URL for `catalog` may return something
* like `https://backstage.example.com/api/catalog`
*/
getExternalBaseUrl(pluginId: string): Promise<string>;
}
/**
* Provides handling of credentials in an ongoing request.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/http-auth | service documentation} for more details.
*
* @public
*/
interface HttpAuthService {
/**
* Extracts the caller's credentials from a request.
*
* @remarks
*
* The credentials have been validated before returning, and are guaranteed to
* adhere to whatever policies have been added to this route using
* {@link HttpRouterService.addAuthPolicy}, if any.
*
* Further restrictions can be imposed by passing in options that control the
* allowed types of credential.
*
* You can narrow the returned credentials object to specific principal types
* using {@link AuthService.isPrincipal}.
*/
credentials<TAllowed extends keyof BackstagePrincipalTypes = 'unknown'>(
/**
* An Express request object.
*/
req: Request<any, any, any, any, any>,
/**
* Optional further restrictions.
*/
options?: {
/**
* If specified, allow only principals of the given type(s).
*
* If the incoming credentials were not of a type that matched this
* restriction, a {@link @backstage/errors#NotAllowedError} is thrown.
*
* The default is to allow user and service principals.
*/
allow?: Array<TAllowed>;
/**
* If set to true, allow limited access tokens (such as cookies).
*
* If this flag is not set, or is set to false, calls with limited access
* tokens will lead to a {@link @backstage/errors#NotAllowedError} being
* thrown.
*/
allowLimitedAccess?: boolean;
}): Promise<BackstageCredentials<BackstagePrincipalTypes[TAllowed]>>;
/**
* Issues a limited access token as a cookie on the given response object.
* This is only possible for requests that were originally made with user
* credentials (such as a Backstage token).
*
* This must be called before sending any payload data.
*/
issueUserCookie(
/**
* An Express response object.
*/
res: Response,
/**
* Optional further settings.
*/
options?: {
/**
* Issue the cookie for this specific credential. Must be a "user" type
* principal, or a "none" type (which leads to deleting the cookie).
*
* @remarks
*
* Normally you do not have to specify this option, because the default
* behavior is to extract the credentials from the request that
* corresponded to the given response.
*/
credentials?: BackstageCredentials;
}): Promise<{
expiresAt: Date;
}>;
}
/**
* Options for {@link HttpRouterService.addAuthPolicy}.
*
* @public
*/
interface HttpRouterServiceAuthPolicy {
path: string;
allow: 'unauthenticated' | 'user-cookie';
}
/**
* Allows plugins to register HTTP routes.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/http-router | service documentation} for more details.
*
* @public
*/
interface HttpRouterService {
/**
* Registers an Express request handler under the plugin's base router. This
* typically makes its base path `/api/<plugin-id>`.
*/
use(handler: Handler): void;
/**
* Adds an auth policy to the router. This is used to allow unauthenticated or
* cookie based access to parts of a plugin's API.
*
* @remarks
*
* The paths given follow the same pattern as the routers given to the `use`
* method, that is, they are relative to the plugin's base URL, and can
* contain placeholders.
*
* @example
*
* ```ts
* http.addAuthPolicy({
* path: '/static/:id',
* allow: 'user-cookie',
* });
* ```
*
* This allows limited access tokens via cookies on the
* `/api/<plugin-id>/static/*` paths, but not unauthenticated access.
*/
addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void;
}
/**
* A service that provides a logging facility.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/logger | service documentation} for more details.
*
* @public
*/
interface LoggerService {
error(message: string, meta?: Error | JsonObject): void;
warn(message: string, meta?: Error | JsonObject): void;
info(message: string, meta?: Error | JsonObject): void;
debug(message: string, meta?: Error | JsonObject): void;
child(meta: JsonObject): LoggerService;
}
/**
* @public
*/
type LifecycleServiceStartupHook = () => void | Promise<void>;
/**
* @public
*/
interface LifecycleServiceStartupOptions {
/**
* Optional {@link LoggerService} that will be used for logging instead of the default logger.
*/
logger?: LoggerService;
}
/**
* @public
*/
type LifecycleServiceShutdownHook = () => void | Promise<void>;
/**
* @public
*/
interface LifecycleServiceShutdownOptions {
/**
* Optional {@link LoggerService} that will be used for logging instead of the default logger.
*/
logger?: LoggerService;
}
/**
* Provides registration of plugin startup and shutdown lifecycle hooks.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/lifecycle | service documentation} for more details.
*
* @public
*/
interface LifecycleService {
/**
* Register a function to be called when the backend has been initialized.
*
* @remarks
*
* When used with plugin scope it will wait for the plugin itself to have been initialized.
*
* When used with root scope it will wait for all plugins to have been initialized.
*/
addStartupHook(hook: LifecycleServiceStartupHook, options?: LifecycleServiceStartupOptions): void;
/**
* Register a function to be called when the backend is shutting down.
*/
addShutdownHook(hook: LifecycleServiceShutdownHook, options?: LifecycleServiceShutdownOptions): void;
}
/**
* Options for {@link PermissionsService} requests.
*
* @public
*/
interface PermissionsServiceRequestOptions extends EvaluatorRequestOptions {
credentials: BackstageCredentials;
}
/**
* Permission system integration for authorization of user/service actions.
*
* See the {@link https://backstage.io/docs/permissions/overview | permissions documentation}
* and the {@link https://backstage.io/docs/backend-system/core-services/permissions | service documentation}
* for more details.
*
* @public
*/
interface PermissionsService extends PermissionEvaluator {
/**
* Evaluates
* {@link @backstage/plugin-permission-common#Permission | Permissions} and
* returns definitive decisions.
*
* @remarks
*
* The returned array has the same number of items, in the same order, as the
* given requests.
*/
authorize(requests: AuthorizePermissionRequest[], options: PermissionsServiceRequestOptions): Promise<AuthorizePermissionResponse[]>;
/**
* Evaluates {@link @backstage/plugin-permission-common#ResourcePermission | ResourcePermissions} and returns both definitive and
* conditional decisions, depending on the configured
* {@link @backstage/plugin-permission-node#PermissionPolicy}.
*
* @remarks
*
* This method is useful when the
* caller needs more control over the processing of conditional decisions. For example, a plugin
* backend may want to use {@link @backstage/plugin-permission-common#PermissionCriteria | conditions} in a database query instead of
* evaluating each resource in memory.
*
* The returned array has the same number of items, in the same order, as the
* given requests.
*/
authorizeConditional(requests: QueryPermissionRequest[], options: PermissionsServiceRequestOptions): Promise<QueryPermissionResponse[]>;
}
/**
* Prevent use of type parameter from contributing to type inference.
*
* https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795
* @ignore
*/
type NoInfer<T> = T extends infer S ? S : never;
/**
* Options for adding a resource type to the permission system.
*
* @public
*/
type PermissionsRegistryServiceAddResourceTypeOptions<TResourceType extends string, TResource, TQuery> = {
/**
* The {@link @backstage/plugin-permission-node#PermissionResourceRef} that identifies the resource type.
*/
resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>;
/**
* Permissions that are available for this resource type.
*/
permissions?: Array<Permission>;
/**
* Permission rules that are available for this resource type.
*/
rules: PermissionRule<NoInfer<TResource>, NoInfer<TQuery>, NoInfer<TResourceType>>[];
/**
* The function used to load associated resources based in the provided
* references.
*
* @remarks
*
* If this function is not provided the permission system will not be able to
* resolve conditional decisions except when requesting resources directly
* from the plugin.
*/
getResources?(resourceRefs: string[]): Promise<Array<NoInfer<TResource> | undefined>>;
};
/**
* Permission system integration for registering resources and permissions.
*
* See the {@link https://backstage.io/docs/permissions/overview | permissions documentation}
* and the {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | service documentation}
* for more details.
*
* @public
*/
interface PermissionsRegistryService {
/**
* Add permissions for this plugin to the permission system.
*/
addPermissions(permissions: Permission[]): void;
/**
* Adds a set of permission rules to the permission system for a resource type
* that is owned by this plugin.
*
* @remarks
*
* Rules should be created using corresponding `create*PermissionRule`
* functions exported by plugins, who in turn are created with
* `makeCreatePermissionRule`.
*
* Rules can be added either directly by the plugin itself or through a plugin
* module.
*/
addPermissionRules(rules: PermissionRule<any, any, string>[]): void;
/**
* Add a new resource type that is owned by this plugin to the permission
* system.
*
* @remarks
*
* To make this concrete, we can use the Backstage software catalog as an
* example. The catalog has conditional rules around access to specific
* _entities_ in the catalog. The _type_ of resource is captured here as
* `resourceType`, a string identifier (`catalog-entity` in this example) that
* can be provided with permission definitions. This is merely a _type_ to
* verify that conditions in an authorization policy are constructed
* correctly, not a reference to a specific resource.
*
* The `rules` parameter is an array of
* {@link @backstage/plugin-permission-node#PermissionRule}s that introduce
* conditional filtering logic for resources; for the catalog, these are
* things like `isEntityOwner` or `hasAnnotation`. Rules describe how to
* filter a list of resources, and the `conditions` returned allow these rules
* to be applied with specific parameters (such as 'group:default/team-a', or
* 'backstage.io/edit-url').
*
* The `getResources` argument should load resources based on a reference
* identifier. For the catalog, this is an
* [entity reference](https://backstage.io/docs/features/software-catalog/references#string-references).
* For other plugins, this can be any serialized format. This is used to add a
* permissions registry API via the HTTP router service. This API will be
* called by the `permission-backend` when authorization conditions relating
* to this plugin need to be evaluated.
*/
addResourceType<const TResourceType extends string, TResource, TQuery>(options: PermissionsRegistryServiceAddResourceTypeOptions<TResourceType, TResource, TQuery>): void;
/**
* Returns the set of registered rules for this resource.
*
* @remarks
*
* Primarily intended for use with {@link @backstage/plugin-permission-node#createConditionAuthorizer} and {@link @backstage/plugin-permission-node#createConditionTransformer}.
*/
getPermissionRuleset<TResourceType extends string, TResource, TQuery>(resourceRef: PermissionResourceRef<TResource, TQuery, TResourceType>): PermissionRuleset<TResource, TQuery, TResourceType>;
}
/**
* Access metadata about the current plugin.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/plugin-metadata | service documentation} for more details.
*
* @public
*/
interface PluginMetadataService {
/**
* The ID of the current plugin.
*/
getId(): string;
}
/**
* Provides access to static configuration.
*
* See the {@link https://backstage.io/docs/conf/ | configuration documentation}
* and the {@link https://backstage.io/docs/backend-system/core-services/root-config | service documentation}
* for more details.
*
* @public
*/
interface RootConfigService extends Config {
}
/**
* @public
*/
interface RootHealthService {
/**
* Get the liveness status of the backend.
*/
getLiveness(): Promise<{
status: number;
payload?: JsonValue;
}>;
/**
* Get the readiness status of the backend.
*/
getReadiness(): Promise<{
status: number;
payload?: JsonValue;
}>;
}
/**
* HTTP route registration for root services.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/root-http-router | service documentation} for more details.
*
* @public
*/
interface RootHttpRouterService {
/**
* Registers a handler at the root of the backend router.
* The path is required and may not be empty.
*/
use(path: string, handler: Handler): void;
}
/**
* Registration of backend startup and shutdown lifecycle hooks.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/root-lifecycle | service documentation} for more details.
*
* @public
*/
interface RootLifecycleService extends LifecycleService {
addBeforeShutdownHook(hook: () => void | Promise<void>): void;
}
/**
* Root-level logging.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/root-logger | service documentation} for more details.
*
* @public
*/
interface RootLoggerService extends LoggerService {
}
/**
* A function that can be called as a scheduled task.
*
* It may optionally accept an abort signal argument. When the signal triggers,
* processing should abort and return as quickly as possible.
*
* @public
*/
type SchedulerServiceTaskFunction = ((abortSignal: AbortSignal) => void | Promise<void>) | (() => void | Promise<void>);
/**
* A semi-opaque type to describe an actively scheduled task.
*
* @public
*/
type SchedulerServiceTaskDescriptor = {
/**
* The unique identifier of the task.
*/
id: string;
/**
* The scope of the task.
*/
scope: 'global' | 'local';
/**
* The settings that control the task flow. This is a semi-opaque structure
* that is mainly there for debugging purposes. Do not make any assumptions
* about the contents of this field.
*/
settings: {
version: number;
} & JsonObject;
};
/**
* Options that control the scheduling of a task.
*
* @public
*/
interface SchedulerServiceTaskScheduleDefinition {
/**
* How often you want the task to run. The system does its best to avoid
* overlapping invocations.
*
* @remarks
*
* This is the best effort value; under some circumstances there can be
* deviations. For example, if the task runtime is longer than the frequency
* and the timeout has not been given or not been exceeded yet, the next
* invocation of this task will be delayed until after the previous one
* finishes.
*
* This is a required field.
*/
frequency: {
/**
* A crontab style string.
*
* @remarks
*
* Overview:
*
* ```
* ┌────────────── second (0-60, optional)
* │ ┌──────────── minute (0-59)
* │ │ ┌────────── hour (0-23)
* │ │ │ ┌──────── day of month (1-31)
* │ │ │ │ ┌────── month (1-12)
* │ │ │ │ │ ┌──── day of week (0-6, 0 is Sunday)
* │ │ │ │ │ │
* * * * * * *
* ```
*/
cron: string;
} | Duration | HumanDuration | {
trigger: 'manual';
};
/**
* The maximum amount of time that a single task invocation can take, before
* it's considered timed out and gets "released" such that a new invocation
* is permitted to take place (possibly, then, on a different worker).
*/
timeout: Duration | HumanDuration;
/**
* The amount of time that should pass before the first invocation happens.
*
* @remarks
*
* This can be useful in cold start scenarios to stagger or delay some heavy
* compute jobs. If no value is given for this field then the first invocation
* will happen as soon as possible according to the cadence.
*
* NOTE: This is a per-worker delay. If you have a cluster of workers all
* collaborating on a task that has its `scope` field set to `'global'`, then
* you may still see the task being processed by other long-lived workers,
* while any given single worker is in its initial sleep delay time e.g. after
* a deployment. Therefore, this parameter is not useful for "globally" pausing
* work; its main intended use is for individual machines to get a chance to
* reach some equilibrium at startup before triggering heavy batch workloads.
*/
initialDelay?: Duration | HumanDuration;
/**
* Sets the scope of concurrency control / locking to apply for invocations of
* this task.
*
* @remarks
*
* When the scope is set to the default value `'global'`, the scheduler will
* attempt to ensure that only one worker machine runs the task at a time,
* according to the given cadence. This means that as the number of worker
* hosts increases, the invocation frequency of this task will not go up.
* Instead, the load is spread randomly across hosts. This setting is useful
* for tasks that access shared resources, for example catalog ingestion tasks
* where you do not want many machines to repeatedly import the same data and
* trample over each other.
*
* When the scope is set to `'local'`, there is no concurrency control across
* hosts. Each host runs the task according to the given cadence similarly to
* `setInterval`, but the runtime ensures that there are no overlapping runs.
*
* @defaultValue 'global'
*/
scope?: 'global' | 'local';
}
/**
* Config options for {@link SchedulerServiceTaskScheduleDefinition}
* that control the scheduling of a task.
*
* @public
*/
interface SchedulerServiceTaskScheduleDefinitionConfig {
/**
* How often you want the task to run. The system does its best to avoid
* overlapping invocations.
*
* @remarks
*
* This is the best effort value; under some circumstances there can be
* deviations. For example, if the task runtime is longer than the frequency
* and the timeout has not been given or not been exceeded yet, the next
* invocation of this task will be delayed until after the previous one
* finishes.
*
* This is a required field.
*/
frequency: {
/**
* A crontab style string.
*
* @remarks
*
* Overview:
*
* ```
* ┌────────────── second (0-60, optional)
* │ ┌──────────── minute (0-59)
* │ │ ┌────────── hour (0-23)
* │ │ │ ┌──────── day of month (1-31)
* │ │ │ │ ┌────── month (1-12)
* │ │ │ │ │ ┌──── day of week (0-6, 0 is Sunday)
* │ │ │ │ │ │
* * * * * * *
* ```
*/
cron: string;
} | string | HumanDuration
/**
* This task will only run when manually triggered with the `triggerTask` method; no automatic
* scheduling. This is useful for locking of global tasks that should not be run concurrently.
*/
| {
trigger: 'manual';
};
/**
* The maximum amount of time that a single task invocation can take, before
* it's considered timed out and gets "released" such that a new invocation
* is permitted to take place (possibly, then, on a different worker).
*/
timeout: string | HumanDuration;
/**
* The amount of time that should pass before the first invocation happens.
*
* @remarks
*
* This can be useful in cold start scenarios to stagger or delay some heavy
* compute jobs. If no value is given for this field then the first invocation
* will happen as soon as possible according to the cadence.
*
* NOTE: This is a per-worker delay. If you have a cluster of workers all
* collaborating on a task that has its `scope` field set to `'global'`, then
* you may still see the task being processed by other long-lived workers,
* while any given single worker is in its initial sleep delay time e.g. after
* a deployment. Therefore, this parameter is not useful for "globally" pausing
* work; its main intended use is for individual machines to get a chance to
* reach some equilibrium at startup before triggering heavy batch workloads.
*/
initialDelay?: string | HumanDuration;
/**
* Sets the scope of concurrency control / locking to apply for invocations of
* this task.
*
* @remarks
*
* When the scope is set to the default value `'global'`, the scheduler will
* attempt to ensure that only one worker machine runs the task at a time,
* according to the given cadence. This means that as the number of worker
* hosts increases, the invocation frequency of this task will not go up.
* Instead, the load is spread randomly across hosts. This setting is useful
* for tasks that access shared resources, for example catalog ingestion tasks
* where you do not want many machines to repeatedly import the same data and
* trample over each other.
*
* When the scope is set to `'local'`, there is no concurrency control across
* hosts. Each host runs the task according to the given cadence similarly to
* `setInterval`, but the runtime ensures that there are no overlapping runs.
*
* @defaultValue 'global'
*/
scope?: 'global' | 'local';
}
/**
* Options that apply to the invocation of a given task.
*
* @public
*/
interface SchedulerServiceTaskInvocationDefinition {
/**
* A unique ID (within the scope of the plugin) for the task.
*/
id: string;
/**
* The actual task function to be invoked regularly.
*/
fn: SchedulerServiceTaskFunction;
/**
* An abort signal that, when triggered, will stop the recurring execution of
* the task.
*/
signal?: AbortSignal;
}
/**
* A previously prepared task schedule, ready to be invoked.
*
* @public
*/
interface SchedulerServiceTaskRunner {
/**
* Takes the schedule and executes an actual task using it.
*
* @param task - The actual runtime properties of the task
*/
run(task: SchedulerServiceTaskInvocationDefinition): Promise<void>;
}
/**
* Deals with the scheduling of distributed tasks, for a given plugin.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/scheduler | service documentation} for more details.
*
* @public
*/
interface SchedulerService {
/**
* Manually triggers a task by ID.
*
* If the task doesn't exist, a NotFoundError is thrown. If the task is
* currently running, a ConflictError is thrown.
*
* @param id - The task ID
*/
triggerTask(id: string): Promise<void>;
/**
* Schedules a task function for recurring runs.
*
* @remarks
*
* The `scope` task field controls whether to use coordinated exclusive
* invocation across workers, or to just coordinate within the current worker.
*
* This convenience method performs both the scheduling and invocation in one
* go.
*
* @param task - The task definition
*/
scheduleTask(task: SchedulerServiceTaskScheduleDefinition & SchedulerServiceTaskInvocationDefinition): Promise<void>;
/**
* Creates a scheduled but dormant recurring task, ready to be launched at a
* later time.
*
* @remarks
*
* This method is useful for pre-creating a schedule in outer code to be
* passed into an inner implementation, such that the outer code controls
* scheduling while inner code controls implementation.
*
* @param schedule - The task schedule
*/
createScheduledTaskRunner(schedule: SchedulerServiceTaskScheduleDefinition): SchedulerServiceTaskRunner;
/**
* Returns all scheduled tasks registered to this scheduler.
*
* @remarks
*
* This method is useful for triggering tasks manually using the triggerTask
* functionality. Note that the returned tasks contain only tasks that have
* been initialized in this instance of the scheduler.
*
* @returns Scheduled tasks
*/
getScheduledTasks(): Promise<SchedulerServiceTaskDescriptor[]>;
}
/**
* Reads a {@link SchedulerServiceTaskScheduleDefinition} from config. Expects
* the config not to be the root config, but the config for the definition.
*
* @param config - config for a TaskScheduleDefinition.
* @public
*/
declare function readSchedulerServiceTaskScheduleDefinitionFromConfig(config: Config): SchedulerServiceTaskScheduleDefinition;
/**
* A generic interface for fetching plain data from URLs.
*
* See the {@link https://backstage.io/docs/backend-system/core-services/url-reader | service documentation} for more details.
*
* @public
*/
interface UrlReaderService {
/**
* Reads a single file and return its content.
*/
readUrl(url: string, options?: UrlReaderServiceReadUrlOptions): Promise<UrlReaderServiceReadUrlResponse>;
/**
* Reads a full or partial file tree.
*/
readTree(url: string, options?: UrlReaderServiceReadTreeOptions): Promise<UrlReaderServiceReadTreeResponse>;
/**
* Searches for a file in a tree using a glob pattern.
*/
search(url: string, options?: UrlReaderServiceSearchOptions): Promise<UrlReaderServiceSearchResponse>;
}
/**
* An options object for readUrl operations.
*
* @public
*/
type UrlReaderServiceReadUrlOptions = {
/**
* An ETag which can be provided to check whether a
* {@link UrlReaderService.readUrl} response has changed from a previous execution.
*
* @remarks
*
* In the {@link UrlReaderService.readUrl} response, an ETag is returned along with
* the data. The ETag is a unique identifier of the data, usually the commit
* SHA or ETag from the target.
*
* When an ETag is given in ReadUrlOptions, {@link UrlReaderService.readUrl} will
* first compare the ETag against the ETag of the target. If they match,
* {@link UrlReaderService.readUrl} will throw a
* {@link @backstage/errors#NotModifiedError} indicating that the response
* will not differ from the previous response which included this particular
* ETag. If they do not match, {@link UrlReaderService.readUrl} will return the rest
* of the response along with a new ETag.
*/
etag?: string;
/**
* A date which can be provided to check whether a
* {@link UrlReaderService.readUrl} response has changed since the lastModifiedAt.
*
* @remarks
*
* In the {@link UrlReaderService.readUrl} response, an lastModifiedAt is returned
* along with data. The lastModifiedAt date represents the last time the data
* was modified.
*
* When an lastModifiedAfter is given in ReadUrlOptions, {@link UrlReaderService.readUrl}
* will compare the lastModifiedAfter against the lastModifiedAt of the target. If
* the data has not been modified since this date, the {@link UrlReaderService.readUrl}
* will throw a {@link @backstage/errors#NotModifiedError} indicating that the
* response does not contain any new data. If they do not match,
* {@link UrlReaderService.readUrl} will return the rest of the response along with new
* lastModifiedAt date.
*/
lastModifiedAfter?: Date;
/**
* An abort signal to pass down to the underlying request.
*
* @remarks
*
* Not all reader implementations may take this field into account.
*/
signal?: AbortSignal;
/**
* An optional token to use for authentication when reading the resources.
*
* @remarks
*
* By default all URL Readers will use the integrations config which is supplied
* when creating the Readers. Sometimes it might be desirable to use the already
* created URLReaders but with a different token, maybe that's supplied by the user
* at runtime.
*/
token?: string;
};
/**
* A response object for {@link UrlReaderService.readUrl} operations.
*
* @public
*/
type UrlReaderServiceReadUrlResponse = {
/**
* Returns the data that was read from the remote URL.
*/
buffer(): Promise<Buffer>;
/**
* Returns the data that was read from the remote URL as a Readable stream.
*
* @remarks
*
* This method will be required in a future release.
*/
stream?(): Readable;
/**
* Etag returned by content provider.
*
* @remarks
*
* Can be used to compare and cache responses when doing subsequent calls.
*/
etag?: string;
/**
* Last modified date of the file contents.
*/
lastModifiedAt?: Date;
};
/**
* An options object for {@link UrlReaderService.readTree} operations.
*
* @public
*/
type UrlReaderServiceReadTreeOptions = {
/**
* A filter that can be used to select which files should be included.
*
* @remarks
*
* The path passed to the filter function is the relative path from the URL
* that the file tree is fetched from, without any leading '/'.
*
* For example, given the URL https://github.com/my/repo/tree/master/my-dir, a file
* at https://github.com/my/repo/blob/master/my-dir/my-subdir/my-file.txt will
* be represented as my-subdir/my-file.txt
*
* If no filter is provided, all files are extracted.
*/
filter?(path: string, info?: {
size: number;
}): boolean;
/**
* An ETag which can be provided to check whether a
* {@link UrlReaderService.readTree} response has changed from a previous execution.
*
* @remarks
*
* In the {@link UrlReaderService.readTree} response, an ETag is returned along with
* the tree blob. The ETag is a unique identifier of the tree blob, usually
* the commit SHA or ETag from the target.
*
* When an ETag is given as a request option, {@link UrlReaderService.readTree} will
* first compare the ETag against the ETag on the target branch. If they
* match, {@link UrlReaderService.readTree} will throw a
* {@link @backstage/errors#NotModifiedError} indicating that the response
* will not differ from the previous response which included this particular
* ETag. If they do not match, {@link UrlReaderService.readTree} will return the
* rest of the response along with a new ETag.
*/
etag?: string;
/**
* An abort signal to pass down to the underlying request.
*
* @remarks
*
* Not all reader implementations may take this field into account.
*/
signal?: AbortSignal;
/**
* An optional token to use for authentication when reading the resources.
*
* @remarks
*
* By default all URL Readers will use the integrations config which is supplied
* when creating the Readers. Sometimes it might be desirable to use the already
* created URLReaders but with a different token, maybe that's supplied by the user
* at runtime.
*/
token?: string;
};
/**
* Options that control {@link UrlReaderServiceReadTreeResponse.dir} execution.
*
* @public
*/
type UrlReaderServiceReadTreeResponseDirOptions = {
/**
* The directory to write files to.
*
* @remarks
*
* Defaults to the OS tmpdir, or `backend.workingDirectory` if set in config.
*/
targetDir?: string;
};
/**
* A response object for {@link UrlReaderService.readTree} operations.
*
* @public
*/
type UrlReaderServiceReadTreeResponse = {
/**
* Returns an array of all the files inside the tree, and corresponding
* functions to read their content.
*/
files(): Promise<UrlReaderServiceReadTreeResponseFile[]>;
/**
* Returns the tree contents as a binary archive, using a stream.
*/
archive(): Promise<NodeJS.ReadableStream>;
/**
* Extracts the tree response into a directory and returns the path of the
* directory.
*
* **NOTE**: It is the responsibility of the caller to remove the directory after use.
*/
dir(options?: UrlReaderServiceReadTreeResponseDirOptions): Promise<string>;
/**
* Etag returned by content provider.
*
* @remarks
*
* Can be used to compare and cache responses when doing subsequent calls.
*/
etag: string;
};
/**
* Represents a single file in a {@link UrlReaderService.readTree} response.
*
* @public
*/
type UrlReaderServiceReadTreeResponseFile = {
/**
* The filepath of the data.
*/
path: string;
/**
* The binary contents of the file.
*/
content(): Promise<Buffer>;
/**
* The last modified timestamp of the data.
*/
lastModifiedAt?: Date;
};
/**
* An options object for search operations.
*
* @public
*/
type UrlReaderServiceSearchOptions = {
/**
* An etag can be provided to check whether the search response has changed from a previous execution.
*
* In the search() response, an etag is returned along with the files. The etag is a unique identifier
* of the current tree, usually the commit SHA or etag from the target.
*
* When an etag is given in SearchOptions, search will first compare the etag against the etag
* on the target branch. If they match, search will throw a NotModifiedError indicating that the search
* response will not differ from the previous response which included this particular etag. If they mismatch,
* search will return the rest of SearchResponse along with a new