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:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/errors': minor
|
||||
---
|
||||
|
||||
A new `toError` utility function is now available for converting 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 to avoid unhelpful messages like `[object Object]`. Non-error causes passed to `CustomErrorBase` are now converted and stored using `toError` rather than discarded.
|
||||
@@ -35,7 +35,7 @@ import type {
|
||||
} from '../../../backend-plugin-api/src/wiring/types';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';
|
||||
import { ForwardedError, ConflictError, assertError } from '@backstage/errors';
|
||||
import { ConflictError, ForwardedError, toError } from '@backstage/errors';
|
||||
import { DependencyGraph } from '../lib/DependencyGraph';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
import { createInitializationResultCollector } from './createInitializationResultCollector';
|
||||
@@ -385,12 +385,8 @@ export class BackendInitializer {
|
||||
await moduleInit.init.func(moduleDeps);
|
||||
resultCollector.onPluginModuleResult(pluginId, moduleId);
|
||||
} catch (error: unknown) {
|
||||
assertError(error);
|
||||
resultCollector.onPluginModuleResult(
|
||||
pluginId,
|
||||
moduleId,
|
||||
error,
|
||||
);
|
||||
const err = toError(error);
|
||||
resultCollector.onPluginModuleResult(pluginId, moduleId, err);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -414,8 +410,8 @@ export class BackendInitializer {
|
||||
const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);
|
||||
await lifecycleService.startup();
|
||||
} catch (error: unknown) {
|
||||
assertError(error);
|
||||
resultCollector.onPluginResult(pluginId, error);
|
||||
const err = toError(error);
|
||||
resultCollector.onPluginResult(pluginId, err);
|
||||
}
|
||||
}),
|
||||
).catch(error => {
|
||||
@@ -515,19 +511,19 @@ export class BackendInitializer {
|
||||
throw new Error(`Invalid registration type '${(r as any).type}'`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
// Clean up partially registered extension points
|
||||
for (const id of addedExtensionPointIds) {
|
||||
this.#extensionPoints.delete(id);
|
||||
}
|
||||
if ('pluginId' in r && 'moduleId' in r) {
|
||||
resultCollector.onPluginModuleResult(r.pluginId, r.moduleId, error);
|
||||
resultCollector.onPluginModuleResult(r.pluginId, r.moduleId, err);
|
||||
} else if ('pluginId' in r) {
|
||||
pluginInits.delete(r.pluginId);
|
||||
moduleInits.delete(r.pluginId);
|
||||
resultCollector.onPluginResult(r.pluginId, error);
|
||||
resultCollector.onPluginResult(r.pluginId, err);
|
||||
} else {
|
||||
throw error;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { decodeJwt, importJWK, SignJWT, decodeProtectedHeader } from 'jose';
|
||||
import { assertError, AuthenticationError } from '@backstage/errors';
|
||||
import { AuthenticationError, toError } from '@backstage/errors';
|
||||
import { jwtVerify } from 'jose';
|
||||
import { tokenTypes } from '@backstage/plugin-auth-node';
|
||||
import { JwksClient } from '../JwksClient';
|
||||
@@ -210,8 +210,10 @@ export class DefaultPluginTokenHandler implements PluginTokenHandler {
|
||||
this.supportedTargetPlugins.add(targetPluginId);
|
||||
return true;
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
this.logger.error('Unexpected failure for target JWKS check', error);
|
||||
this.logger.error(
|
||||
'Unexpected failure for target JWKS check',
|
||||
toError(error),
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
this.targetPluginInflightChecks.delete(targetPluginId);
|
||||
|
||||
+3
-6
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { isError, toError } from '@backstage/errors';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
function handleBadError(error: Error, logger: LoggerService) {
|
||||
@@ -37,11 +37,8 @@ export function applyInternalErrorFilter(
|
||||
error: unknown,
|
||||
logger: LoggerService,
|
||||
): Error {
|
||||
try {
|
||||
assertError(error);
|
||||
} catch (assertionError: unknown) {
|
||||
assertError(assertionError);
|
||||
return handleBadError(assertionError, logger);
|
||||
if (!isError(error)) {
|
||||
return handleBadError(toError(error), logger);
|
||||
}
|
||||
|
||||
const constructorName = error.constructor.name;
|
||||
|
||||
@@ -32,11 +32,7 @@ import {
|
||||
AwsCodeCommitIntegration,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
assertError,
|
||||
ForwardedError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, ForwardedError, NotModifiedError } from '@backstage/errors';
|
||||
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
|
||||
import {
|
||||
CodeCommitClient,
|
||||
@@ -418,8 +414,8 @@ export class AwsCodeCommitUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -33,11 +33,7 @@ import {
|
||||
ScmIntegrations,
|
||||
AwsS3IntegrationConfig,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
assertError,
|
||||
ForwardedError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, ForwardedError, NotModifiedError } from '@backstage/errors';
|
||||
import { fromTemporaryCredentials } from '@aws-sdk/credential-providers';
|
||||
import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
|
||||
import {
|
||||
@@ -400,8 +396,8 @@ export class AwsS3UrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
+3
-8
@@ -21,11 +21,7 @@ import {
|
||||
StorageSharedKeyCredential,
|
||||
} from '@azure/storage-blob';
|
||||
import { ReaderFactory, ReadTreeResponseFactory } from './types';
|
||||
import {
|
||||
assertError,
|
||||
ForwardedError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, ForwardedError, NotModifiedError } from '@backstage/errors';
|
||||
import { Readable } from 'node:stream';
|
||||
import { relative } from 'node:path/posix';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
@@ -261,9 +257,8 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
throw error;
|
||||
} catch (e) {
|
||||
throw toError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,7 @@ import {
|
||||
} from '@backstage/integration';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Minimatch } from 'minimatch';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import { ReadTreeResponseFactory, ReaderFactory } from './types';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
|
||||
@@ -212,8 +208,8 @@ export class AzureUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -23,11 +23,7 @@ import {
|
||||
UrlReaderServiceSearchOptions,
|
||||
UrlReaderServiceSearchResponse,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import {
|
||||
BitbucketCloudIntegration,
|
||||
getBitbucketCloudDefaultBranch,
|
||||
@@ -196,8 +192,8 @@ export class BitbucketCloudUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -23,11 +23,7 @@ import {
|
||||
UrlReaderServiceSearchOptions,
|
||||
UrlReaderServiceSearchResponse,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import {
|
||||
BitbucketServerIntegration,
|
||||
getBitbucketServerDownloadUrl,
|
||||
@@ -180,8 +176,8 @@ export class BitbucketServerUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -22,11 +22,7 @@ import {
|
||||
UrlReaderServiceSearchOptions,
|
||||
UrlReaderServiceSearchResponse,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import { ReaderFactory } from './types';
|
||||
import path from 'node:path';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
@@ -236,8 +232,8 @@ export class FetchUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
ResponseError,
|
||||
assertError,
|
||||
toError,
|
||||
} from '@backstage/errors';
|
||||
import { ReadTreeResponseFactory, ReaderFactory } from './types';
|
||||
import { Minimatch } from 'minimatch';
|
||||
@@ -174,8 +174,8 @@ export class GerritUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
import { ReaderFactory, ReadTreeResponseFactory } from './types';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
import {
|
||||
assertError,
|
||||
toError,
|
||||
AuthenticationError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
@@ -189,8 +189,8 @@ export class GiteaUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -37,11 +37,7 @@ import fetch, { RequestInit, Response } from 'node-fetch';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Minimatch } from 'minimatch';
|
||||
import { Readable } from 'node:stream';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import { ReadTreeResponseFactory, ReaderFactory } from './types';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
import { parseLastModified } from './util';
|
||||
@@ -209,8 +205,8 @@ export class GithubUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -23,11 +23,7 @@ import {
|
||||
UrlReaderServiceSearchOptions,
|
||||
UrlReaderServiceSearchResponse,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
assertError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
} from '@backstage/errors';
|
||||
import { toError, NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import {
|
||||
getGitLabFileFetchUrl,
|
||||
getGitLabIntegrationRelativePath,
|
||||
@@ -260,8 +256,8 @@ export class GitlabUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
import { Readable } from 'node:stream';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
import packageinfo from '../../../../package.json';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { relative } from 'node:path/posix';
|
||||
|
||||
const GOOGLE_GCS_HOST = 'storage.cloud.google.com';
|
||||
@@ -182,8 +182,8 @@ export class GoogleGcsUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -36,7 +36,7 @@ import { ReadTreeResponseFactory, ReaderFactory } from './types';
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
import {
|
||||
assertError,
|
||||
toError,
|
||||
AuthenticationError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
@@ -187,8 +187,8 @@ export class HarnessUrlReader implements UrlReaderService {
|
||||
],
|
||||
etag: data.etag ?? '',
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
} catch (e) {
|
||||
const error = toError(e);
|
||||
if (error.name === 'NotFoundError') {
|
||||
return {
|
||||
files: [],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { ChildProcess, SpawnOptions } from 'node:child_process';
|
||||
import spawn from 'cross-spawn';
|
||||
import { ExitCodeError } from './errors';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
|
||||
/**
|
||||
* Callback function that can be used to receive stdout or stderr data from a child process.
|
||||
@@ -193,14 +193,14 @@ export async function runOutput(
|
||||
|
||||
return Buffer.concat(stdoutChunks).toString().trim();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
|
||||
(error as Error & { stdout?: string }).stdout =
|
||||
(err as Error & { stdout?: string }).stdout =
|
||||
Buffer.concat(stdoutChunks).toString();
|
||||
(error as Error & { stderr?: string }).stderr =
|
||||
(err as Error & { stderr?: string }).stderr =
|
||||
Buffer.concat(stderrChunks).toString();
|
||||
|
||||
throw error;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError } from '@backstage/errors';
|
||||
import { runOutput } from '@backstage/cli-common';
|
||||
|
||||
const versions = new Map<string, Promise<'classic' | 'berry'>>();
|
||||
@@ -32,7 +32,6 @@ export function detectYarnVersion(dir?: string): Promise<'classic' | 'berry'> {
|
||||
});
|
||||
return stdout.trim().startsWith('1.') ? 'classic' : 'berry';
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
throw new ForwardedError('Failed to determine yarn version', error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { addCodeownersEntry } from '../codeowners';
|
||||
import { Task } from '../tasks';
|
||||
import {
|
||||
@@ -68,9 +68,9 @@ export async function executePortableTemplate(
|
||||
}).waitForExit();
|
||||
});
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
Task.error(
|
||||
`Warning: Failed to execute command '${commandStr}', ${error}`,
|
||||
`Warning: Failed to execute command '${commandStr}', ${err}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,8 +80,8 @@ export async function executePortableTemplate(
|
||||
Task.log(`🎉 Successfully created ${template.name}`);
|
||||
Task.log();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
Task.error(error.message);
|
||||
const err = toError(error);
|
||||
Task.error(err.message);
|
||||
|
||||
if (modified) {
|
||||
Task.log('It seems that something went wrong in the creation process 🤔');
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ForwardedError, toError } from '@backstage/errors';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
import { runOutput } from '@backstage/cli-common';
|
||||
|
||||
@@ -28,13 +28,10 @@ export async function runGit(...args: string[]) {
|
||||
});
|
||||
return stdout.trim().split(/\r\n|\r|\n/);
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
if (
|
||||
'code' in error &&
|
||||
typeof (error as { code?: number }).code === 'number'
|
||||
) {
|
||||
const code = (error as { code?: number }).code;
|
||||
const stderr = (error as { stderr?: string }).stderr;
|
||||
const err = toError(error);
|
||||
if ('code' in err && typeof (err as { code?: number }).code === 'number') {
|
||||
const code = (err as { code?: number }).code;
|
||||
const stderr = (err as { stderr?: string }).stderr;
|
||||
const msg = stderr?.trim() ?? `with exit code ${code}`;
|
||||
throw new Error(`git ${args[0]} failed, ${msg}`);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
assertError,
|
||||
ForwardedError,
|
||||
NotImplementedError,
|
||||
} from '@backstage/errors';
|
||||
import { ForwardedError, NotImplementedError } from '@backstage/errors';
|
||||
import { PackageInfo, PackageManager } from '../PackageManager';
|
||||
import { Lockfile } from '../Lockfile';
|
||||
import { YarnVersion } from './types';
|
||||
@@ -96,7 +92,6 @@ function detectYarnVersion(dir?: string): Promise<YarnVersion> {
|
||||
: 'berry';
|
||||
return { version: versionString, codename };
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
throw new ForwardedError('Failed to determine yarn version', error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { exitWithError } from './errors';
|
||||
|
||||
type ActionFunc = (...args: any[]) => Promise<void>;
|
||||
@@ -40,8 +40,7 @@ export function lazy<TModule extends object>(
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
exitWithError(error);
|
||||
exitWithError(toError(error));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from 'node:path';
|
||||
import { ConfigSchemaPackageEntry } from './types';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
|
||||
type Item = {
|
||||
name?: string;
|
||||
@@ -249,9 +249,9 @@ async function compileTsSchemas(
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
if (error.message !== 'type Config not found') {
|
||||
throw error;
|
||||
const err = toError(error);
|
||||
if (err.message !== 'type Config not found') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { AppConfig } from '@backstage/config';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { AsyncConfigSourceGenerator, ConfigSource } from './types';
|
||||
|
||||
@@ -165,7 +165,6 @@ function safeJsonParse(str: string): [Error | null, any] {
|
||||
try {
|
||||
return [null, JSON.parse(str)];
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
return [err, str];
|
||||
return [toError(err), str];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { TransformContext, TransformFunc } from './types';
|
||||
import { isObject } from './utils';
|
||||
import { createSubstitutionTransform } from './substitution';
|
||||
@@ -50,8 +50,7 @@ export async function applyConfigTransforms(
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
throw new Error(`error at ${path}, ${error.message}`);
|
||||
throw new Error(`error at ${path}, ${toError(error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { createElement, isValidElement, useState } from 'react';
|
||||
import { isError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import {
|
||||
configApiRef,
|
||||
IconComponent,
|
||||
@@ -63,7 +63,7 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => {
|
||||
try {
|
||||
await request.trigger();
|
||||
} catch (e) {
|
||||
setError(isError(e) ? e.message : 'An unspecified error occurred');
|
||||
setError(toError(e).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
@@ -155,4 +155,7 @@ export class ServiceUnavailableError extends CustomErrorBase {}
|
||||
|
||||
// @public
|
||||
export function stringifyError(error: unknown): string;
|
||||
|
||||
// @public
|
||||
export function toError(value: unknown): ErrorLike;
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { stringifyError } from '../serialization/error';
|
||||
import { isError } from './assertion';
|
||||
import { toError } from './assertion';
|
||||
|
||||
/**
|
||||
* A base class that custom Error classes can inherit from.
|
||||
@@ -63,6 +63,6 @@ export class CustomErrorBase extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
this.cause = isError(cause) ? cause : undefined;
|
||||
this.cause = cause !== undefined ? toError(cause) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError, isError } from './assertion';
|
||||
import { assertError, isError, toError } from './assertion';
|
||||
import { NotFoundError } from './common';
|
||||
import { CustomErrorBase } from './CustomErrorBase';
|
||||
|
||||
@@ -77,3 +77,85 @@ describe('isError', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('toError', () => {
|
||||
it.each(areErrors)(
|
||||
'should pass through error-like values as-is %#',
|
||||
error => {
|
||||
expect(toError(error)).toBe(error);
|
||||
},
|
||||
);
|
||||
|
||||
it('should preserve the original error instance', () => {
|
||||
const original = new NotFoundError('not found');
|
||||
expect(toError(original)).toBe(original);
|
||||
});
|
||||
|
||||
it('should use strings directly as the error message', () => {
|
||||
const result = toError('something went wrong');
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe('something went wrong');
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
const result = toError('');
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe('');
|
||||
});
|
||||
|
||||
it('should wrap undefined', () => {
|
||||
const result = toError(undefined);
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe("unknown error 'undefined'");
|
||||
});
|
||||
|
||||
it('should wrap null', () => {
|
||||
const result = toError(null);
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe("unknown error 'null'");
|
||||
});
|
||||
|
||||
it('should wrap numbers', () => {
|
||||
expect(toError(0).message).toBe("unknown error '0'");
|
||||
expect(toError(42).message).toBe("unknown error '42'");
|
||||
});
|
||||
|
||||
it('should wrap booleans', () => {
|
||||
expect(toError(false).message).toBe("unknown error 'false'");
|
||||
expect(toError(true).message).toBe("unknown error 'true'");
|
||||
});
|
||||
|
||||
it('should wrap plain objects using JSON when toString is unhelpful', () => {
|
||||
expect(toError({ name: 'e' }).message).toBe(`unknown error '{"name":"e"}'`);
|
||||
expect(toError({ message: '' }).message).toBe(
|
||||
`unknown error '{"message":""}'`,
|
||||
);
|
||||
expect(toError({ code: 404, detail: 'missing' }).message).toBe(
|
||||
`unknown error '{"code":404,"detail":"missing"}'`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should fall back to [object Object] for empty plain objects', () => {
|
||||
expect(toError({}).message).toBe("unknown error '[object Object]'");
|
||||
});
|
||||
|
||||
it('should wrap arrays', () => {
|
||||
expect(toError([]).message).toBe("unknown error ''");
|
||||
expect(toError([1, 2]).message).toBe("unknown error '1,2'");
|
||||
});
|
||||
|
||||
it('should handle objects with a custom toString', () => {
|
||||
const obj = { toString: () => 'custom string' };
|
||||
expect(toError(obj).message).toBe("unknown error 'custom string'");
|
||||
});
|
||||
|
||||
it('should handle symbols', () => {
|
||||
const result = toError(Symbol('test'));
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe("unknown error 'Symbol(test)'");
|
||||
});
|
||||
|
||||
it('should handle BigInt', () => {
|
||||
expect(toError(BigInt(42)).message).toBe("unknown error '42'");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,3 +71,30 @@ export function assertError(value: unknown): asserts value is ErrorLike {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an unknown value to an {@link ErrorLike} object.
|
||||
*
|
||||
* If the value is already an {@link ErrorLike} object, it is returned as-is. Otherwise, a new
|
||||
* `Error` is created with the value stringified as the message.
|
||||
*
|
||||
* @public
|
||||
* @param value - an unknown value
|
||||
* @returns an {@link ErrorLike} object
|
||||
*/
|
||||
export function toError(value: unknown): ErrorLike {
|
||||
if (isError(value)) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return new Error(value) as ErrorLike;
|
||||
}
|
||||
const str = String(value);
|
||||
if (str === '[object Object]') {
|
||||
const json = JSON.stringify(value);
|
||||
if (json !== '{}') {
|
||||
return new Error(`unknown error '${json}'`) as ErrorLike;
|
||||
}
|
||||
}
|
||||
return new Error(`unknown error '${str}'`) as ErrorLike;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { assertError, isError } from './assertion';
|
||||
export { assertError, isError, toError } from './assertion';
|
||||
export type { ErrorLike } from './assertion';
|
||||
export {
|
||||
AuthenticationError,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { Command } from 'commander';
|
||||
import { exitWithError } from '../lib/errors';
|
||||
|
||||
@@ -302,8 +302,7 @@ export function lazy<TModule extends object>(
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
exitWithError(error);
|
||||
exitWithError(toError(error));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
WriteResult,
|
||||
} from '@google-cloud/firestore';
|
||||
|
||||
import { toError } from '@backstage/errors';
|
||||
import { AnyJWK, KeyStore, StoredKey } from './types';
|
||||
|
||||
export type FirestoreKeyStoreSettings = Settings & Options;
|
||||
@@ -66,14 +67,11 @@ export class FirestoreKeyStore implements KeyStore {
|
||||
try {
|
||||
await keyStore.verify();
|
||||
} catch (error) {
|
||||
const err = toError(error);
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
throw new Error(
|
||||
`Failed to connect to database: ${(error as Error).message}`,
|
||||
);
|
||||
throw new Error(`Failed to connect to database: ${err.message}`);
|
||||
}
|
||||
logger?.warn(
|
||||
`Failed to connect to database: ${(error as Error).message}`,
|
||||
);
|
||||
logger?.warn(`Failed to connect to database: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, NotFoundError } from '@backstage/errors';
|
||||
import { NotFoundError, toError } from '@backstage/errors';
|
||||
import {
|
||||
AuthOwnershipResolver,
|
||||
AuthProviderFactory,
|
||||
@@ -104,14 +104,17 @@ export function bindProviderRouters(
|
||||
|
||||
targetRouter.use(`/${providerId}`, r);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
throw new Error(
|
||||
`Failed to initialize ${providerId} auth provider, ${e.message}`,
|
||||
`Failed to initialize ${providerId} auth provider, ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
logger.warn(`Skipping ${providerId} auth provider, ${e.message}`);
|
||||
logger.warn(
|
||||
`Skipping ${providerId} auth provider, ${toError(e).message}`,
|
||||
);
|
||||
|
||||
targetRouter.use(`/${providerId}`, () => {
|
||||
// If the user added the provider under auth.providers but the clientId and clientSecret etc. were not found.
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import { useCallback } from 'react';
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
import useAsyncFn from 'react-use/esm/useAsyncFn';
|
||||
import { isError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
|
||||
interface Session {
|
||||
id: string;
|
||||
@@ -111,9 +111,7 @@ export const useConsentSession = (opts: { sessionId?: string }) => {
|
||||
if (sessionState.error) {
|
||||
return {
|
||||
status: 'error',
|
||||
error: isError(sessionState.error)
|
||||
? sessionState.error.message
|
||||
: 'Failed to load consent request',
|
||||
error: toError(sessionState.error).message,
|
||||
};
|
||||
}
|
||||
if (sessionState.value) {
|
||||
@@ -133,7 +131,7 @@ export const useConsentSession = (opts: { sessionId?: string }) => {
|
||||
await handleActionInternal(action, state.session);
|
||||
} catch (err) {
|
||||
alertApi.post({
|
||||
message: isError(err) ? err.message : `Failed to ${action} consent`,
|
||||
message: toError(err).message,
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
+4
-9
@@ -21,7 +21,7 @@ import { IncrementalIngestionDatabaseManager } from '../database/IncrementalInge
|
||||
import { performance } from 'node:perf_hooks';
|
||||
import { Duration } from 'luxon';
|
||||
import { v4 } from 'uuid';
|
||||
import { stringifyError } from '@backstage/errors';
|
||||
import { stringifyError, toError } from '@backstage/errors';
|
||||
import { EventParams } from '@backstage/plugin-events-node';
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
|
||||
@@ -123,17 +123,12 @@ export class IncrementalIngestionEngine implements IterationEngine {
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
(error as Error).message &&
|
||||
(error as Error).message === 'CANCEL'
|
||||
) {
|
||||
const err = toError(error);
|
||||
if (err.message === 'CANCEL') {
|
||||
this.options.logger.info(
|
||||
`incremental-engine: Ingestion '${ingestionId}' canceled`,
|
||||
);
|
||||
await this.manager.setProviderCanceling(
|
||||
ingestionId,
|
||||
(error as Error).message,
|
||||
);
|
||||
await this.manager.setProviderCanceling(ingestionId, err.message);
|
||||
} else {
|
||||
const currentBackoff = Duration.fromObject(
|
||||
this.backoff[Math.min(this.backoff.length - 1, attempts)],
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Entity,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { assertError, serializeError, stringifyError } from '@backstage/errors';
|
||||
import { serializeError, stringifyError, toError } from '@backstage/errors';
|
||||
import { Hash } from 'node:crypto';
|
||||
import stableStringify from 'fast-json-stable-stringify';
|
||||
import { Knex } from 'knex';
|
||||
@@ -344,8 +344,7 @@ export class DefaultCatalogProcessingEngine {
|
||||
|
||||
track.markSuccessfulWithChanges();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
track.markFailed(error);
|
||||
track.markFailed(toError(error));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
stringifyLocationRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
assertError,
|
||||
ConflictError,
|
||||
InputError,
|
||||
NotAllowedError,
|
||||
toError,
|
||||
} from '@backstage/errors';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
@@ -192,10 +192,10 @@ export class DefaultCatalogProcessingOrchestrator
|
||||
ok: collectorResults.errors.length === 0,
|
||||
};
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
return {
|
||||
ok: false,
|
||||
errors: collector.results().errors.concat(error),
|
||||
errors: collector.results().errors.concat(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
stringifyLocationRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorResult,
|
||||
@@ -97,9 +97,11 @@ export class ProcessorOutputCollector {
|
||||
try {
|
||||
entity = validateEntityEnvelope(i.entity);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
logger.debug(`Envelope validation failed at ${location}, ${e}`);
|
||||
this.errors.push(e);
|
||||
const validationError = toError(e);
|
||||
logger.debug(
|
||||
`Envelope validation failed at ${location}, ${validationError}`,
|
||||
);
|
||||
this.errors.push(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import limiterFactory, { Limit } from 'p-limit';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
import {
|
||||
@@ -109,18 +109,18 @@ export class UrlReaderProcessor implements CatalogProcessor {
|
||||
|
||||
emit(processingResult.refresh(`${location.type}:${location.target}`));
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const message = `Unable to read ${location.type}, ${error}`.substring(
|
||||
const err = toError(error);
|
||||
const message = `Unable to read ${location.type}, ${err}`.substring(
|
||||
0,
|
||||
5000,
|
||||
);
|
||||
if (error.name === 'NotModifiedError' && cacheItem) {
|
||||
if (err.name === 'NotModifiedError' && cacheItem) {
|
||||
for (const parseResult of cacheItem.value) {
|
||||
emit(parseResult);
|
||||
}
|
||||
emit(processingResult.refresh(`${location.type}:${location.target}`));
|
||||
await cache.set(CACHE_KEY, cacheItem);
|
||||
} else if (error.name === 'NotFoundError') {
|
||||
} else if (err.name === 'NotFoundError') {
|
||||
if (!optional) {
|
||||
emit(processingResult.notFoundError(location, message));
|
||||
}
|
||||
|
||||
+2
-3
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { useTranslationRef } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
catalogApiRef,
|
||||
@@ -210,8 +210,7 @@ export const StepPrepareCreatePullRequest = (
|
||||
{ notRepeatable: true },
|
||||
);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
setError(e.message);
|
||||
setError(toError(e).message);
|
||||
setSubmitted(false);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { configApiRef, useAnalytics, useApi } from '@backstage/core-plugin-api';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { useTranslationRef } from '@backstage/frontend-plugin-api';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
@@ -94,12 +94,12 @@ export const StepReviewLocation = ({
|
||||
locations,
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const caughtError = toError(e);
|
||||
// TODO: this error should be handled differently. We add it as 'optional' and
|
||||
// it is not uncommon that a PR has not been merged yet.
|
||||
if (
|
||||
prepareResult.type === 'repository' &&
|
||||
e.message.startsWith(
|
||||
caughtError.message.startsWith(
|
||||
'Location was added but has no entities specified yet',
|
||||
)
|
||||
) {
|
||||
@@ -111,7 +111,7 @@ export const StepReviewLocation = ({
|
||||
})),
|
||||
});
|
||||
} else {
|
||||
setError(e.message);
|
||||
setError(caughtError.message);
|
||||
setSubmitted(false);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -25,7 +25,7 @@ import {
|
||||
|
||||
import { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { Progress, ResponseErrorPanel } from '@backstage/core-components';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { catalogReactTranslationRef } from '../../translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import {
|
||||
@@ -75,8 +75,7 @@ function useUnregisterDialogHandlers(
|
||||
await state.unregisterLocation();
|
||||
onConfirm();
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
alertApi.post({ message: err.message });
|
||||
alertApi.post({ message: toError(err).message });
|
||||
} finally {
|
||||
setBusyAction(null);
|
||||
}
|
||||
@@ -98,8 +97,7 @@ function useUnregisterDialogHandlers(
|
||||
display: 'transient',
|
||||
});
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
alertApi.post({ message: err.message });
|
||||
alertApi.post({ message: toError(err).message });
|
||||
} finally {
|
||||
setBusyAction(null);
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import { useState } from 'react';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
|
||||
interface DeleteEntityConfirmationProps {
|
||||
open: boolean;
|
||||
@@ -37,7 +37,7 @@ export function DeleteEntityConfirmationDialog(
|
||||
try {
|
||||
onConfirm();
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
void toError(err);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { useState } from 'react';
|
||||
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { catalogTranslationRef } from '../../alpha/translation';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { Button, Dialog, DialogFooter, DialogHeader } from '@backstage/ui';
|
||||
@@ -44,8 +44,7 @@ export function DeleteEntityDialog(props: DeleteEntityDialogProps) {
|
||||
await catalogApi.removeEntityByUid(uid!);
|
||||
onConfirm();
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
alertApi.post({ message: err.message });
|
||||
alertApi.post({ message: toError(err).message });
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import os from 'node:os';
|
||||
import fs from 'fs-extra';
|
||||
import { Lockfile } from '../util/Lockfile';
|
||||
import { memoize } from 'lodash';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/** @public */
|
||||
@@ -179,7 +179,7 @@ export class DevToolsBackendApi {
|
||||
const data = ConfigReader.fromConfigs(sanitizedConfigs).get();
|
||||
configInfo.config = data;
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
// The config is not valid for some reason but we want to be able to see it still
|
||||
const config = {
|
||||
data: this.config.get() as JsonObject,
|
||||
@@ -194,10 +194,10 @@ export class DevToolsBackendApi {
|
||||
const data = ConfigReader.fromConfigs(sanitizedConfigs).get();
|
||||
configInfo.config = data;
|
||||
configInfo.error = {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
messages: error.messages as string[] | undefined,
|
||||
stack: error.stack,
|
||||
name: err.name,
|
||||
message: err.message,
|
||||
messages: err.messages as string[] | undefined,
|
||||
stack: err.stack,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { McpService } from '../services/McpService';
|
||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
||||
import { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { isError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { MetricsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { bucketBoundaries, McpServerSessionAttributes } from '../metrics';
|
||||
import { McpServerConfig } from '../config';
|
||||
@@ -83,11 +83,10 @@ export const createStreamableRouter = ({
|
||||
sessionDuration.record(durationSeconds, baseAttributes);
|
||||
});
|
||||
} catch (error) {
|
||||
const errorType = isError(error) ? error.name : 'Error';
|
||||
const err = toError(error);
|
||||
const errorType = err.name;
|
||||
|
||||
if (isError(error)) {
|
||||
logger.error(error.message);
|
||||
}
|
||||
logger.error(err.message);
|
||||
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config, readDurationFromConfig } from '@backstage/config';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import { NotFoundError, toError } from '@backstage/errors';
|
||||
import { Notification } from '@backstage/plugin-notifications-common';
|
||||
import {
|
||||
NotificationProcessor,
|
||||
@@ -238,9 +238,7 @@ export class SlackNotificationProcessor implements NotificationProcessor {
|
||||
channel = await this.getSlackNotificationTarget(entityRef);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to get Slack channel for entity: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
`Failed to get Slack channel for entity: ${toError(error).message}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { Octokit } from 'octokit';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
@@ -109,9 +109,9 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const error = toError(e);
|
||||
if (
|
||||
e.message.includes(
|
||||
error.message.includes(
|
||||
'Upgrade to GitHub Pro or make this repository public to enable this feature',
|
||||
)
|
||||
) {
|
||||
@@ -119,7 +119,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
|
||||
'Branch protection was not enabled as it requires GitHub Pro for private repositories',
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError, InputError } from '@backstage/errors';
|
||||
import { InputError, toError } from '@backstage/errors';
|
||||
import {
|
||||
GithubCredentialsProvider,
|
||||
ScmIntegrations,
|
||||
@@ -175,11 +175,11 @@ export function createGithubActionsDispatchAction(options: {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const error = toError(e);
|
||||
ctx.logger.warn(
|
||||
`Failed: dispatching workflow '${workflowId}' on repo: '${repo}', ${e.message}`,
|
||||
`Failed: dispatching workflow '${workflowId}' on repo: '${repo}', ${error.message}`,
|
||||
);
|
||||
throw e;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
createTemplateAction,
|
||||
parseRepoUrl,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { assertError, InputError } from '@backstage/errors';
|
||||
import { InputError, toError } from '@backstage/errors';
|
||||
import { Octokit } from 'octokit';
|
||||
import { getOctokitOptions } from '../util';
|
||||
import { examples } from './githubIssuesCreate.examples';
|
||||
@@ -171,11 +171,11 @@ export function createGithubIssuesCreateAction(options: {
|
||||
`Successfully created issue #${issue.number}: ${issue.html_url}`,
|
||||
);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const error = toError(e);
|
||||
ctx.logger.warn(
|
||||
`Failed: creating issue '${title}' on repo: '${repo}', ${e.message}`,
|
||||
`Failed: creating issue '${title}' on repo: '${repo}', ${error.message}`,
|
||||
);
|
||||
throw e;
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
createTemplateAction,
|
||||
parseRepoUrl,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { assertError, InputError } from '@backstage/errors';
|
||||
import { InputError, toError } from '@backstage/errors';
|
||||
import { Octokit } from 'octokit';
|
||||
import { getOctokitOptions } from '../util';
|
||||
import { examples } from './githubIssuesLabel.examples';
|
||||
@@ -101,9 +101,10 @@ export function createGithubIssuesLabelAction(options: {
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
ctx.logger.warn(
|
||||
`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${e.message}`,
|
||||
`Failed: adding labels to issue: '${number}' on repo: '${repo}', ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
parseRepoUrl,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { emitterEventNames } from '@octokit/webhooks';
|
||||
import { assertError, InputError } from '@backstage/errors';
|
||||
import { InputError, toError } from '@backstage/errors';
|
||||
import { Octokit } from 'octokit';
|
||||
import { getOctokitOptions } from '../util';
|
||||
import { examples } from './githubWebhook.examples';
|
||||
@@ -173,9 +173,10 @@ export function createGithubWebhookAction(options: {
|
||||
|
||||
ctx.logger.info(`Webhook '${webhookUrl}' created successfully`);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
ctx.logger.warn(
|
||||
`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`,
|
||||
`Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, NotFoundError } from '@backstage/errors';
|
||||
import { NotFoundError, toError } from '@backstage/errors';
|
||||
import { Octokit } from 'octokit';
|
||||
|
||||
import {
|
||||
@@ -124,14 +124,15 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
try {
|
||||
newRepo = (await repoCreationPromise).data;
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
if (e.message === 'Resource not accessible by integration') {
|
||||
if (toError(e).message === 'Resource not accessible by integration') {
|
||||
logger.warn(
|
||||
`The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`,
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`,
|
||||
`Failed to create the ${user.data.type} repository ${owner}/${repo}, ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -174,10 +175,11 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const name = extractCollaboratorName(collaborator);
|
||||
logger.warn(
|
||||
`Skipping ${collaborator.access} access for ${name}, ${e.message}`,
|
||||
`Skipping ${collaborator.access} access for ${name}, ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -191,8 +193,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
names: topics.map(t => t.toLowerCase()),
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`);
|
||||
logger.warn(`Skipping topics ${topics.join(' ')}, ${toError(e).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,9 +357,10 @@ export async function initRepoPushAndProtect(
|
||||
requiredLinearHistory,
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
logger.warn(
|
||||
`Skipping: default branch protection on '${repo}', ${e.message}`,
|
||||
`Skipping: default branch protection on '${repo}', ${
|
||||
toError(e).message
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -369,8 +371,12 @@ export async function initRepoPushAndProtect(
|
||||
function extractCollaboratorName(
|
||||
collaborator: { user: string } | { team: string } | { username: string },
|
||||
) {
|
||||
if ('username' in collaborator) return collaborator.username;
|
||||
if ('user' in collaborator) return collaborator.user;
|
||||
if ('username' in collaborator) {
|
||||
return collaborator.username;
|
||||
}
|
||||
if ('user' in collaborator) {
|
||||
return collaborator.user;
|
||||
}
|
||||
return collaborator.team;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { AuditorService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import type { MetricsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { assertError, InputError, stringifyError } from '@backstage/errors';
|
||||
import { InputError, stringifyError, toError } from '@backstage/errors';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
@@ -228,12 +228,12 @@ export class TaskWorker {
|
||||
await task.complete('completed', { output });
|
||||
await auditorEvent?.success();
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
const err = toError(error);
|
||||
await auditorEvent?.fail({
|
||||
error,
|
||||
error: err,
|
||||
});
|
||||
await task.complete('failed', {
|
||||
error: { name: error.name, message: error.message },
|
||||
error: { name: err.name, message: err.message },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, InputError, NotFoundError } from '@backstage/errors';
|
||||
import { InputError, NotFoundError, toError } from '@backstage/errors';
|
||||
import { CatalogService } from '@backstage/plugin-catalog-node';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import fs from 'fs-extra';
|
||||
@@ -47,13 +47,13 @@ export async function getWorkingDirectory(
|
||||
await fs.access(workingDirectory, fs.constants.F_OK | fs.constants.W_OK);
|
||||
logger.info(`using working directory: ${workingDirectory}`);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
const error = toError(err);
|
||||
logger.error(
|
||||
`working directory ${workingDirectory} ${
|
||||
err.code === 'ENOENT' ? 'does not exist' : 'is not writable'
|
||||
error.code === 'ENOENT' ? 'does not exist' : 'is not writable'
|
||||
}`,
|
||||
);
|
||||
throw err;
|
||||
throw error;
|
||||
}
|
||||
return workingDirectory;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { Writable } from 'node:stream';
|
||||
|
||||
@@ -66,8 +66,7 @@ export abstract class BatchSearchEngineIndexer extends Writable {
|
||||
await this.initialize();
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +90,7 @@ export abstract class BatchSearchEngineIndexer extends Writable {
|
||||
this.currentBatch = [];
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +108,7 @@ export abstract class BatchSearchEngineIndexer extends Writable {
|
||||
await this.finalize();
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { toError } from '@backstage/errors';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { Transform } from 'node:stream';
|
||||
|
||||
@@ -60,8 +60,7 @@ export abstract class DecoratorBase extends Transform {
|
||||
await this.initialize();
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +95,7 @@ export abstract class DecoratorBase extends Transform {
|
||||
this.push(decorated);
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +108,7 @@ export abstract class DecoratorBase extends Transform {
|
||||
await this.finalize();
|
||||
done();
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
done(e);
|
||||
done(toError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, isError } from '@backstage/errors';
|
||||
import { isError, toError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import {
|
||||
GeneratorBase,
|
||||
@@ -225,9 +225,8 @@ export class DocsBuilder {
|
||||
// Not a blocker hence no need to await this.
|
||||
fs.remove(preparedDir);
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
this.logger.debug(
|
||||
`Error removing prepared directory ${error.message}`,
|
||||
`Error removing prepared directory ${toError(error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -238,9 +237,8 @@ export class DocsBuilder {
|
||||
// Not a blocker hence no need to await this.
|
||||
fs.remove(outputDir);
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
this.logger.debug(
|
||||
`Error removing generated directory ${error.message}`,
|
||||
`Error removing generated directory ${toError(error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { assertError, CustomErrorBase } from '@backstage/errors';
|
||||
import { CustomErrorBase, toError } from '@backstage/errors';
|
||||
import { Config, readDurationFromConfig } from '@backstage/config';
|
||||
import { CacheService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { durationToMilliseconds } from '@backstage/types';
|
||||
@@ -77,9 +77,9 @@ export class TechDocsCache {
|
||||
this.logger.debug(`Cache miss: ${path}`);
|
||||
return response;
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Error getting cache entry ${path}: ${e.message}`);
|
||||
this.logger.debug(e.message, e);
|
||||
const error = toError(e);
|
||||
this.logger.warn(`Error getting cache entry ${path}: ${error.message}`);
|
||||
this.logger.debug(error.message, error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, NotFoundError } from '@backstage/errors';
|
||||
import { NotFoundError, toError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import {
|
||||
GeneratorBuilder,
|
||||
@@ -146,13 +146,13 @@ export class DocsSynchronizer {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
const buildError = toError(e);
|
||||
const msg = `Failed to build the docs page for entity ${stringifyEntityRef(
|
||||
entity,
|
||||
)}: ${e.message}`;
|
||||
)}: ${buildError.message}`;
|
||||
taskLogger.error(msg);
|
||||
this.logger.error(msg, e);
|
||||
error(e);
|
||||
this.logger.error(msg, buildError);
|
||||
error(buildError);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -241,10 +241,9 @@ export class DocsSynchronizer {
|
||||
finish({ updated: false });
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
// In case of error, log and allow the user to go about their business.
|
||||
this.logger.error(
|
||||
`Error syncing cache for ${entityTripletPath}: ${e.message}`,
|
||||
`Error syncing cache for ${entityTripletPath}: ${toError(e).message}`,
|
||||
);
|
||||
finish({ updated: false });
|
||||
} finally {
|
||||
|
||||
@@ -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