Merge pull request #30231 from backstage/blam/actions-definition-change
`ActionsRegistry`: Allow providing attributes to the `Actions` that are registered
This commit is contained in:
@@ -70,6 +70,11 @@ describe('actionsServiceFactory', () => {
|
||||
input: {},
|
||||
output: {},
|
||||
},
|
||||
attributes: {
|
||||
destructive: false,
|
||||
idempotent: false,
|
||||
readOnly: false,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -306,6 +311,11 @@ describe('actionsServiceFactory', () => {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
destructive: true,
|
||||
idempotent: false,
|
||||
readOnly: false,
|
||||
},
|
||||
title: 'Test',
|
||||
},
|
||||
],
|
||||
|
||||
+9
-2
@@ -67,13 +67,20 @@ export class DefaultActionsRegistryService implements ActionsRegistryService {
|
||||
actions: Array.from(this.actions.entries()).map(([id, action]) => ({
|
||||
id,
|
||||
...action,
|
||||
attributes: {
|
||||
// Inspired by the @modelcontextprotocol/sdk defaults for the hints.
|
||||
// https://github.com/modelcontextprotocol/typescript-sdk/blob/dd69efa1de8646bb6b195ff8d5f52e13739f4550/src/types.ts#L777-L812
|
||||
destructive: action.attributes?.destructive ?? true,
|
||||
idempotent: action.attributes?.idempotent ?? false,
|
||||
readOnly: action.attributes?.readOnly ?? false,
|
||||
},
|
||||
schema: {
|
||||
input: action.schema?.input
|
||||
? zodToJsonSchema(action.schema.input(z))
|
||||
: zodToJsonSchema(z.any()),
|
||||
: zodToJsonSchema(z.object({})),
|
||||
output: action.schema?.output
|
||||
? zodToJsonSchema(action.schema.output(z))
|
||||
: zodToJsonSchema(z.any()),
|
||||
: zodToJsonSchema(z.object({})),
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
+103
@@ -181,6 +181,109 @@ describe('actionsRegistryServiceFactory', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should set default attributes', async () => {
|
||||
const pluginSubject = createBackendPlugin({
|
||||
pluginId: 'my-plugin',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
actionsRegistry: coreServices.actionsRegistry,
|
||||
},
|
||||
async init({ actionsRegistry }) {
|
||||
actionsRegistry.register({
|
||||
name: 'test',
|
||||
title: 'Test',
|
||||
description: 'Test',
|
||||
schema: {
|
||||
input: z => z.object({}),
|
||||
output: z => z.object({}),
|
||||
},
|
||||
action: async () => ({ output: { ok: true } }),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { server } = await startTestBackend({
|
||||
features: [pluginSubject, ...defaultServices],
|
||||
});
|
||||
|
||||
const { body, status } = await request(server).get(
|
||||
'/api/my-plugin/.backstage/actions/v1/actions',
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
actions: [
|
||||
{
|
||||
name: 'test',
|
||||
attributes: {
|
||||
destructive: true,
|
||||
idempotent: false,
|
||||
readOnly: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow setting attributes', async () => {
|
||||
const pluginSubject = createBackendPlugin({
|
||||
pluginId: 'my-plugin',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
actionsRegistry: coreServices.actionsRegistry,
|
||||
},
|
||||
async init({ actionsRegistry }) {
|
||||
actionsRegistry.register({
|
||||
name: 'test',
|
||||
title: 'Test',
|
||||
description: 'Test',
|
||||
attributes: {
|
||||
destructive: false,
|
||||
idempotent: true,
|
||||
readOnly: true,
|
||||
},
|
||||
schema: {
|
||||
input: z => z.object({}),
|
||||
output: z => z.object({}),
|
||||
},
|
||||
action: async () => ({ output: { ok: true } }),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { server } = await startTestBackend({
|
||||
features: [pluginSubject, ...defaultServices],
|
||||
});
|
||||
|
||||
const { body, status } = await request(server).get(
|
||||
'/api/my-plugin/.backstage/actions/v1/actions',
|
||||
);
|
||||
|
||||
expect(status).toBe(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
actions: [
|
||||
{
|
||||
name: 'test',
|
||||
title: 'Test',
|
||||
description: 'Test',
|
||||
attributes: {
|
||||
destructive: false,
|
||||
idempotent: true,
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should forces registration of input and output schema as objects', async () => {
|
||||
const pluginSubject = createBackendPlugin({
|
||||
pluginId: 'my-plugin',
|
||||
|
||||
@@ -48,6 +48,11 @@ export type ActionsRegistryActionOptions<
|
||||
input: (zod: typeof z) => TInputSchema;
|
||||
output: (zod: typeof z) => TOutputSchema;
|
||||
};
|
||||
attributes?: {
|
||||
destructive?: boolean;
|
||||
idempotent?: boolean;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
action: (context: ActionsRegistryActionContext<TInputSchema>) => Promise<
|
||||
z.infer<TOutputSchema> extends void
|
||||
? void
|
||||
@@ -94,6 +99,11 @@ export type ActionsServiceAction = {
|
||||
input: JSONSchema7;
|
||||
output: JSONSchema7;
|
||||
};
|
||||
attributes: {
|
||||
readOnly: boolean;
|
||||
destructive: boolean;
|
||||
idempotent: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -40,6 +40,11 @@ export type ActionsRegistryActionOptions<
|
||||
input: (zod: typeof z) => TInputSchema;
|
||||
output: (zod: typeof z) => TOutputSchema;
|
||||
};
|
||||
attributes?: {
|
||||
destructive?: boolean;
|
||||
idempotent?: boolean;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
action: (
|
||||
context: ActionsRegistryActionContext<TInputSchema>,
|
||||
) => Promise<
|
||||
|
||||
@@ -29,6 +29,11 @@ export type ActionsServiceAction = {
|
||||
input: JSONSchema7;
|
||||
output: JSONSchema7;
|
||||
};
|
||||
attributes: {
|
||||
readOnly: boolean;
|
||||
destructive: boolean;
|
||||
idempotent: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user