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:
Oliver Sand
2020-10-08 16:25:31 +02:00
committed by GitHub
6 changed files with 45 additions and 176 deletions
+26
View File
@@ -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(),
@@ -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';