Merge pull request #2803 from SDA-SE/feat/remove-api-annotation
feat: remove the backstage.io/definition-at-location annotation
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
Remove the backstage.io/definition-at-location annotation.
|
||||
The annotation was superseded by the placeholder processor.
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: spotify
|
||||
description: The Spotify web API
|
||||
tags:
|
||||
- spotify
|
||||
- rest
|
||||
annotations:
|
||||
# Don't use this annotation, but the placeholder $text instead (see below).
|
||||
backstage.io/definition-at-location: 'url:https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml'
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: production
|
||||
owner: spotify@example.com
|
||||
definition:
|
||||
$text: https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml
|
||||
```
|
||||
@@ -41,27 +41,6 @@ expecting a two-item array out of it. The format of the target part is
|
||||
type-dependent and could conceivably even be an empty string, but the separator
|
||||
colon is always present.
|
||||
|
||||
### backstage.io/definition-at-location
|
||||
|
||||
```yaml
|
||||
# Example
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: petstore
|
||||
annotations:
|
||||
backstage.io/definition-at-location: 'url:https://petstore.swagger.io/v2/swagger.json'
|
||||
spec:
|
||||
type: openapi
|
||||
```
|
||||
|
||||
This annotation allows to fetch an API definition from another location, instead
|
||||
of wrapping the API definition inside the definition field. This allows to
|
||||
easily consume existing API definition. The definition is fetched during
|
||||
ingestion by a processor and included in the entity. It is updated on every
|
||||
refresh. The annotation contains a location reference string that contains the
|
||||
location processor type and the target.
|
||||
|
||||
### backstage.io/techdocs-ref
|
||||
|
||||
```yaml
|
||||
@@ -205,6 +184,25 @@ This annotation was used for a while to enable the GitHub Actions feature. This
|
||||
is now instead using the [github.com/project-slug](#github-com-project-slug)
|
||||
annotation, with the same value format.
|
||||
|
||||
### backstage.io/definition-at-location
|
||||
|
||||
This annotation allowed to load the API definition from another location. Now
|
||||
placeholders can be used instead:
|
||||
|
||||
```
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: petstore
|
||||
description: The Petstore API
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: production
|
||||
owner: petstore@example.com
|
||||
definition:
|
||||
$text: https://petstore.swagger.io/v2/swagger.json
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Descriptor Format: annotations](descriptor-format.md#annotations-optional)
|
||||
|
||||
@@ -26,7 +26,6 @@ import { Config, ConfigReader } from '@backstage/config';
|
||||
import { Logger } from 'winston';
|
||||
import { CatalogRulesEnforcer } from './CatalogRules';
|
||||
import { AnnotateLocationEntityProcessor } from './processors/AnnotateLocationEntityProcessor';
|
||||
import { ApiDefinitionAtLocationProcessor } from './processors/ApiDefinitionAtLocationProcessor';
|
||||
import { AzureApiReaderProcessor } from './processors/AzureApiReaderProcessor';
|
||||
import { BitbucketApiReaderProcessor } from './processors/BitbucketApiReaderProcessor';
|
||||
import { CodeOwnersProcessor } from './processors/CodeOwnersProcessor';
|
||||
@@ -127,7 +126,6 @@ export class LocationReaders implements LocationReader {
|
||||
new YamlProcessor(),
|
||||
PlaceholderProcessor.default({ reader: options.reader }),
|
||||
new CodeOwnersProcessor(),
|
||||
new ApiDefinitionAtLocationProcessor(),
|
||||
new EntityPolicyProcessor(entityPolicy),
|
||||
new LocationRefProcessor(),
|
||||
new AnnotateLocationEntityProcessor(),
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
import { ApiEntity, Entity, LocationSpec } from '@backstage/catalog-model';
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { ApiDefinitionAtLocationProcessor } from './ApiDefinitionAtLocationProcessor';
|
||||
|
||||
describe('ApiDefinitionAtLocationProcessor', () => {
|
||||
let processor: ApiDefinitionAtLocationProcessor;
|
||||
let entity: Entity;
|
||||
let location: LocationSpec;
|
||||
|
||||
beforeEach(() => {
|
||||
processor = new ApiDefinitionAtLocationProcessor();
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'API',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
lifecycle: 'production',
|
||||
owner: 'info@example.com',
|
||||
type: 'openapi',
|
||||
definition: 'Hello',
|
||||
},
|
||||
};
|
||||
location = {
|
||||
type: 'url',
|
||||
target: `http://example.com/api.yaml`,
|
||||
};
|
||||
});
|
||||
|
||||
it('should skip entities without annotation', async () => {
|
||||
const read = jest.fn().mockRejectedValue(new Error('boo'));
|
||||
|
||||
const generated = (await processor.processEntity(
|
||||
entity,
|
||||
location,
|
||||
() => {},
|
||||
read,
|
||||
)) as ApiEntity;
|
||||
|
||||
expect(generated.spec.definition).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should load from location', async () => {
|
||||
entity.metadata.annotations = {
|
||||
'backstage.io/definition-at-location':
|
||||
'url:http://example.com/openapi.yaml',
|
||||
};
|
||||
|
||||
const read = jest.fn().mockResolvedValue(Buffer.from('Hello'));
|
||||
|
||||
const generated = (await processor.processEntity(
|
||||
entity,
|
||||
location,
|
||||
() => {},
|
||||
read,
|
||||
)) as ApiEntity;
|
||||
|
||||
expect(generated.spec.definition).toBe('Hello');
|
||||
expect(read.mock.calls[0][0]).toStrictEqual({
|
||||
type: 'url',
|
||||
target: 'http://example.com/openapi.yaml',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw errors while loading', async () => {
|
||||
entity.metadata.annotations = {
|
||||
'backstage.io/definition-at-location': 'missing',
|
||||
};
|
||||
|
||||
const read = jest
|
||||
.fn()
|
||||
.mockRejectedValue(new Error('Failed to load location'));
|
||||
|
||||
await expect(
|
||||
processor.processEntity(entity, location, () => {}, read),
|
||||
).rejects.toThrow('Failed to load location');
|
||||
});
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { ApiEntity, Entity, LocationSpec } from '@backstage/catalog-model';
|
||||
import {
|
||||
LocationProcessor,
|
||||
LocationProcessorEmit,
|
||||
LocationProcessorRead,
|
||||
} from './types';
|
||||
|
||||
const DEFINITION_AT_LOCATION_ANNOTATION = 'backstage.io/definition-at-location';
|
||||
|
||||
export class ApiDefinitionAtLocationProcessor implements LocationProcessor {
|
||||
async processEntity(
|
||||
entity: Entity,
|
||||
_location: LocationSpec,
|
||||
_emit: LocationProcessorEmit,
|
||||
read: LocationProcessorRead,
|
||||
): Promise<Entity> {
|
||||
if (
|
||||
entity.kind !== 'API' ||
|
||||
!entity.metadata.annotations ||
|
||||
!entity.metadata.annotations[DEFINITION_AT_LOCATION_ANNOTATION]
|
||||
) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
const reference =
|
||||
entity.metadata.annotations[DEFINITION_AT_LOCATION_ANNOTATION];
|
||||
const { type, target } = extractReference(reference);
|
||||
const data = await read({ type, target });
|
||||
const definition = data.toString();
|
||||
const apiEntity = entity as ApiEntity;
|
||||
apiEntity.spec.definition = definition;
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
function extractReference(reference: string): { type: string; target: string } {
|
||||
const delimiterIndex = reference.indexOf(':');
|
||||
const type = reference.slice(0, delimiterIndex);
|
||||
const target = reference.slice(delimiterIndex + 1);
|
||||
|
||||
return { type, target };
|
||||
}
|
||||
@@ -20,7 +20,6 @@ export { results };
|
||||
export * from './types';
|
||||
|
||||
export { AnnotateLocationEntityProcessor } from './AnnotateLocationEntityProcessor';
|
||||
export { ApiDefinitionAtLocationProcessor } from './ApiDefinitionAtLocationProcessor';
|
||||
export { AzureApiReaderProcessor } from './AzureApiReaderProcessor';
|
||||
export { BitbucketApiReaderProcessor } from './BitbucketApiReaderProcessor';
|
||||
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
|
||||
|
||||
Reference in New Issue
Block a user