UNPKG

@backstage/backend-plugin-api

Version:

Core API used by Backstage backend plugins

1,424 lines (1,405 loc) 74.1 kB
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