Merge pull request #19594 from rikez/rikez/parse-entity-ref-filter
feat(scaffolder): allow default kind and namespace args on parseEntityRef filter
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
Improved the `parseEntityRef` Scaffolder filter by introducing the ability for users to provide default kind and/or namespace values. The filter now takes
|
||||
2 arguments, similarly to the original [parseEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/entity/ref.ts#L77).
|
||||
@@ -627,3 +627,98 @@ output things. You can grab that output using `steps.$stepId.output.$property`.
|
||||
You can read more about all the `inputs` and `outputs` defined in the actions in
|
||||
code part of the `JSONSchema`, or you can read more about our
|
||||
[built in actions](./builtin-actions.md).
|
||||
|
||||
## Built in Filters
|
||||
|
||||
Template filters are functions that help you transform data, extract specific information,
|
||||
and perform various operations in Scaffolder Templates.
|
||||
|
||||
This section introduces the built-in filters provided by Backstage and offers examples of
|
||||
how to use them in the Scaffolder templates. It's important to mention that Backstage also leverages the
|
||||
native filters from the Nunjucks library. For a complete list of these native filters and their usage,
|
||||
refer to the [Nunjucks documentation](https://mozilla.github.io/nunjucks/templating.html#builtin-filters).
|
||||
|
||||
### parseRepoUrl
|
||||
|
||||
The `parseRepoUrl` filter parse a repository URL into
|
||||
its components, such as `owner`, repository `name`, and more.
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Repo URL
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.repoUrl | parseRepoUrl }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&org=backstage`
|
||||
- **Output**: [RepoSpec](https://github.com/backstage/backstage/blob/v1.17.2/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts#L39)
|
||||
|
||||
### parseEntityRef
|
||||
|
||||
The `parseEntityRef` filter allows you to extract different parts of
|
||||
an entity reference, such as the `kind`, `namespace`, and `name`.
|
||||
|
||||
**Usage example**
|
||||
|
||||
1. Without context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef }}
|
||||
```
|
||||
|
||||
- **Input**: `group:techdocs`
|
||||
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
|
||||
|
||||
2. With context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }}
|
||||
```
|
||||
|
||||
- **Input**: `techdocs`
|
||||
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
|
||||
|
||||
### pick
|
||||
|
||||
This `pick` filter allows you to select specific properties from an object.
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Pick
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef | pick('name') }}
|
||||
```
|
||||
|
||||
- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }`
|
||||
- **Output**: `techdocs`
|
||||
|
||||
### projectSlug
|
||||
|
||||
The `projectSlug` filter generates a project slug from a repository URL
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Project Slug
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.repoUrl | projectSlug }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&org=backstage`
|
||||
- **Output**: `backstage/backstage`
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { parseEntityRef } from '@backstage/catalog-model';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import type { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { TemplateFilter } from '..';
|
||||
import { parseRepoUrl } from '../../scaffolder/actions/builtin/publish/util';
|
||||
import get from 'lodash/get';
|
||||
@@ -27,7 +27,8 @@ export const createDefaultFilters = ({
|
||||
}): Record<string, TemplateFilter> => {
|
||||
return {
|
||||
parseRepoUrl: url => parseRepoUrl(url as string, integrations),
|
||||
parseEntityRef: ref => parseEntityRef(ref as string),
|
||||
parseEntityRef: (ref: JsonValue, context?: JsonValue) =>
|
||||
parseEntityRef(ref as string, context as JsonObject),
|
||||
pick: (obj: JsonValue, key: JsonValue) => get(obj, key as string),
|
||||
projectSlug: repoUrl => {
|
||||
const { owner, repo } = parseRepoUrl(repoUrl as string, integrations);
|
||||
|
||||
@@ -808,31 +808,174 @@ describe('DefaultWorkflowRunner', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('provides the parseEntityRef filter', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
describe('parseEntityRef', () => {
|
||||
it('parses entity ref', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: '${{ parameters.entity | parseEntityRef }}',
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: '${{ parameters.entity | parseEntityRef }}',
|
||||
},
|
||||
parameters: {
|
||||
entity: 'component:default/ben',
|
||||
},
|
||||
parameters: {
|
||||
entity: 'component:default/ben',
|
||||
},
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'component',
|
||||
namespace: 'default',
|
||||
name: 'ben',
|
||||
});
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
it('provides default kind for parsing entity ref', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: `\${{ parameters.entity | parseEntityRef({ defaultKind:"user" }) }}`,
|
||||
},
|
||||
parameters: {
|
||||
entity: 'ben',
|
||||
},
|
||||
});
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'component',
|
||||
namespace: 'default',
|
||||
name: 'ben',
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'user',
|
||||
namespace: 'default',
|
||||
name: 'ben',
|
||||
});
|
||||
});
|
||||
|
||||
it('provides default namespace for parsing entity ref', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: `\${{ parameters.entity | parseEntityRef({ defaultNamespace:"namespace-b" }) }}`,
|
||||
},
|
||||
parameters: {
|
||||
entity: 'user:ben',
|
||||
},
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'user',
|
||||
namespace: 'namespace-b',
|
||||
name: 'ben',
|
||||
});
|
||||
});
|
||||
|
||||
it('provides default kind and namespace for parsing entity ref', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: `\${{ parameters.entity | parseEntityRef({ defaultKind:"user", defaultNamespace:"namespace-b" }) }}`,
|
||||
},
|
||||
parameters: {
|
||||
entity: 'ben',
|
||||
},
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'user',
|
||||
namespace: 'namespace-b',
|
||||
name: 'ben',
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['undefined', 'null', 'None', 'group', 0, '{}', '[]'])(
|
||||
'ignores invalid context "%s" for parsing entity refF',
|
||||
async kind => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: `\${{ parameters.entity | parseEntityRef(${kind}) }}`,
|
||||
},
|
||||
parameters: {
|
||||
entity: 'user:default/ben',
|
||||
},
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual({
|
||||
kind: 'user',
|
||||
namespace: 'default',
|
||||
name: 'ben',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('fails when unable to parse entity ref', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'output-action',
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
output: {
|
||||
foo: `\${{ parameters.entity | parseEntityRef({ defaultNamespace:"namespace-b" }) }}`,
|
||||
},
|
||||
parameters: {
|
||||
entity: 'ben',
|
||||
},
|
||||
});
|
||||
|
||||
const { output } = await runner.execute(task);
|
||||
|
||||
expect(output.foo).toEqual(
|
||||
`\${{ parameters.entity | parseEntityRef({ defaultNamespace:"namespace-b" }) }}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user