Use Parameter Objects for Publisher Constructors

Refactored Techdocs publishers to use a single parameter object as the
constructor argument to ease extendability as more options are introduced.

Updated local publisher to use `.fromConfig` for instantiation so that it
follows the same design pattern as the other publishers.

Signed-off-by: Colton Padden <colton.padden@fastmail.com>
This commit is contained in:
Colton Padden
2021-10-31 11:15:47 -04:00
parent c3cb126a9d
commit a2d4389587
8 changed files with 140 additions and 82 deletions
+12
View File
@@ -0,0 +1,12 @@
---
'@backstage/techdocs-common': patch
---
1. Techdocs publishers constructors now use parameter objects when being instantiated
2. The `LocalPublish` publisher can now be created using `fromConfig`:
```
--- const publisher = new LocalPublish(config, logger, discovery);
+++ const publisher = LocalPublish.fromConfig(config, logger, discovery);
```
@@ -57,6 +57,26 @@ const streamToBuffer = (stream: Readable): Promise<Buffer> => {
};
export class AwsS3Publish implements PublisherBase {
private readonly storageClient: aws.S3;
private readonly bucketName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly bucketRootPath: string;
constructor(options: {
storageClient: aws.S3;
bucketName: string;
legacyPathCasing: boolean;
logger: Logger;
bucketRootPath: string;
}) {
this.storageClient = options.storageClient;
this.bucketName = options.bucketName;
this.legacyPathCasing = options.legacyPathCasing;
this.logger = options.logger;
this.bucketRootPath = options.bucketRootPath;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
let bucketName = '';
try {
@@ -112,13 +132,13 @@ export class AwsS3Publish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
return new AwsS3Publish(
return new AwsS3Publish({
storageClient,
bucketName,
bucketRootPath,
legacyPathCasing,
logger,
bucketRootPath,
);
});
}
private static buildCredentials(
@@ -152,20 +172,6 @@ export class AwsS3Publish implements PublisherBase {
return explicitCredentials;
}
constructor(
private readonly storageClient: aws.S3,
private readonly bucketName: string,
private readonly legacyPathCasing: boolean,
private readonly logger: Logger,
private readonly bucketRootPath: string,
) {
this.storageClient = storageClient;
this.bucketName = bucketName;
this.legacyPathCasing = legacyPathCasing;
this.logger = logger;
this.bucketRootPath = bucketRootPath;
}
/**
* Check if the defined bucket exists. Being able to connect means the configuration is good
* and the storage client will work.
@@ -47,6 +47,23 @@ import {
const BATCH_CONCURRENCY = 3;
export class AzureBlobStoragePublish implements PublisherBase {
private readonly storageClient: BlobServiceClient;
private readonly containerName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
constructor(options: {
storageClient: BlobServiceClient;
containerName: string;
legacyPathCasing: boolean;
logger: Logger;
}) {
this.storageClient = options.storageClient;
this.containerName = options.containerName;
this.legacyPathCasing = options.legacyPathCasing;
this.logger = options.logger;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
let containerName = '';
try {
@@ -95,24 +112,12 @@ export class AzureBlobStoragePublish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
return new AzureBlobStoragePublish(
storageClient,
containerName,
legacyPathCasing,
logger,
);
}
constructor(
private readonly storageClient: BlobServiceClient,
private readonly containerName: string,
private readonly legacyPathCasing: boolean,
private readonly logger: Logger,
) {
this.storageClient = storageClient;
this.containerName = containerName;
this.legacyPathCasing = legacyPathCasing;
this.logger = logger;
return new AzureBlobStoragePublish({
storageClient: storageClient,
containerName: containerName,
legacyPathCasing: legacyPathCasing,
logger: logger,
});
}
async getReadiness(): Promise<ReadinessResponse> {
@@ -41,6 +41,26 @@ import {
} from './types';
export class GoogleGCSPublish implements PublisherBase {
private readonly storageClient: Storage;
private readonly bucketName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly bucketRootPath: string;
constructor(options: {
storageClient: Storage;
bucketName: string;
legacyPathCasing: boolean;
logger: Logger;
bucketRootPath: string;
}) {
this.storageClient = options.storageClient;
this.bucketName = options.bucketName;
this.legacyPathCasing = options.legacyPathCasing;
this.logger = options.logger;
this.bucketRootPath = options.bucketRootPath;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
let bucketName = '';
try {
@@ -84,27 +104,13 @@ export class GoogleGCSPublish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
return new GoogleGCSPublish(
return new GoogleGCSPublish({
storageClient,
bucketName,
legacyPathCasing,
logger,
bucketRootPath,
);
}
constructor(
private readonly storageClient: Storage,
private readonly bucketName: string,
private readonly legacyPathCasing: boolean,
private readonly logger: Logger,
private readonly bucketRootPath: string,
) {
this.storageClient = storageClient;
this.bucketName = bucketName;
this.legacyPathCasing = legacyPathCasing;
this.logger = logger;
this.bucketRootPath = bucketRootPath;
});
}
/**
@@ -63,7 +63,11 @@ describe('local publisher', () => {
const mockConfig = new ConfigReader({});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
const publisher = LocalPublish.fromConfig(
mockConfig,
logger,
testDiscovery,
);
const mockEntity = createMockEntity();
const lowerMockEntity = createMockEntity(undefined, true);
@@ -90,7 +94,11 @@ describe('local publisher', () => {
},
});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
const publisher = LocalPublish.fromConfig(
mockConfig,
logger,
testDiscovery,
);
const mockEntity = createMockEntity();
const lowerMockEntity = createMockEntity(undefined, true);
@@ -106,7 +114,11 @@ describe('local publisher', () => {
describe('docsRouter', () => {
const mockConfig = new ConfigReader({});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
const publisher = LocalPublish.fromConfig(
mockConfig,
logger,
testDiscovery,
);
let app: express.Express;
beforeEach(() => {
@@ -166,7 +178,7 @@ describe('local publisher', () => {
legacyUseCaseSensitiveTripletPaths: true,
},
});
const legacyPublisher = new LocalPublish(
const legacyPublisher = LocalPublish.fromConfig(
legacyConfig,
logger,
testDiscovery,
@@ -59,24 +59,37 @@ try {
* called "static" at the root of techdocs-backend plugin.
*/
export class LocalPublish implements PublisherBase {
private legacyPathCasing: boolean;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly discovery: PluginEndpointDiscovery;
// TODO: Use a static fromConfig method to create a LocalPublish instance, similar to aws/gcs publishers.
// Move the logic of setting staticDocsDir based on config over to fromConfig,
// and set the value as a class parameter.
constructor(
// @ts-ignore
private readonly config: Config,
private readonly logger: Logger,
private readonly discovery: PluginEndpointDiscovery,
) {
this.config = config;
this.logger = logger;
this.discovery = discovery;
this.legacyPathCasing =
// TODO: Move the logic of setting staticDocsDir based on config over to
// fromConfig, and set the value as a class parameter.
constructor(options: {
logger: Logger;
discovery: PluginEndpointDiscovery;
legacyPathCasing: boolean;
}) {
this.logger = options.logger;
this.discovery = options.discovery;
this.legacyPathCasing = options.legacyPathCasing;
}
static fromConfig(
config: Config,
logger: Logger,
discovery: PluginEndpointDiscovery,
): PublisherBase {
const legacyPathCasing =
config.getOptionalBoolean(
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
return new LocalPublish({
logger,
discovery,
legacyPathCasing,
});
}
async getReadiness(): Promise<ReadinessResponse> {
@@ -54,6 +54,20 @@ const bufferToStream = (buffer: Buffer): Readable => {
};
export class OpenStackSwiftPublish implements PublisherBase {
private readonly storageClient: SwiftClient;
private readonly containerName: string;
private readonly logger: Logger;
constructor(options: {
storageClient: SwiftClient;
containerName: string;
logger: Logger;
}) {
this.storageClient = options.storageClient;
this.containerName = options.containerName;
this.logger = options.logger;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
let containerName = '';
try {
@@ -78,17 +92,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
secret: openStackSwiftConfig.getString('credentials.secret'),
});
return new OpenStackSwiftPublish(storageClient, containerName, logger);
}
constructor(
private readonly storageClient: SwiftClient,
private readonly containerName: string,
private readonly logger: Logger,
) {
this.storageClient = storageClient;
this.containerName = containerName;
this.logger = logger;
return new OpenStackSwiftPublish({ storageClient, containerName, logger });
}
/*
@@ -61,10 +61,10 @@ export class Publisher {
return OpenStackSwiftPublish.fromConfig(config, logger);
case 'local':
logger.info('Creating Local publisher for TechDocs');
return new LocalPublish(config, logger, discovery);
return LocalPublish.fromConfig(config, logger, discovery);
default:
logger.info('Creating Local publisher for TechDocs');
return new LocalPublish(config, logger, discovery);
return LocalPublish.fromConfig(config, logger, discovery);
}
}
}