errors: add toError utility and migrate assertError usages
Add a `toError` utility function to `@backstage/errors` that converts unknown values to `ErrorLike` objects. If the value is already error-like it is returned as-is. Strings are used directly as the error message, and other values are stringified with a fallback to JSON.stringify to avoid unhelpful `[object Object]` messages. Non-error causes passed to `CustomErrorBase` are now converted and stored using `toError` rather than discarded. Existing `assertError` call sites across the codebase are migrated to `toError`. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
import { isChildPath, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { NotAllowedError } from '@backstage/errors';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError, NotAllowedError, toError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { SpawnOptionsWithoutStdio, spawn } from 'node:child_process';
|
||||
import fs from 'fs-extra';
|
||||
@@ -442,8 +441,9 @@ export const createOrUpdateMetadata = async (
|
||||
try {
|
||||
json = await fs.readJson(techdocsMetadataPath);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
const message = `Invalid JSON at ${techdocsMetadataPath} with error ${err.message}`;
|
||||
const message = `Invalid JSON at ${techdocsMetadataPath} with error ${
|
||||
toError(err).message
|
||||
}`;
|
||||
logger.error(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
@@ -457,9 +457,10 @@ export const createOrUpdateMetadata = async (
|
||||
file.replace(`${techdocsMetadataDir}${path.sep}`, ''),
|
||||
);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
json.files = [];
|
||||
logger.warn(`Unable to add files list to metadata: ${err.message}`);
|
||||
logger.warn(
|
||||
`Unable to add files list to metadata: ${toError(err).message}`,
|
||||
);
|
||||
}
|
||||
|
||||
await fs.writeJson(techdocsMetadataPath, json);
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
getRepoUrlFromLocationAnnotation,
|
||||
MKDOCS_SCHEMA,
|
||||
} from './helpers';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
@@ -45,9 +45,10 @@ const patchMkdocsFile = async (
|
||||
try {
|
||||
mkdocsYmlFileString = await fs.readFile(mkdocsYmlPath, 'utf8');
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
logger.warn(
|
||||
`Could not read MkDocs YAML config file ${mkdocsYmlPath} before running the generator: ${error.message}`,
|
||||
`Could not read MkDocs YAML config file ${mkdocsYmlPath} before running the generator: ${
|
||||
toError(error).message
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -62,9 +63,10 @@ const patchMkdocsFile = async (
|
||||
throw new Error('Bad YAML format.');
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
logger.warn(
|
||||
`Error in parsing YAML at ${mkdocsYmlPath} before running the generator. ${error.message}`,
|
||||
`Error in parsing YAML at ${mkdocsYmlPath} before running the generator. ${
|
||||
toError(error).message
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -80,9 +82,10 @@ const patchMkdocsFile = async (
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
logger.warn(
|
||||
`Could not write to ${mkdocsYmlPath} after updating it before running the generator. ${error.message}`,
|
||||
`Could not write to ${mkdocsYmlPath} after updating it before running the generator. ${
|
||||
toError(error).message
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { getDocFilesFromRepository } from '../../helpers';
|
||||
import {
|
||||
@@ -62,13 +62,13 @@ export class UrlPreparer implements PreparerBase {
|
||||
logger: this.logger,
|
||||
});
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
// NotModifiedError means that etag based cache is still valid.
|
||||
if (error.name === 'NotModifiedError') {
|
||||
if (err.name === 'NotModifiedError') {
|
||||
this.logger.debug(`Cache is valid for etag ${options?.etag}`);
|
||||
} else {
|
||||
this.logger.debug(
|
||||
`Unable to fetch files for building docs ${error.message}`,
|
||||
`Unable to fetch files for building docs ${err.message}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError, toError } from '@backstage/errors';
|
||||
|
||||
// Maximum size in bytes for a single upload part (5MB)
|
||||
const MAX_SINGLE_UPLOAD_BYTES = 5 * 1024 * 1024;
|
||||
@@ -491,9 +491,10 @@ export class AwsS3Publish implements PublisherBase {
|
||||
.map(f => f.Key || '')
|
||||
.filter(f => !!f);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${e.message}`,
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -708,9 +709,8 @@ export class AwsS3Publish implements PublisherBase {
|
||||
|
||||
resolve(techdocsMetadata);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(err.message);
|
||||
reject(new Error(err.message));
|
||||
this.logger.error(toError(err).message);
|
||||
reject(new Error(toError(err).message));
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -769,9 +769,10 @@ export class AwsS3Publish implements PublisherBase {
|
||||
})
|
||||
.pipe(res);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.warn(
|
||||
`TechDocs S3 router failed to serve static files from bucket ${this.bucketName} at key ${filePath}: ${err.message}`,
|
||||
`TechDocs S3 router failed to serve static files from bucket ${
|
||||
this.bucketName
|
||||
} at key ${filePath}: ${toError(err).message}`,
|
||||
);
|
||||
res.status(404).send('File Not Found');
|
||||
}
|
||||
@@ -823,8 +824,7 @@ export class AwsS3Publish implements PublisherBase {
|
||||
try {
|
||||
newPath = lowerCaseEntityTripletInStoragePath(file);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
this.logger.warn(toError(e).message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -852,8 +852,9 @@ export class AwsS3Publish implements PublisherBase {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Unable to migrate ${file}: ${e.message}`);
|
||||
this.logger.warn(
|
||||
`Unable to migrate ${file}: ${toError(e).message}`,
|
||||
);
|
||||
}
|
||||
}, f),
|
||||
),
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '@azure/storage-blob';
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError, toError } from '@backstage/errors';
|
||||
import express from 'express';
|
||||
import JSON5 from 'json5';
|
||||
import limiterFactory from 'p-limit';
|
||||
@@ -152,8 +152,9 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(`from Azure Blob Storage client library: ${e.message}`);
|
||||
this.logger.error(
|
||||
`from Azure Blob Storage client library: ${toError(e).message}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.error(
|
||||
@@ -190,9 +191,10 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
maxPageSize: BATCH_CONCURRENCY,
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${e.message}`,
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -421,8 +423,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
try {
|
||||
newPath = lowerCaseEntityTripletInStoragePath(originalPath);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
this.logger.warn(toError(e).message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -431,8 +432,9 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
this.logger.debug(`Migrating ${originalPath}`);
|
||||
await this.renameBlob(originalPath, newPath, removeOriginal);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Unable to migrate ${originalPath}: ${e.message}`);
|
||||
this.logger.warn(
|
||||
`Unable to migrate ${originalPath}: ${toError(e).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import {
|
||||
File,
|
||||
FileExistsResponse,
|
||||
@@ -149,14 +149,13 @@ export class GoogleGCSPublish implements PublisherBase {
|
||||
isAvailable: true,
|
||||
};
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(
|
||||
`Could not retrieve metadata about the GCS bucket ${this.bucketName}. ` +
|
||||
'Make sure the bucket exists. Also make sure that authentication is setup either by explicitly defining ' +
|
||||
'techdocs.publisher.googleGcs.credentials in app config or by using environment variables. ' +
|
||||
'Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage',
|
||||
);
|
||||
this.logger.error(`from GCS client library: ${err.message}`);
|
||||
this.logger.error(`from GCS client library: ${toError(err).message}`);
|
||||
|
||||
return { isAvailable: false };
|
||||
}
|
||||
@@ -186,9 +185,10 @@ export class GoogleGCSPublish implements PublisherBase {
|
||||
);
|
||||
existingFiles = await this.getFilesForFolder(remoteFolder);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${e.message}`,
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { File } from '@google-cloud/storage';
|
||||
import { Writable } from 'node:stream';
|
||||
import { lowerCaseEntityTripletInStoragePath } from '../helpers';
|
||||
@@ -47,8 +47,7 @@ export class MigrateWriteStream extends Writable {
|
||||
try {
|
||||
newFile = lowerCaseEntityTripletInStoragePath(file.name);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
this.logger.warn(toError(e).message);
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
ReadinessResponse,
|
||||
TechDocsMetadata,
|
||||
} from './types';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError, toError } from '@backstage/errors';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
|
||||
@@ -129,8 +129,9 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
isAvailable: false,
|
||||
};
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(`from OpenStack client library: ${err.message}`);
|
||||
this.logger.error(
|
||||
`from OpenStack client library: ${toError(err).message}`,
|
||||
);
|
||||
return {
|
||||
isAvailable: false,
|
||||
};
|
||||
@@ -221,9 +222,8 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
|
||||
resolve(techdocsMetadata);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(err.message);
|
||||
reject(new Error(err.message));
|
||||
this.logger.error(toError(err).message);
|
||||
reject(new Error(toError(err).message));
|
||||
}
|
||||
} else {
|
||||
reject({
|
||||
@@ -264,9 +264,10 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
|
||||
res.send(await streamToBuffer(stream));
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.warn(
|
||||
`TechDocs OpenStack swift router failed to serve content from container ${this.containerName} at path ${filePath}: ${err.message}`,
|
||||
`TechDocs OpenStack swift router failed to serve content from container ${
|
||||
this.containerName
|
||||
} at path ${filePath}: ${toError(err).message}`,
|
||||
);
|
||||
res.status(404).send('File Not Found');
|
||||
}
|
||||
@@ -296,8 +297,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.warn(err.message);
|
||||
this.logger.warn(toError(err).message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -316,8 +316,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
try {
|
||||
newPath = lowerCaseEntityTripletInStoragePath(file);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
this.logger.warn(toError(e).message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,8 +337,9 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
await this.storageClient.delete(this.containerName, file);
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Unable to migrate ${file}: ${e.message}`);
|
||||
this.logger.warn(
|
||||
`Unable to migrate ${file}: ${toError(e).message}`,
|
||||
);
|
||||
}
|
||||
}, f),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user