feat(techdocs): Allow to pass options to GCS publisher (#26836)
* feat(techdocs): Allow to pass options to GCS publisher Signed-off-by: Adrian Kosinski <adrian.kosinski@allegro.com> --------- Signed-off-by: Adrian Kosinski <adrian.kosinski@allegro.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
'@backstage/plugin-techdocs-node': patch
|
||||
---
|
||||
|
||||
Allow to pass StorageOptions to GCS Publisher
|
||||
@@ -120,6 +120,36 @@ techdocs:
|
||||
Your Backstage app is now ready to use Google Cloud Storage for TechDocs, to
|
||||
store and read the static generated documentation files.
|
||||
|
||||
### Extending default Storage configuration
|
||||
|
||||
If you need a non-standard configuration of Google Cloud Storage client,
|
||||
`TechdocsPublisherExtensionPoint` is something you should look at.
|
||||
You can register custom `StorageOptions` that will be used to configure the client. To do so, you
|
||||
need to register publisher settings inside your module init, like in the following example:
|
||||
|
||||
```typescript
|
||||
export const gcsPublisherCustomizer = createBackendModule({
|
||||
pluginId: 'techdocs',
|
||||
moduleId: 'gcs-publisher-customizer',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
techdocsExtensionPoint: techdocsPublisherExtensionPoint,
|
||||
},
|
||||
async init({ techdocsExtensionPoint }) {
|
||||
const customOptions: StorageOptions = {
|
||||
userAgent: 'my-custom-user-agent',
|
||||
};
|
||||
techdocsExtensionPoint.registerPublisherSettings(
|
||||
'googleGcs',
|
||||
customOptions,
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Configuring AWS S3 Bucket with TechDocs
|
||||
|
||||
**1. Set `techdocs.publisher.type` config in your `app-config.yaml`**
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
Preparers,
|
||||
Publisher,
|
||||
PublisherBase,
|
||||
PublisherSettings,
|
||||
PublisherType,
|
||||
RemoteProtocol,
|
||||
techdocsBuildsExtensionPoint,
|
||||
@@ -89,6 +90,7 @@ export const techdocsPlugin = createBackendPlugin({
|
||||
});
|
||||
|
||||
let customTechdocsPublisher: PublisherBase | undefined;
|
||||
const publisherSettings: PublisherSettings = {};
|
||||
env.registerExtensionPoint(techdocsPublisherExtensionPoint, {
|
||||
registerPublisher(type: PublisherType, publisher: PublisherBase) {
|
||||
if (customTechdocsPublisher) {
|
||||
@@ -96,6 +98,12 @@ export const techdocsPlugin = createBackendPlugin({
|
||||
}
|
||||
customTechdocsPublisher = publisher;
|
||||
},
|
||||
registerPublisherSettings<T extends keyof PublisherSettings>(
|
||||
publisher: T,
|
||||
settings: PublisherSettings[T],
|
||||
) {
|
||||
publisherSettings[publisher] = settings;
|
||||
},
|
||||
});
|
||||
|
||||
env.registerInit({
|
||||
@@ -144,6 +152,7 @@ export const techdocsPlugin = createBackendPlugin({
|
||||
logger: winstonLogger,
|
||||
discovery: discovery,
|
||||
customPublisher: customTechdocsPublisher,
|
||||
publisherSettings,
|
||||
});
|
||||
|
||||
// checks if the publisher is working and logs the result
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { StorageOptions } from '@google-cloud/storage';
|
||||
import { UrlReaderService } from '@backstage/backend-plugin-api';
|
||||
import * as winston from 'winston';
|
||||
import { Writable } from 'stream';
|
||||
@@ -217,8 +218,15 @@ export type PublisherFactory = {
|
||||
logger: LoggerService;
|
||||
discovery: DiscoveryService;
|
||||
customPublisher?: PublisherBase | undefined;
|
||||
publisherSettings?: PublisherSettings;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface PublisherSettings {
|
||||
// (undocumented)
|
||||
googleGcs?: StorageOptions;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type PublisherType =
|
||||
| 'local'
|
||||
@@ -344,6 +352,11 @@ export const techdocsPreparerExtensionPoint: ExtensionPoint<TechdocsPreparerExte
|
||||
export interface TechdocsPublisherExtensionPoint {
|
||||
// (undocumented)
|
||||
registerPublisher(type: PublisherType, publisher: PublisherBase): void;
|
||||
// (undocumented)
|
||||
registerPublisherSettings<T extends keyof PublisherSettings>(
|
||||
publisher: T,
|
||||
settings: PublisherSettings[T],
|
||||
): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -368,13 +381,15 @@ export class UrlPreparer implements PreparerBase {
|
||||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/extensions.d.ts:10:5 - (ae-undocumented) Missing documentation for "setBuildStrategy".
|
||||
// src/extensions.d.ts:11:5 - (ae-undocumented) Missing documentation for "setBuildLogTransport".
|
||||
// src/extensions.d.ts:25:5 - (ae-undocumented) Missing documentation for "setTechdocsGenerator".
|
||||
// src/extensions.d.ts:39:5 - (ae-undocumented) Missing documentation for "registerPreparer".
|
||||
// src/extensions.d.ts:53:5 - (ae-undocumented) Missing documentation for "registerPublisher".
|
||||
// src/extensions.d.ts:11:5 - (ae-undocumented) Missing documentation for "setBuildStrategy".
|
||||
// src/extensions.d.ts:12:5 - (ae-undocumented) Missing documentation for "setBuildLogTransport".
|
||||
// src/extensions.d.ts:26:5 - (ae-undocumented) Missing documentation for "setTechdocsGenerator".
|
||||
// src/extensions.d.ts:40:5 - (ae-undocumented) Missing documentation for "registerPreparer".
|
||||
// src/extensions.d.ts:54:5 - (ae-undocumented) Missing documentation for "registerPublisher".
|
||||
// src/extensions.d.ts:55:5 - (ae-undocumented) Missing documentation for "registerPublisherSettings".
|
||||
// src/stages/generate/index.d.ts:10:22 - (ae-undocumented) Missing documentation for "getMkDocsYml".
|
||||
// src/stages/publish/publish.d.ts:10:5 - (ae-undocumented) Missing documentation for "register".
|
||||
// src/stages/publish/publish.d.ts:11:5 - (ae-undocumented) Missing documentation for "get".
|
||||
// src/stages/publish/types.d.ts:21:5 - (ae-undocumented) Missing documentation for "googleGcs".
|
||||
// src/techdocsTypes.d.ts:39:5 - (ae-undocumented) Missing documentation for "shouldBuild".
|
||||
```
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
TechdocsGenerator,
|
||||
} from './stages';
|
||||
import * as winston from 'winston';
|
||||
import { PublisherSettings } from './stages/publish/types';
|
||||
|
||||
/**
|
||||
* Extension point type for configuring TechDocs builds.
|
||||
@@ -89,6 +90,10 @@ export const techdocsPreparerExtensionPoint =
|
||||
*/
|
||||
export interface TechdocsPublisherExtensionPoint {
|
||||
registerPublisher(type: PublisherType, publisher: PublisherBase): void;
|
||||
registerPublisherSettings<T extends keyof PublisherSettings>(
|
||||
publisher: T,
|
||||
settings: PublisherSettings[T],
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,9 +26,12 @@ import {
|
||||
createMockDirectory,
|
||||
mockServices,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { StorageOptions } from '@google-cloud/storage';
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
let createdStorageOptions: Array<StorageOptions | undefined> = [];
|
||||
|
||||
jest.mock('@google-cloud/storage', () => {
|
||||
class GCSFile {
|
||||
constructor(private readonly filePath: string) {}
|
||||
@@ -118,6 +121,10 @@ jest.mock('@google-cloud/storage', () => {
|
||||
}
|
||||
|
||||
class Storage {
|
||||
constructor(readonly options?: StorageOptions) {
|
||||
createdStorageOptions.push(options);
|
||||
}
|
||||
|
||||
bucket(bucketName: string) {
|
||||
return new Bucket(bucketName);
|
||||
}
|
||||
@@ -144,10 +151,12 @@ const createPublisherFromConfig = ({
|
||||
bucketName = 'bucketName',
|
||||
bucketRootPath = '/',
|
||||
legacyUseCaseSensitiveTripletPaths = false,
|
||||
storageOptions = {},
|
||||
}: {
|
||||
bucketName?: string;
|
||||
bucketRootPath?: string;
|
||||
legacyUseCaseSensitiveTripletPaths?: boolean;
|
||||
storageOptions?: StorageOptions;
|
||||
} = {}) => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
@@ -162,7 +171,7 @@ const createPublisherFromConfig = ({
|
||||
legacyUseCaseSensitiveTripletPaths,
|
||||
},
|
||||
});
|
||||
return GoogleGCSPublish.fromConfig(config, logger);
|
||||
return GoogleGCSPublish.fromConfig(config, logger, storageOptions);
|
||||
};
|
||||
|
||||
describe('GoogleGCSPublish', () => {
|
||||
@@ -211,11 +220,24 @@ describe('GoogleGCSPublish', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createdStorageOptions = [];
|
||||
mockDir.setContent({
|
||||
[directory]: files,
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass options to storage', () => {
|
||||
createPublisherFromConfig({
|
||||
storageOptions: {
|
||||
userAgent: 'Test-UA',
|
||||
},
|
||||
});
|
||||
|
||||
expect(createdStorageOptions.map(opt => opt?.userAgent)).toContain(
|
||||
'Test-UA',
|
||||
);
|
||||
});
|
||||
|
||||
describe('getReadiness', () => {
|
||||
it('should validate correct config', async () => {
|
||||
const publisher = createPublisherFromConfig();
|
||||
|
||||
@@ -68,7 +68,11 @@ export class GoogleGCSPublish implements PublisherBase {
|
||||
this.bucketRootPath = options.bucketRootPath;
|
||||
}
|
||||
|
||||
static fromConfig(config: Config, logger: LoggerService): PublisherBase {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
logger: LoggerService,
|
||||
options?: StorageOptions,
|
||||
): PublisherBase {
|
||||
let bucketName = '';
|
||||
try {
|
||||
bucketName = config.getString('techdocs.publisher.googleGcs.bucketName');
|
||||
@@ -103,7 +107,7 @@ export class GoogleGCSPublish implements PublisherBase {
|
||||
}
|
||||
}
|
||||
|
||||
const clientOpts: StorageOptions = {};
|
||||
const clientOpts: StorageOptions = options ?? {};
|
||||
if (projectId) {
|
||||
clientOpts.projectId = projectId;
|
||||
}
|
||||
|
||||
@@ -24,4 +24,5 @@ export type {
|
||||
MigrateRequest,
|
||||
ReadinessResponse,
|
||||
TechDocsMetadata,
|
||||
PublisherSettings,
|
||||
} from './types';
|
||||
|
||||
@@ -86,7 +86,11 @@ export class Publisher implements PublisherBuilder {
|
||||
logger.info('Creating Google Storage Bucket publisher for TechDocs');
|
||||
publishers.register(
|
||||
publisherType,
|
||||
GoogleGCSPublish.fromConfig(config, logger),
|
||||
GoogleGCSPublish.fromConfig(
|
||||
config,
|
||||
logger,
|
||||
options.publisherSettings?.googleGcs,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case 'awsS3':
|
||||
|
||||
@@ -17,6 +17,7 @@ import express from 'express';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { StorageOptions } from '@google-cloud/storage';
|
||||
|
||||
/**
|
||||
* Options for building publishers
|
||||
@@ -26,8 +27,17 @@ export type PublisherFactory = {
|
||||
logger: LoggerService;
|
||||
discovery: DiscoveryService;
|
||||
customPublisher?: PublisherBase | undefined;
|
||||
publisherSettings?: PublisherSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Additional configurations for publishers.
|
||||
* @public
|
||||
*/
|
||||
export interface PublisherSettings {
|
||||
googleGcs?: StorageOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key for all the different types of TechDocs publishers that are supported.
|
||||
* @public
|
||||
|
||||
Reference in New Issue
Block a user