arrange in backend-plugin-api and backend-defaults

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-04-25 16:06:57 +02:00
parent 736bc3c243
commit 906705b77b
44 changed files with 2938 additions and 230 deletions
@@ -1,23 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type SchedulerService as SchedulerService_ } from '@backstage/backend-plugin-api/scheduler';
/**
* @public
* @deprecated import from `@backstage/backend-plugin-api/scheduler` instead
*/
export type SchedulerService = SchedulerService_;
@@ -1,26 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { readTaskScheduleDefinitionFromConfig } from './readTaskScheduleDefinitionFromConfig';
export type {
SchedulerService,
TaskDescriptor,
TaskFunction,
TaskInvocationDefinition,
TaskRunner,
TaskScheduleDefinition,
TaskScheduleDefinitionConfig,
} from './types';
@@ -1,78 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Config, readDurationFromConfig } from '@backstage/config';
import { HumanDuration } from '@backstage/types';
import { TaskScheduleDefinition } from './types';
import { Duration } from 'luxon';
function readDuration(config: Config, key: string): Duration | HumanDuration {
if (typeof config.get(key) === 'string') {
const value = config.getString(key);
const duration = Duration.fromISO(value);
if (!duration.isValid) {
throw new Error(`Invalid duration: ${value}`);
}
return duration;
}
return readDurationFromConfig(config, { key });
}
function readCronOrDuration(
config: Config,
key: string,
): { cron: string } | Duration | HumanDuration {
const value = config.get(key);
if (typeof value === 'object' && (value as { cron?: string }).cron) {
return value as { cron: string };
}
return readDuration(config, key);
}
/**
* Reads a TaskScheduleDefinition from a Config.
* Expects the config not to be the root config,
* but the config for the definition.
*
* @param config - config for a TaskScheduleDefinition.
* @public
*/
export function readTaskScheduleDefinitionFromConfig(
config: Config,
): TaskScheduleDefinition {
const frequency = readCronOrDuration(config, 'frequency');
const timeout = readDuration(config, 'timeout');
const initialDelay = config.has('initialDelay')
? readDuration(config, 'initialDelay')
: undefined;
const scope = config.getOptionalString('scope');
if (scope && !['global', 'local'].includes(scope)) {
throw new Error(
`Only "global" or "local" are allowed for TaskScheduleDefinition.scope, but got: ${scope}`,
);
}
return {
frequency,
timeout,
initialDelay,
scope: scope as 'global' | 'local' | undefined,
};
}
-1
View File
@@ -24,4 +24,3 @@ export * from './services';
export type { BackendFeature } from './types';
export * from './paths';
export * from './wiring';
export * from './deprecated';
@@ -17,9 +17,9 @@
import { ConfigReader } from '@backstage/config';
import { HumanDuration } from '@backstage/types';
import { Duration } from 'luxon';
import { readTaskScheduleDefinitionFromConfig } from './readTaskScheduleDefinitionFromConfig';
import { readSchedulerServiceTaskScheduleDefinitionFromConfig } from './SchedulerService';
describe('readTaskScheduleDefinitionFromConfig', () => {
describe('readSchedulerServiceTaskScheduleDefinitionFromConfig', () => {
it('all valid values', () => {
const config = new ConfigReader({
frequency: {
@@ -32,7 +32,7 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
scope: 'global',
});
const result = readTaskScheduleDefinitionFromConfig(config);
const result = readSchedulerServiceTaskScheduleDefinitionFromConfig(config);
expect((result.frequency as { cron: string }).cron).toBe('0 30 * * * *');
expect(result.timeout).toEqual(Duration.fromISO('PT3M'));
@@ -48,7 +48,7 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
timeout: 'PT3M',
});
const result = readTaskScheduleDefinitionFromConfig(config);
const result = readSchedulerServiceTaskScheduleDefinitionFromConfig(config);
expect((result.frequency as { cron: string }).cron).toBe('0 30 * * * *');
expect(result.timeout).toEqual(Duration.fromISO('PT3M'));
@@ -61,9 +61,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
timeout: 'PT3M',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
"Missing required config value at 'frequency'",
);
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow("Missing required config value at 'frequency'");
});
it('fail without required timeout', () => {
@@ -71,9 +71,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
frequency: 'PT30M',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
"Missing required config value at 'timeout'",
);
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow("Missing required config value at 'timeout'");
});
it('invalid frequency key', () => {
@@ -84,7 +84,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
timeout: 'PT3M',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow(
"Failed to read duration from config at 'frequency', Error: Needs one or more of 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'",
);
});
@@ -97,7 +99,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
timeout: 'PT3M',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow(
"Failed to read duration from config, Error: Unable to convert config value for key 'frequency.minutes' in 'mock-config' to a number",
);
});
@@ -111,7 +115,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
timeout: 'PT3M',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow(
"Failed to read duration from config at 'frequency', Error: Unknown property 'invalid'; expected one or more of 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'",
);
});
@@ -125,7 +131,9 @@ describe('readTaskScheduleDefinitionFromConfig', () => {
scope: 'invalid',
});
expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow(
expect(() =>
readSchedulerServiceTaskScheduleDefinitionFromConfig(config),
).toThrow(
'Only "global" or "local" are allowed for TaskScheduleDefinition.scope, but got: invalid',
);
});
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { Config, readDurationFromConfig } from '@backstage/config';
import { HumanDuration, JsonObject } from '@backstage/types';
import { Duration } from 'luxon';
@@ -25,7 +26,7 @@ import { Duration } from 'luxon';
*
* @public
*/
export type TaskFunction =
export type SchedulerServiceTaskFunction =
| ((abortSignal: AbortSignal) => void | Promise<void>)
| (() => void | Promise<void>);
@@ -34,7 +35,7 @@ export type TaskFunction =
*
* @public
*/
export type TaskDescriptor = {
export type SchedulerServiceTaskDescriptor = {
/**
* The unique identifier of the task.
*/
@@ -56,7 +57,7 @@ export type TaskDescriptor = {
*
* @public
*/
export interface TaskScheduleDefinition {
export interface SchedulerServiceTaskScheduleDefinition {
/**
* How often you want the task to run. The system does its best to avoid
* overlapping invocations.
@@ -148,12 +149,12 @@ export interface TaskScheduleDefinition {
}
/**
* Config options for {@link TaskScheduleDefinition}
* Config options for {@link SchedulerServiceTaskScheduleDefinition}
* that control the scheduling of a task.
*
* @public
*/
export interface TaskScheduleDefinitionConfig {
export interface SchedulerServiceTaskScheduleDefinitionConfig {
/**
* How often you want the task to run. The system does its best to avoid
* overlapping invocations.
@@ -249,7 +250,7 @@ export interface TaskScheduleDefinitionConfig {
*
* @public
*/
export interface TaskInvocationDefinition {
export interface SchedulerServiceTaskInvocationDefinition {
/**
* A unique ID (within the scope of the plugin) for the task.
*/
@@ -258,7 +259,7 @@ export interface TaskInvocationDefinition {
/**
* The actual task function to be invoked regularly.
*/
fn: TaskFunction;
fn: SchedulerServiceTaskFunction;
/**
* An abort signal that, when triggered, will stop the recurring execution of
@@ -272,13 +273,13 @@ export interface TaskInvocationDefinition {
*
* @public
*/
export interface TaskRunner {
export interface SchedulerServiceTaskRunner {
/**
* Takes the schedule and executes an actual task using it.
*
* @param task - The actual runtime properties of the task
*/
run(task: TaskInvocationDefinition): Promise<void>;
run(task: SchedulerServiceTaskInvocationDefinition): Promise<void>;
}
/**
@@ -311,7 +312,8 @@ export interface SchedulerService {
* @param task - The task definition
*/
scheduleTask(
task: TaskScheduleDefinition & TaskInvocationDefinition,
task: SchedulerServiceTaskScheduleDefinition &
SchedulerServiceTaskInvocationDefinition,
): Promise<void>;
/**
@@ -326,7 +328,9 @@ export interface SchedulerService {
*
* @param schedule - The task schedule
*/
createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner;
createScheduledTaskRunner(
schedule: SchedulerServiceTaskScheduleDefinition,
): SchedulerServiceTaskRunner;
/**
* Returns all scheduled tasks registered to this scheduler.
@@ -339,5 +343,62 @@ export interface SchedulerService {
*
* @returns Scheduled tasks
*/
getScheduledTasks(): Promise<TaskDescriptor[]>;
getScheduledTasks(): Promise<SchedulerServiceTaskDescriptor[]>;
}
function readDuration(config: Config, key: string): Duration | HumanDuration {
if (typeof config.get(key) === 'string') {
const value = config.getString(key);
const duration = Duration.fromISO(value);
if (!duration.isValid) {
throw new Error(`Invalid duration: ${value}`);
}
return duration;
}
return readDurationFromConfig(config, { key });
}
function readCronOrDuration(
config: Config,
key: string,
): { cron: string } | Duration | HumanDuration {
const value = config.get(key);
if (typeof value === 'object' && (value as { cron?: string }).cron) {
return value as { cron: string };
}
return readDuration(config, key);
}
/**
* 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
*/
export function readSchedulerServiceTaskScheduleDefinitionFromConfig(
config: Config,
): SchedulerServiceTaskScheduleDefinition {
const frequency = readCronOrDuration(config, 'frequency');
const timeout = readDuration(config, 'timeout');
const initialDelay = config.has('initialDelay')
? readDuration(config, 'initialDelay')
: undefined;
const scope = config.getOptionalString('scope');
if (scope && !['global', 'local'].includes(scope)) {
throw new Error(
`Only "global" or "local" are allowed for TaskScheduleDefinition.scope, but got: ${scope}`,
);
}
return {
frequency,
timeout,
initialDelay,
scope: scope as 'global' | 'local' | undefined,
};
}
@@ -165,7 +165,7 @@ export namespace coreServices {
* @public
*/
export const scheduler = createServiceRef<
import('@backstage/backend-plugin-api/scheduler').SchedulerService
import('./SchedulerService').SchedulerService
>({ id: 'core.scheduler' });
/**
@@ -52,6 +52,16 @@ export type { PluginMetadataService } from './PluginMetadataService';
export type { RootHttpRouterService } from './RootHttpRouterService';
export type { RootLifecycleService } from './RootLifecycleService';
export type { RootLoggerService } from './RootLoggerService';
export { readSchedulerServiceTaskScheduleDefinitionFromConfig } from './SchedulerService';
export type {
SchedulerService,
SchedulerServiceTaskDescriptor,
SchedulerServiceTaskFunction,
SchedulerServiceTaskInvocationDefinition,
SchedulerServiceTaskRunner,
SchedulerServiceTaskScheduleDefinition,
SchedulerServiceTaskScheduleDefinitionConfig,
} from './SchedulerService';
export type { TokenManagerService } from './TokenManagerService';
export type {
ReadTreeOptions,