relations wip

This commit is contained in:
Patrik Oldsberg
2020-08-23 10:42:17 +02:00
parent 255f6ce03b
commit 62493e54c1
9 changed files with 172 additions and 6 deletions
+38
View File
@@ -0,0 +1,38 @@
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: root
spec:
type: org
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: platform
spec:
type: mission
parent: root
# ---
# apiVersion: backstage.io/v1alpha1
# kind: Group
# metadata:
# name: pdx
# spec:
# type: tribe
# parent: platform
# ---
# apiVersion: backstage.io/v1alpha1
# kind: Group
# metadata:
# name: tools
# spec:
# type: squad
# parent: pdx
# ---
# apiVersion: backstage.io/v1alpha1
# kind: Group
# metadata:
# name: pulp-fiction
# spec:
# type: squad
# parent: pdx
@@ -69,7 +69,11 @@ describe('HigherOrderOperations', () => {
};
locationsCatalog.addLocation.mockImplementation(x => Promise.resolve(x));
locationsCatalog.locations.mockResolvedValue([]);
locationReader.read.mockResolvedValue({ entities: [], errors: [] });
locationReader.read.mockResolvedValue({
entities: [],
errors: [],
relations: [],
});
const result = await higherOrderOperation.addLocation(spec);
@@ -109,7 +113,11 @@ describe('HigherOrderOperations', () => {
data: location,
},
]);
locationReader.read.mockResolvedValue({ entities: [], errors: [] });
locationReader.read.mockResolvedValue({
entities: [],
errors: [],
relations: [],
});
const result = await higherOrderOperation.addLocation(spec);
@@ -138,6 +146,7 @@ describe('HigherOrderOperations', () => {
locationReader.read.mockResolvedValue({
entities: [{ entity, location }],
errors: [{ error: new Error('abcd'), location }],
relations: [],
});
await expect(higherOrderOperation.addLocation(spec)).rejects.toThrow(
@@ -186,6 +195,7 @@ describe('HigherOrderOperations', () => {
locationReader.read.mockResolvedValue({
entities: [{ entity: desc, location }],
errors: [],
relations: [],
});
entitiesCatalog.batchAddOrUpdateEntities.mockResolvedValue(undefined);
@@ -231,6 +241,7 @@ describe('HigherOrderOperations', () => {
locationReader.read.mockResolvedValue({
entities: [{ entity: desc, location }],
errors: [],
relations: [],
});
entitiesCatalog.entities.mockResolvedValue([]);
entitiesCatalog.addEntities.mockResolvedValue(undefined);
@@ -61,7 +61,11 @@ export class LocationReaders implements LocationReader {
async read(location: LocationSpec): Promise<ReadLocationResult> {
const { rulesEnforcer, logger } = this.options;
const output: ReadLocationResult = { entities: [], errors: [] };
const output: ReadLocationResult = {
entities: [],
errors: [],
relations: [],
};
let items: CatalogProcessorResult[] = [result.location(location, false)];
for (let depth = 0; depth < MAX_DEPTH; ++depth) {
@@ -96,6 +100,10 @@ export class LocationReaders implements LocationReader {
location: item.location,
error: item.error,
});
} else if (item.type === 'relation') {
output.relations.push({
relation: item.relation,
});
}
}
@@ -0,0 +1,82 @@
/*
* 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 {
Entity,
ENTITY_DEFAULT_NAMESPACE,
GroupEntity,
LocationSpec,
} from '@backstage/catalog-model';
import { CatalogProcessor, CatalogProcessorEmit } from './types';
import * as result from './results';
export class GroupPopulatorProcessor implements CatalogProcessor {
async postProcessEntity(
entity: Entity,
_location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity> {
if (entity.kind.toLowerCase() !== 'group') {
return entity;
}
const spec = entity.spec as GroupEntity['spec'];
const { parent, children } = spec;
const self = {
kind: entity.kind,
name: entity.metadata.name,
namespace: entity.metadata.namespace ?? ENTITY_DEFAULT_NAMESPACE,
};
if (parent) {
emit(
result.relation({
type: 'parent',
source: self,
target: { ...self, name: parent },
}),
);
emit(
result.relation({
type: 'child',
source: { ...self, name: parent },
target: self,
}),
);
}
if (children) {
for (const child of children) {
emit(
result.relation({
type: 'child',
source: self,
target: { ...self, name: child },
}),
);
emit(
result.relation({
type: 'parent',
source: { ...self, name: child },
target: self,
}),
);
}
}
return entity;
}
}
@@ -28,6 +28,7 @@ export { GithubOrgReaderProcessor } from './GithubOrgReaderProcessor';
export { GithubReaderProcessor } from './GithubReaderProcessor';
export { GitlabApiReaderProcessor } from './GitlabApiReaderProcessor';
export { GitlabReaderProcessor } from './GitlabReaderProcessor';
export { GroupPopulatorProcessor } from './GroupPopulatorProcessor';
export { LocationRefProcessor } from './LocationEntityProcessor';
export { PlaceholderProcessor } from './PlaceholderProcessor';
export type { PlaceholderResolver } from './PlaceholderProcessor';
@@ -15,7 +15,11 @@
*/
import { InputError, NotFoundError } from '@backstage/backend-common';
import { Entity, LocationSpec } from '@backstage/catalog-model';
import {
Entity,
EntityRelationSpec,
LocationSpec,
} from '@backstage/catalog-model';
import { CatalogProcessorResult } from './types';
export function notFoundError(
@@ -67,3 +71,7 @@ export function entity(
): CatalogProcessorResult {
return { type: 'entity', location: atLocation, entity: newEntity };
}
export function relation(spec: EntityRelationSpec): CatalogProcessorResult {
return { type: 'relation', relation: spec };
}
@@ -14,7 +14,11 @@
* limitations under the License.
*/
import { Entity, LocationSpec } from '@backstage/catalog-model';
import {
Entity,
EntityRelationSpec,
LocationSpec,
} from '@backstage/catalog-model';
export type CatalogProcessor = {
/**
@@ -113,6 +117,11 @@ export type CatalogProcessorEntityResult = {
location: LocationSpec;
};
export type CatalogProcessorRelationResult = {
type: 'relation';
relation: EntityRelationSpec;
};
export type CatalogProcessorErrorResult = {
type: 'error';
error: Error;
@@ -123,4 +132,5 @@ export type CatalogProcessorResult =
| CatalogProcessorLocationResult
| CatalogProcessorDataResult
| CatalogProcessorEntityResult
| CatalogProcessorRelationResult
| CatalogProcessorErrorResult;
@@ -14,7 +14,12 @@
* limitations under the License.
*/
import type { Entity, Location, LocationSpec } from '@backstage/catalog-model';
import type {
Entity,
EntityRelationSpec,
Location,
LocationSpec,
} from '@backstage/catalog-model';
//
// HigherOrderOperation
@@ -47,6 +52,7 @@ export type LocationReader = {
export type ReadLocationResult = {
entities: ReadLocationEntity[];
relations: { relation: EntityRelationSpec }[];
errors: ReadLocationError[];
};
@@ -52,6 +52,7 @@ import {
GithubReaderProcessor,
GitlabApiReaderProcessor,
GitlabReaderProcessor,
GroupPopulatorProcessor,
HigherOrderOperation,
HigherOrderOperations,
LocationReaders,
@@ -334,6 +335,7 @@ export class CatalogBuilder {
new YamlProcessor(),
new CodeOwnersProcessor({ reader }),
new LocationRefProcessor(),
new GroupPopulatorProcessor(),
new AnnotateLocationEntityProcessor(),
];