diff --git a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts index 20a3724079..724de8271d 100644 --- a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts +++ b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts @@ -50,7 +50,37 @@ export class FieldFormatEntityPolicy implements EntityPolicy { } if (!isValid) { - throw new Error(`${field} "${value}" is not valid`); + let expectation; + switch (validator.name) { + case 'isValidLabelValue': + expectation = + 'a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total'; + break; + case 'isValidLabelKey': + case 'isValidApiVersion': + case 'isValidAnnotationKey': + expectation = 'a valid prefix and/or suffix'; + break; + case 'isValidAnnotationValue': + expectation = 'a string'; + break; + case 'isValidNamespace': + expectation = + 'a string that is sequences of [a-zA-Z0-9] seperated by [-], at most 63 characters in total'; + break; + default: + expectation = undefined; + break; + } + + // ensure that if there are other/future validators, the error message defaults to a general "is not valid, visit link" + const message = expectation + ? ` expected ${expectation} but found "${value}".` + : ''; + + throw new Error( + `"${field}" is not valid;${message} To learn more about catalog file format, visit: https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md`, + ); } } diff --git a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts index 6df6d9b4b7..a644dd48aa 100644 --- a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts +++ b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { InputError } from '@backstage/backend-common'; import { Entity, Location, LocationSpec } from '@backstage/catalog-model'; import { v4 as uuidv4 } from 'uuid'; import { Logger } from 'winston'; @@ -81,9 +80,7 @@ export class HigherOrderOperations implements HigherOrderOperation { const readerOutput = await this.locationReader.read(spec); if (readerOutput.errors.length) { const item = readerOutput.errors[0]; - throw new InputError( - `Failed to read location ${item.location.type}:${item.location.target}, ${item.error}`, - ); + throw item.error; } // TODO(freben): At this point, we could detect orphaned entities, by way diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index e23a4524b6..8fec1541df 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -189,7 +189,7 @@ export class LocationReaders implements LocationReader { ); } catch (e) { const message = `Processor ${processor.constructor.name} threw an error while preprocessing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; - emit(result.generalError(item.location, message)); + emit(result.generalError(item.location, e.message)); logger.warn(message); return undefined; } @@ -207,7 +207,7 @@ export class LocationReaders implements LocationReader { current = next; } catch (e) { const message = `Policy check failed while analyzing entity ${kind}:${namespace}/${name} at ${item.location.type} ${item.location.target}, ${e}`; - emit(result.inputError(item.location, message)); + emit(result.inputError(item.location, e.message)); logger.warn(message); return undefined; }