@backstage/backend-defaults 0.14.1-next.1 → 0.15.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @backstage/backend-defaults
|
|
2
2
|
|
|
3
|
+
## 0.15.0-next.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6fc00e6: Added action filtering support with glob patterns and attribute constraints.
|
|
8
|
+
|
|
9
|
+
The `ActionsService` now supports filtering actions based on configuration. This allows controlling which actions are exposed to consumers like the MCP backend.
|
|
10
|
+
|
|
11
|
+
Configuration example:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
backend:
|
|
15
|
+
actions:
|
|
16
|
+
pluginSources:
|
|
17
|
+
- catalog
|
|
18
|
+
- scaffolder
|
|
19
|
+
filter:
|
|
20
|
+
include:
|
|
21
|
+
- id: 'catalog:*'
|
|
22
|
+
attributes:
|
|
23
|
+
destructive: false
|
|
24
|
+
- id: 'scaffolder:*'
|
|
25
|
+
exclude:
|
|
26
|
+
- id: '*:delete-*'
|
|
27
|
+
- attributes:
|
|
28
|
+
readOnly: false
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Filtering logic:
|
|
32
|
+
|
|
33
|
+
- `include`: Rules for actions to include. Each rule can specify an `id` glob pattern and/or `attributes` constraints. An action must match at least one rule to be included. If no include rules are specified, all actions are included by default.
|
|
34
|
+
- `exclude`: Rules for actions to exclude. Takes precedence over include rules.
|
|
35
|
+
- Each rule combines `id` and `attributes` with AND logic (both must match if specified).
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- Updated dependencies
|
|
40
|
+
- @backstage/backend-app-api@1.4.0
|
|
41
|
+
- @backstage/plugin-auth-node@0.6.10
|
|
42
|
+
- @backstage/plugin-permission-node@0.10.7
|
|
43
|
+
|
|
3
44
|
## 0.14.1-next.1
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
package/config.d.ts
CHANGED
|
@@ -154,6 +154,104 @@ export interface Config {
|
|
|
154
154
|
* List of plugin sources to load actions from.
|
|
155
155
|
*/
|
|
156
156
|
pluginSources?: string[];
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Filter configuration for actions. Allows controlling which actions
|
|
160
|
+
* are exposed to consumers based on patterns and attributes.
|
|
161
|
+
*/
|
|
162
|
+
filter?: {
|
|
163
|
+
/**
|
|
164
|
+
* Rules for actions to include. An action must match at least one rule to be included.
|
|
165
|
+
* Each rule can specify an id pattern and/or attribute constraints.
|
|
166
|
+
* If no include rules are specified, all actions are included by default.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```yaml
|
|
170
|
+
* include:
|
|
171
|
+
* - id: 'catalog:*'
|
|
172
|
+
* attributes:
|
|
173
|
+
* destructive: false
|
|
174
|
+
* - id: 'scaffolder:*'
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
include?: Array<{
|
|
178
|
+
/**
|
|
179
|
+
* Glob pattern for action IDs to match.
|
|
180
|
+
* Action IDs have the format `{pluginId}:{actionName}`.
|
|
181
|
+
* @example 'catalog:*'
|
|
182
|
+
*/
|
|
183
|
+
id?: string;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Attribute constraints. All specified attributes must match.
|
|
187
|
+
* Actions are compared against their resolved attributes (with defaults applied).
|
|
188
|
+
*/
|
|
189
|
+
attributes?: {
|
|
190
|
+
/**
|
|
191
|
+
* If specified, only match actions where destructive matches this value.
|
|
192
|
+
* Actions default to destructive: true if not explicitly set.
|
|
193
|
+
*/
|
|
194
|
+
destructive?: boolean;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* If specified, only match actions where readOnly matches this value.
|
|
198
|
+
* Actions default to readOnly: false if not explicitly set.
|
|
199
|
+
*/
|
|
200
|
+
readOnly?: boolean;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* If specified, only match actions where idempotent matches this value.
|
|
204
|
+
* Actions default to idempotent: false if not explicitly set.
|
|
205
|
+
*/
|
|
206
|
+
idempotent?: boolean;
|
|
207
|
+
};
|
|
208
|
+
}>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Rules for actions to exclude. Exclusions take precedence over inclusions.
|
|
212
|
+
* Each rule can specify an id pattern and/or attribute constraints.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```yaml
|
|
216
|
+
* exclude:
|
|
217
|
+
* - id: '*:delete-*'
|
|
218
|
+
* - attributes:
|
|
219
|
+
* readOnly: false
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
exclude?: Array<{
|
|
223
|
+
/**
|
|
224
|
+
* Glob pattern for action IDs to match.
|
|
225
|
+
* Action IDs have the format `{pluginId}:{actionName}`.
|
|
226
|
+
* @example '*:delete-*'
|
|
227
|
+
*/
|
|
228
|
+
id?: string;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Attribute constraints. All specified attributes must match.
|
|
232
|
+
* Actions are compared against their resolved attributes (with defaults applied).
|
|
233
|
+
*/
|
|
234
|
+
attributes?: {
|
|
235
|
+
/**
|
|
236
|
+
* If specified, only match actions where destructive matches this value.
|
|
237
|
+
* Actions default to destructive: true if not explicitly set.
|
|
238
|
+
*/
|
|
239
|
+
destructive?: boolean;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* If specified, only match actions where readOnly matches this value.
|
|
243
|
+
* Actions default to readOnly: false if not explicitly set.
|
|
244
|
+
*/
|
|
245
|
+
readOnly?: boolean;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* If specified, only match actions where idempotent matches this value.
|
|
249
|
+
* Actions default to idempotent: false if not explicitly set.
|
|
250
|
+
*/
|
|
251
|
+
idempotent?: boolean;
|
|
252
|
+
};
|
|
253
|
+
}>;
|
|
254
|
+
};
|
|
157
255
|
};
|
|
158
256
|
|
|
159
257
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var errors = require('@backstage/errors');
|
|
4
|
+
var minimatch = require('minimatch');
|
|
4
5
|
|
|
5
6
|
class DefaultActionsService {
|
|
6
7
|
discovery;
|
|
@@ -42,7 +43,7 @@ class DefaultActionsService {
|
|
|
42
43
|
}
|
|
43
44
|
})
|
|
44
45
|
);
|
|
45
|
-
return { actions: remoteActionsList.flat() };
|
|
46
|
+
return { actions: this.applyFilters(remoteActionsList.flat()) };
|
|
46
47
|
}
|
|
47
48
|
async invoke(opts) {
|
|
48
49
|
const pluginId = this.pluginIdFromActionId(opts.id);
|
|
@@ -88,6 +89,65 @@ class DefaultActionsService {
|
|
|
88
89
|
}
|
|
89
90
|
return id.substring(0, colonIndex);
|
|
90
91
|
}
|
|
92
|
+
applyFilters(actions) {
|
|
93
|
+
const filterConfig = this.config.getOptionalConfig(
|
|
94
|
+
"backend.actions.filter"
|
|
95
|
+
);
|
|
96
|
+
if (!filterConfig) {
|
|
97
|
+
return actions;
|
|
98
|
+
}
|
|
99
|
+
const includeRules = this.parseFilterRules(
|
|
100
|
+
filterConfig.getOptionalConfigArray("include") ?? []
|
|
101
|
+
);
|
|
102
|
+
const excludeRules = this.parseFilterRules(
|
|
103
|
+
filterConfig.getOptionalConfigArray("exclude") ?? []
|
|
104
|
+
);
|
|
105
|
+
return actions.filter((action) => {
|
|
106
|
+
const excluded = excludeRules.some(
|
|
107
|
+
(rule) => this.matchesRule(action, rule)
|
|
108
|
+
);
|
|
109
|
+
if (excluded) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (includeRules.length === 0) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return includeRules.some((rule) => this.matchesRule(action, rule));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
parseFilterRules(configArray) {
|
|
119
|
+
return configArray.map((ruleConfig) => {
|
|
120
|
+
const idPattern = ruleConfig.getOptionalString("id");
|
|
121
|
+
const attributesConfig = ruleConfig.getOptionalConfig("attributes");
|
|
122
|
+
const rule = {};
|
|
123
|
+
if (idPattern) {
|
|
124
|
+
rule.idMatcher = new minimatch.Minimatch(idPattern);
|
|
125
|
+
}
|
|
126
|
+
if (attributesConfig) {
|
|
127
|
+
rule.attributes = {};
|
|
128
|
+
for (const key of ["destructive", "readOnly", "idempotent"]) {
|
|
129
|
+
const value = attributesConfig.getOptionalBoolean(key);
|
|
130
|
+
if (value !== void 0) {
|
|
131
|
+
rule.attributes[key] = value;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return rule;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
matchesRule(action, rule) {
|
|
139
|
+
if (rule.idMatcher && !rule.idMatcher.match(action.id)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (rule.attributes) {
|
|
143
|
+
for (const [key, value] of Object.entries(rule.attributes)) {
|
|
144
|
+
if (action.attributes[key] !== value) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
91
151
|
}
|
|
92
152
|
|
|
93
153
|
exports.DefaultActionsService = DefaultActionsService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultActionsService.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/DefaultActionsService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport {\n ActionsService,\n ActionsServiceAction,\n} from '@backstage/backend-plugin-api/alpha';\n\nexport class DefaultActionsService implements ActionsService {\n private readonly discovery: DiscoveryService;\n private readonly config: RootConfigService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n\n private constructor(\n discovery: DiscoveryService,\n config: RootConfigService,\n logger: LoggerService,\n auth: AuthService,\n ) {\n this.discovery = discovery;\n this.config = config;\n this.logger = logger;\n this.auth = auth;\n }\n\n static create({\n discovery,\n config,\n logger,\n auth,\n }: {\n discovery: DiscoveryService;\n config: RootConfigService;\n logger: LoggerService;\n auth: AuthService;\n }) {\n return new DefaultActionsService(discovery, config, logger, auth);\n }\n\n async list({ credentials }: { credentials: BackstageCredentials }) {\n const pluginSources =\n this.config.getOptionalStringArray('backend.actions.pluginSources') ?? [];\n\n const remoteActionsList = await Promise.all(\n pluginSources.map(async source => {\n try {\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions`,\n pluginId: source,\n credentials,\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n const { actions } = (await response.json()) as {\n actions: ActionsServiceAction;\n };\n\n return actions;\n } catch (error) {\n this.logger.warn(`Failed to fetch actions from ${source}`, error);\n return [];\n }\n }),\n );\n\n return { actions: remoteActionsList.flat() };\n }\n\n async invoke(opts: {\n id: string;\n input?: JsonObject;\n credentials: BackstageCredentials;\n }) {\n const pluginId = this.pluginIdFromActionId(opts.id);\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions/${encodeURIComponent(\n opts.id,\n )}/invoke`,\n pluginId,\n credentials: opts.credentials,\n options: {\n method: 'POST',\n body: JSON.stringify(opts.input),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { output } = await response.json();\n return { output };\n }\n\n private async makeRequest(opts: {\n path: string;\n pluginId: string;\n options?: RequestInit;\n credentials: BackstageCredentials;\n }) {\n const { path, pluginId, credentials, options } = opts;\n const baseUrl = await this.discovery.getBaseUrl(pluginId);\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: opts.pluginId,\n });\n\n return fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n ...options?.headers,\n Authorization: `Bearer ${token}`,\n },\n });\n }\n\n private pluginIdFromActionId(id: string): string {\n const colonIndex = id.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid action id: ${id}`);\n }\n return id.substring(0, colonIndex);\n }\n}\n"],"names":["ResponseError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultActionsService.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/DefaultActionsService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport {\n ActionsService,\n ActionsServiceAction,\n} from '@backstage/backend-plugin-api/alpha';\nimport { Minimatch } from 'minimatch';\nimport { Config } from '@backstage/config';\n\nexport class DefaultActionsService implements ActionsService {\n private readonly discovery: DiscoveryService;\n private readonly config: RootConfigService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n\n private constructor(\n discovery: DiscoveryService,\n config: RootConfigService,\n logger: LoggerService,\n auth: AuthService,\n ) {\n this.discovery = discovery;\n this.config = config;\n this.logger = logger;\n this.auth = auth;\n }\n\n static create({\n discovery,\n config,\n logger,\n auth,\n }: {\n discovery: DiscoveryService;\n config: RootConfigService;\n logger: LoggerService;\n auth: AuthService;\n }) {\n return new DefaultActionsService(discovery, config, logger, auth);\n }\n\n async list({ credentials }: { credentials: BackstageCredentials }) {\n const pluginSources =\n this.config.getOptionalStringArray('backend.actions.pluginSources') ?? [];\n\n const remoteActionsList = await Promise.all(\n pluginSources.map(async source => {\n try {\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions`,\n pluginId: source,\n credentials,\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n const { actions } = (await response.json()) as {\n actions: ActionsServiceAction;\n };\n\n return actions;\n } catch (error) {\n this.logger.warn(`Failed to fetch actions from ${source}`, error);\n return [];\n }\n }),\n );\n\n return { actions: this.applyFilters(remoteActionsList.flat()) };\n }\n\n async invoke(opts: {\n id: string;\n input?: JsonObject;\n credentials: BackstageCredentials;\n }) {\n const pluginId = this.pluginIdFromActionId(opts.id);\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions/${encodeURIComponent(\n opts.id,\n )}/invoke`,\n pluginId,\n credentials: opts.credentials,\n options: {\n method: 'POST',\n body: JSON.stringify(opts.input),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { output } = await response.json();\n return { output };\n }\n\n private async makeRequest(opts: {\n path: string;\n pluginId: string;\n options?: RequestInit;\n credentials: BackstageCredentials;\n }) {\n const { path, pluginId, credentials, options } = opts;\n const baseUrl = await this.discovery.getBaseUrl(pluginId);\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: opts.pluginId,\n });\n\n return fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n ...options?.headers,\n Authorization: `Bearer ${token}`,\n },\n });\n }\n\n private pluginIdFromActionId(id: string): string {\n const colonIndex = id.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid action id: ${id}`);\n }\n return id.substring(0, colonIndex);\n }\n\n private applyFilters(\n actions: ActionsServiceAction[],\n ): ActionsServiceAction[] {\n const filterConfig = this.config.getOptionalConfig(\n 'backend.actions.filter',\n );\n\n if (!filterConfig) {\n return actions;\n }\n\n const includeRules = this.parseFilterRules(\n filterConfig.getOptionalConfigArray('include') ?? [],\n );\n const excludeRules = this.parseFilterRules(\n filterConfig.getOptionalConfigArray('exclude') ?? [],\n );\n\n return actions.filter(action => {\n const excluded = excludeRules.some(rule =>\n this.matchesRule(action, rule),\n );\n\n if (excluded) {\n return false;\n }\n\n // If no include rules, include by default\n if (includeRules.length === 0) {\n return true;\n }\n\n // Must match at least one include rule\n return includeRules.some(rule => this.matchesRule(action, rule));\n });\n }\n\n private parseFilterRules(configArray: Array<Config>): Array<{\n idMatcher?: Minimatch;\n attributes?: Partial<\n Record<'destructive' | 'readOnly' | 'idempotent', boolean>\n >;\n }> {\n return configArray.map(ruleConfig => {\n const idPattern = ruleConfig.getOptionalString('id');\n const attributesConfig = ruleConfig.getOptionalConfig('attributes');\n\n const rule: {\n idMatcher?: Minimatch;\n attributes?: Partial<\n Record<'destructive' | 'readOnly' | 'idempotent', boolean>\n >;\n } = {};\n\n if (idPattern) {\n rule.idMatcher = new Minimatch(idPattern);\n }\n\n if (attributesConfig) {\n rule.attributes = {};\n for (const key of ['destructive', 'readOnly', 'idempotent'] as const) {\n const value = attributesConfig.getOptionalBoolean(key);\n if (value !== undefined) {\n rule.attributes[key] = value;\n }\n }\n }\n\n return rule;\n });\n }\n\n private matchesRule(\n action: ActionsServiceAction,\n rule: {\n idMatcher?: Minimatch;\n attributes?: Partial<\n Record<'destructive' | 'readOnly' | 'idempotent', boolean>\n >;\n },\n ): boolean {\n // If id pattern is specified, it must match\n if (rule.idMatcher && !rule.idMatcher.match(action.id)) {\n return false;\n }\n\n // If attributes are specified, all must match\n if (rule.attributes) {\n for (const [key, value] of Object.entries(rule.attributes)) {\n if (\n action.attributes[\n key as 'destructive' | 'readOnly' | 'idempotent'\n ] !== value\n ) {\n return false;\n }\n }\n }\n\n return true;\n }\n}\n"],"names":["ResponseError","Minimatch"],"mappings":"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"}
|
package/dist/package.json.cjs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/backend-defaults",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0-next.2",
|
|
4
4
|
"description": "Backend defaults used by Backstage backend apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
"devDependencies": {
|
|
285
285
|
"@aws-sdk/util-stream-node": "^3.350.0",
|
|
286
286
|
"@backstage/backend-plugin-api": "1.6.0",
|
|
287
|
-
"@backstage/backend-test-utils": "1.10.3-next.
|
|
287
|
+
"@backstage/backend-test-utils": "1.10.3-next.1",
|
|
288
288
|
"@backstage/cli": "0.35.2-next.1",
|
|
289
289
|
"@google-cloud/cloud-sql-connector": "^1.4.0",
|
|
290
290
|
"@types/archiver": "^7.0.0",
|