Foundation for standard status values

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2021-05-24 09:46:01 +02:00
parent c6af6f8fe9
commit add62a4552
16 changed files with 210 additions and 32 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/catalog-client': patch
'@backstage/catalog-model': patch
'@backstage/plugin-catalog-backend': patch
---
Foundation for standard entity status values
+18
View File
@@ -7,6 +7,9 @@
import { Entity } from '@backstage/catalog-model';
import { EntityName } from '@backstage/catalog-model';
import { Location as Location_2 } from '@backstage/catalog-model';
import { SerializedError } from '@backstage/errors';
import { UNSTABLE_EntityStatusLevel } from '@backstage/catalog-model';
import { UNSTABLE_EntityStatusValue } from '@backstage/catalog-model';
// @public (undocumented)
export type AddLocationRequest = {
@@ -76,6 +79,21 @@ export type CatalogListResponse<T> = {
items: T[];
};
// @public
export const ENTITY_STATUS_CATALOG_PROCESSING_KEY = "backstage.io/catalog-processing";
// @public
export type UNSTABLE_CatalogProcessingStatus = UNSTABLE_EntityStatusValue & {
items?: UNSTABLE_CatalogProcessingStatusItem[];
};
// @public (undocumented)
export type UNSTABLE_CatalogProcessingStatusItem = {
status: UNSTABLE_EntityStatusLevel;
message?: string;
error?: SerializedError;
};
// (No @packageDocumentation comment for this package)
@@ -18,7 +18,8 @@ import { Entity } from '@backstage/catalog-model';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { CatalogClient } from './CatalogClient';
import { CatalogListResponse, DiscoveryApi } from './types';
import { CatalogListResponse } from './types/api';
import { DiscoveryApi } from './types/discovery';
const server = setupServer();
const token = 'fake-token';
+2 -2
View File
@@ -31,8 +31,8 @@ import {
CatalogEntitiesRequest,
CatalogListResponse,
CatalogRequestOptions,
DiscoveryApi,
} from './types';
} from './types/api';
import { DiscoveryApi } from './types/discovery';
export class CatalogClient implements CatalogApi {
private readonly discoveryApi: DiscoveryApi;
+1 -7
View File
@@ -15,10 +15,4 @@
*/
export { CatalogClient } from './CatalogClient';
export type {
AddLocationRequest,
AddLocationResponse,
CatalogApi,
CatalogEntitiesRequest,
CatalogListResponse,
} from './types';
export * from './types';
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Spotify AB
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,10 +81,3 @@ export type AddLocationResponse = {
location: Location;
entities: Entity[];
};
/**
* This is a copy of the core DiscoveryApi, to avoid importing core.
*/
export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
};
@@ -0,0 +1,22 @@
/*
* Copyright 2021 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.
*/
/**
* This is a copy of the core DiscoveryApi, to avoid importing core.
*/
export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
};
@@ -0,0 +1,28 @@
/*
* Copyright 2021 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.
*/
export type {
AddLocationRequest,
AddLocationResponse,
CatalogApi,
CatalogEntitiesRequest,
CatalogListResponse,
} from './api';
export { ENTITY_STATUS_CATALOG_PROCESSING_KEY } from './status';
export type {
UNSTABLE_CatalogProcessingStatus,
UNSTABLE_CatalogProcessingStatusItem,
} from './status';
@@ -0,0 +1,63 @@
/*
* Copyright 2021 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 {
UNSTABLE_EntityStatusLevel,
UNSTABLE_EntityStatusValue,
} from '@backstage/catalog-model';
import { SerializedError } from '@backstage/errors';
/*
* This is the entity status field that's emitted by the catalog processing
* engine, to inform about the status of an entity.
*
* Example:
*
* "status": {
* "backstage.io/catalog-processing": {
* "status": "error",
* "items": [
* {
* "status": "error",
* "error": {
* "name": "InputError",
* "message": "Syntax error: ..."
* }
* }
* ]
* }
* }
*/
/**
* The entity `status` key for the status of the processing engine in regards
* to entity.
*/
export const ENTITY_STATUS_CATALOG_PROCESSING_KEY =
'backstage.io/catalog-processing';
/**
* The status value for the `backstage.io/catalog-processing` key.
*/
export type UNSTABLE_CatalogProcessingStatus = UNSTABLE_EntityStatusValue & {
items?: UNSTABLE_CatalogProcessingStatusItem[];
};
export type UNSTABLE_CatalogProcessingStatusItem = {
status: UNSTABLE_EntityStatusLevel;
message?: string;
error?: SerializedError;
};
+9
View File
@@ -528,6 +528,15 @@ export interface TemplateEntityV1beta2 extends Entity {
// @public (undocumented)
export const templateEntityV1beta2Validator: KindValidator;
// @public
export type UNSTABLE_EntityStatusLevel = 'ok' | 'info' | 'warning' | 'error';
// @public
export type UNSTABLE_EntityStatusValue = {
status: UNSTABLE_EntityStatusLevel;
message?: string;
};
// @public (undocumented)
interface UserEntityV1alpha1 extends Entity {
// (undocumented)
+5 -1
View File
@@ -15,10 +15,10 @@
*/
export {
EDIT_URL_ANNOTATION,
ENTITY_DEFAULT_NAMESPACE,
ENTITY_META_GENERATED_FIELDS,
VIEW_URL_ANNOTATION,
EDIT_URL_ANNOTATION,
} from './constants';
export type {
Entity,
@@ -36,6 +36,10 @@ export {
serializeEntityRef,
stringifyEntityRef,
} from './ref';
export type {
UNSTABLE_EntityStatusLevel,
UNSTABLE_EntityStatusValue,
} from './status';
export {
entityHasChanges,
generateEntityEtag,
@@ -0,0 +1,32 @@
/*
* Copyright 2021 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.
*/
/**
* Each entity status entry has a level, describing its severity.
*/
export type UNSTABLE_EntityStatusLevel =
| 'ok' // Everything is OK
| 'info' // Only informative data
| 'warning' // Warnings were found
| 'error'; // Errors were found
/**
* Reserved root fields in all `status` object values.
*/
export type UNSTABLE_EntityStatusValue = {
status: UNSTABLE_EntityStatusLevel;
message?: string;
};
+1
View File
@@ -31,6 +31,7 @@
"dependencies": {
"@azure/msal-node": "^1.0.0-beta.3",
"@backstage/backend-common": "^0.8.1",
"@backstage/catalog-client": "^0.3.11",
"@backstage/catalog-model": "^0.7.10",
"@backstage/config": "^0.1.5",
"@backstage/errors": "^0.1.1",
@@ -15,6 +15,7 @@
*/
import { stringifyEntityRef } from '@backstage/catalog-model';
import { serializeError } from '@backstage/errors';
import { Logger } from 'winston';
import { ProcessingDatabase } from './database/types';
import { Stitcher } from './Stitcher';
@@ -128,7 +129,7 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine {
id,
processedEntity: result.completedEntity,
state: result.state,
errors: JSON.stringify(result.errors),
errors: JSON.stringify(result.errors.map(e => serializeError(e))),
relations: result.relations,
deferredEntities: result.deferredEntities,
});
@@ -16,6 +16,7 @@
import { getVoidLogger } from '@backstage/backend-common';
import { Knex } from 'knex';
import { ENTITY_STATUS_CATALOG_PROCESSING_KEY } from '@backstage/catalog-client';
import { DatabaseManager } from './database/DatabaseManager';
import {
DbRefreshStateReferencesRow,
@@ -92,7 +93,7 @@ describe('Stitcher', () => {
},
],
status: {
'backstage.io/catalog-processing': {},
[ENTITY_STATUS_CATALOG_PROCESSING_KEY]: { status: 'ok' },
},
apiVersion: 'a',
kind: 'k',
@@ -175,7 +176,7 @@ describe('Stitcher', () => {
},
]),
status: {
'backstage.io/catalog-processing': {},
[ENTITY_STATUS_CATALOG_PROCESSING_KEY]: { status: 'ok' },
},
apiVersion: 'a',
kind: 'k',
+14 -10
View File
@@ -14,9 +14,12 @@
* limitations under the License.
*/
import {
ENTITY_STATUS_CATALOG_PROCESSING_KEY,
UNSTABLE_CatalogProcessingStatusItem,
} from '@backstage/catalog-client';
import { Entity, parseEntityRef } from '@backstage/catalog-model';
import { JsonObject } from '@backstage/config';
import { ConflictError } from '@backstage/errors';
import { ConflictError, SerializedError } from '@backstage/errors';
import { createHash } from 'crypto';
import stableStringify from 'fast-json-stable-stringify';
import { Knex } from 'knex';
@@ -36,10 +39,6 @@ export type DbFinalEntitiesRow = {
final_entity: string;
};
type ProcessingStatus = {
errors?: JsonObject[];
};
function generateStableHash(entity: Entity) {
return createHash('sha1')
.update(stableStringify({ ...entity }))
@@ -139,7 +138,7 @@ export class Stitcher {
// it
const entity = JSON.parse(processedEntity) as Entity;
const isOrphan = Number(incomingReferenceCount) === 0;
const processingStatus: ProcessingStatus = {};
let statusItems: UNSTABLE_CatalogProcessingStatusItem[] = [];
if (isOrphan) {
this.logger.debug(`${entityRef} is an orphan`);
@@ -149,9 +148,12 @@ export class Stitcher {
};
}
if (errors) {
const parsedErrors = JSON.parse(errors);
const parsedErrors = JSON.parse(errors) as SerializedError[];
if (Array.isArray(parsedErrors) && parsedErrors.length) {
processingStatus.errors = parsedErrors;
statusItems = parsedErrors.map(e => ({
status: 'error',
error: e,
}));
}
}
@@ -165,7 +167,9 @@ export class Stitcher {
}));
entity.status = {
...entity.status,
'backstage.io/catalog-processing': processingStatus,
[ENTITY_STATUS_CATALOG_PROCESSING_KEY]: statusItems.length
? { status: 'error', items: statusItems }
: { status: 'ok' },
};
// If the output entity was actually not changed, just abort