Merge pull request #7461 from backstage/rugvip/errors
[PRFC] errors: add isError, assertError and ErrorLike type
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
import { isChildPath } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { spawn } from 'child_process';
|
||||
import fs from 'fs-extra';
|
||||
@@ -155,8 +156,9 @@ export const getMkdocsYml = async (
|
||||
mkdocsYmlPath = path.join(inputDir, 'mkdocs.yml');
|
||||
mkdocsYmlFileString = await fs.readFile(mkdocsYmlPath, 'utf8');
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Could not read MkDocs YAML config file mkdocs.yml or mkdocs.yaml for validation: ${error.message}`,
|
||||
throw new ForwardedError(
|
||||
'Could not read MkDocs YAML config file mkdocs.yml or mkdocs.yaml for validation',
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -225,6 +227,7 @@ export const patchMkdocsYmlPreBuild = 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}`,
|
||||
);
|
||||
@@ -241,6 +244,7 @@ export const patchMkdocsYmlPreBuild = 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}`,
|
||||
);
|
||||
@@ -279,6 +283,7 @@ export const patchMkdocsYmlPreBuild = async (
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
logger.warn(
|
||||
`Could not write to ${mkdocsYmlPath} after updating it before running the generator. ${error.message}`,
|
||||
);
|
||||
@@ -307,6 +312,7 @@ export const addBuildTimestampMetadata = async (
|
||||
try {
|
||||
json = await fs.readJson(techdocsMetadataPath);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
const message = `Invalid JSON at ${techdocsMetadataPath} with error ${err.message}`;
|
||||
logger.error(message);
|
||||
throw new Error(message);
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
GeneratorRunInType,
|
||||
GeneratorRunOptions,
|
||||
} from './types';
|
||||
import { ForwardedError } from '@backstage/errors';
|
||||
|
||||
export class TechdocsGenerator implements GeneratorBase {
|
||||
/**
|
||||
@@ -151,8 +152,9 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
this.logger.debug(
|
||||
`Failed to generate docs from ${inputDir} into ${outputDir}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Failed to generate docs from ${inputDir} into ${outputDir} with error ${error.message}`,
|
||||
throw new ForwardedError(
|
||||
'Failed to generate docs from ${inputDir} into ${outputDir}',
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NotModifiedError } from '@backstage/errors';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Logger } from 'winston';
|
||||
@@ -40,8 +40,9 @@ export class UrlPreparer implements PreparerBase {
|
||||
logger: this.logger,
|
||||
});
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
// NotModifiedError means that etag based cache is still valid.
|
||||
if (error instanceof NotModifiedError) {
|
||||
if (error.name === 'NotModifiedError') {
|
||||
this.logger.debug(`Cache is valid for etag ${options?.etag}`);
|
||||
} else {
|
||||
this.logger.debug(
|
||||
|
||||
@@ -286,7 +286,7 @@ describe('AwsS3Publish', () => {
|
||||
const fails = publisher.fetchTechDocsMetadata(invalidEntityName);
|
||||
|
||||
await expect(fails).rejects.toMatchObject({
|
||||
message: `TechDocs metadata fetch failed, The file ${techDocsMetadaFilePath} does not exist!`,
|
||||
message: `TechDocs metadata fetch failed; caused by Error: The file ${techDocsMetadaFilePath} does not exist!`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
import { Entity, EntityName } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import aws, { Credentials } from 'aws-sdk';
|
||||
import { ListObjectsV2Output } from 'aws-sdk/clients/s3';
|
||||
import { CredentialsOptions } from 'aws-sdk/lib/credentials';
|
||||
@@ -49,7 +50,7 @@ const streamToBuffer = (stream: Readable): Promise<Buffer> => {
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse the response data ${e.message}`);
|
||||
throw new ForwardedError('Unable to parse the response data', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -204,6 +205,7 @@ export class AwsS3Publish implements PublisherBase {
|
||||
prefix: remoteFolder,
|
||||
});
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(
|
||||
`Unable to list files for Entity ${entity.metadata.name}: ${e.message}`,
|
||||
);
|
||||
@@ -312,12 +314,13 @@ export class AwsS3Publish implements PublisherBase {
|
||||
|
||||
resolve(techdocsMetadata);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(err.message);
|
||||
reject(new Error(err.message));
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`TechDocs metadata fetch failed, ${e.message}`);
|
||||
throw new ForwardedError('TechDocs metadata fetch failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +354,7 @@ export class AwsS3Publish implements PublisherBase {
|
||||
|
||||
res.send(await streamToBuffer(stream));
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.warn(
|
||||
`TechDocs S3 router failed to serve static files from bucket ${this.bucketName} at key ${filePath}: ${err.message}`,
|
||||
);
|
||||
@@ -396,6 +400,7 @@ export class AwsS3Publish implements PublisherBase {
|
||||
try {
|
||||
newPath = lowerCaseEntityTripletInStoragePath(file);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
return;
|
||||
}
|
||||
@@ -424,6 +429,7 @@ export class AwsS3Publish implements PublisherBase {
|
||||
.promise();
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Unable to migrate ${file}: ${e.message}`);
|
||||
}
|
||||
}, f),
|
||||
|
||||
@@ -201,7 +201,7 @@ describe('AzureBlobStoragePublish', () => {
|
||||
let error;
|
||||
try {
|
||||
await publisher.publish({ entity, directory });
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ describe('AzureBlobStoragePublish', () => {
|
||||
const fails = publisher.fetchTechDocsMetadata(invalidEntityName);
|
||||
|
||||
await expect(fails).rejects.toMatchObject({
|
||||
message: `TechDocs metadata fetch failed, The file ${techDocsMetadaFilePath} does not exist!`,
|
||||
message: `TechDocs metadata fetch failed; caused by Error: The file ${techDocsMetadaFilePath} does not exist!`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '@azure/storage-blob';
|
||||
import { Entity, EntityName } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import express from 'express';
|
||||
import JSON5 from 'json5';
|
||||
import limiterFactory from 'p-limit';
|
||||
@@ -132,6 +133,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.error(`from Azure Blob Storage client library: ${e.message}`);
|
||||
}
|
||||
|
||||
@@ -165,6 +167,7 @@ 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}`,
|
||||
);
|
||||
@@ -307,7 +310,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
);
|
||||
return techdocsMetadata;
|
||||
} catch (e) {
|
||||
throw new Error(`TechDocs metadata fetch failed, ${e.message}`);
|
||||
throw new ForwardedError('TechDocs metadata fetch failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +389,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
try {
|
||||
newPath = lowerCaseEntityTripletInStoragePath(originalPath);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
return;
|
||||
}
|
||||
@@ -395,6 +399,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
|
||||
this.logger.verbose(`Migrating ${originalPath}`);
|
||||
await this.renameBlob(originalPath, newPath, removeOriginal);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(`Unable to migrate ${originalPath}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
import { Entity, EntityName } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { File, FileExistsResponse, Storage } from '@google-cloud/storage';
|
||||
import express from 'express';
|
||||
import JSON5 from 'json5';
|
||||
@@ -112,6 +113,7 @@ 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 ' +
|
||||
@@ -142,6 +144,7 @@ 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}`,
|
||||
);
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
getHeadersForFileExtension,
|
||||
lowerCaseEntityTripletInStoragePath,
|
||||
} from './helpers';
|
||||
import { assertError } from '@backstage/errors';
|
||||
|
||||
// TODO: Use a more persistent storage than node_modules or /tmp directory.
|
||||
// Make it configurable with techdocs.publisher.local.publishDirectory
|
||||
@@ -134,6 +135,7 @@ export class LocalPublish implements PublisherBase {
|
||||
try {
|
||||
return await fs.readJson(metadataPath);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(
|
||||
`Unable to read techdocs_metadata.json at ${metadataPath}. Error: ${err}`,
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assertError } from '@backstage/errors';
|
||||
import { File } from '@google-cloud/storage';
|
||||
import { Writable } from 'stream';
|
||||
import { Logger } from 'winston';
|
||||
@@ -42,6 +43,7 @@ export class MigrateWriteStream extends Writable {
|
||||
try {
|
||||
newFile = lowerCaseEntityTripletInStoragePath(file.name);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.warn(e.message);
|
||||
next();
|
||||
return;
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
ReadinessResponse,
|
||||
TechDocsMetadata,
|
||||
} from './types';
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
|
||||
const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -40,7 +41,7 @@ const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse the response data ${e.message}`);
|
||||
throw new ForwardedError('Unable to parse the response data', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -118,6 +119,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
isAvailable: false,
|
||||
};
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(`from OpenStack client library: ${err.message}`);
|
||||
return {
|
||||
isAvailable: false,
|
||||
@@ -203,6 +205,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
|
||||
resolve(techdocsMetadata);
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.error(err.message);
|
||||
reject(new Error(err.message));
|
||||
}
|
||||
@@ -245,6 +248,7 @@ 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}`,
|
||||
);
|
||||
@@ -276,6 +280,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
assertError(err);
|
||||
this.logger.warn(err.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user