refactor(techdocs-node): remove dependency on backend-common

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-08-14 08:34:34 +02:00
parent 777f56bde1
commit 33ebb28942
28 changed files with 176 additions and 121 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs-node': patch
---
As the `@backstage/backend-common` package is deprecated, we have updated the `techdocs-node` package to stop depending on it.
+40 -15
View File
@@ -7,13 +7,12 @@
import { CompoundEntityRef } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { ContainerRunner } from '@backstage/backend-common';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import express from 'express';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { IndexableDocument } from '@backstage/plugin-search-common';
import { Logger } from 'winston';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { LoggerService } from '@backstage/backend-plugin-api';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { UrlReaderService } from '@backstage/backend-plugin-api';
import * as winston from 'winston';
@@ -48,8 +47,8 @@ export type GeneratorBuilder = {
// @public
export type GeneratorOptions = {
logger: Logger;
containerRunner?: ContainerRunner;
logger: LoggerService;
containerRunner?: TechDocsContainerRunner;
};
// @public
@@ -58,7 +57,7 @@ export type GeneratorRunOptions = {
outputDir: string;
parsedLocationAnnotation?: ParsedLocationAnnotation;
etag?: string;
logger: Logger;
logger: LoggerService;
logStream?: Writable;
siteOptions?: {
name?: string;
@@ -71,8 +70,8 @@ export class Generators implements GeneratorBuilder {
static fromConfig(
config: Config,
options: {
logger: Logger;
containerRunner?: ContainerRunner;
logger: LoggerService;
containerRunner?: TechDocsContainerRunner;
customGenerator?: TechdocsGenerator;
},
): Promise<GeneratorBuilder>;
@@ -86,7 +85,7 @@ export const getDocFilesFromRepository: (
entity: Entity,
opts?: {
etag?: string;
logger?: Logger;
logger?: LoggerService;
},
) => Promise<PreparerResponse>;
@@ -156,13 +155,13 @@ export type PreparerBuilder = {
// @public
export type PreparerConfig = {
logger: Logger;
logger: LoggerService;
reader: UrlReaderService;
};
// @public
export type PreparerOptions = {
logger?: Logger;
logger?: LoggerService;
etag?: ETag;
};
@@ -214,8 +213,8 @@ export type PublisherBuilder = {
// @public
export type PublisherFactory = {
logger: Logger;
discovery: PluginEndpointDiscovery;
logger: LoggerService;
discovery: DiscoveryService;
customPublisher?: PublisherBase | undefined;
};
@@ -261,6 +260,32 @@ export interface TechdocsBuildsExtensionPoint {
// @public
export const techdocsBuildsExtensionPoint: ExtensionPoint<TechdocsBuildsExtensionPoint>;
// @public
export interface TechDocsContainerRunner {
runContainer(opts: {
imageName: string;
command?: string | string[];
args: string[];
logStream?: Writable;
mountDirs?: Record<string, string>;
workingDir?: string;
envVars?: Record<string, string>;
pullImage?: boolean;
defaultUser?: boolean;
pullOptions?: {
authconfig?: {
username?: string;
password?: string;
auth?: string;
email?: string;
serveraddress?: string;
[key: string]: unknown;
};
[key: string]: unknown;
};
}): Promise<void>;
}
// @public
export interface TechDocsDocument extends IndexableDocument {
kind: string;
@@ -274,8 +299,8 @@ export interface TechDocsDocument extends IndexableDocument {
// @public
export class TechdocsGenerator implements GeneratorBase {
constructor(options: {
logger: Logger;
containerRunner?: ContainerRunner;
logger: LoggerService;
containerRunner?: TechDocsContainerRunner;
config: Config;
scmIntegrations: ScmIntegrationRegistry;
});
+6 -4
View File
@@ -14,8 +14,11 @@
* limitations under the License.
*/
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { resolveSafeChildPath } from '@backstage/backend-plugin-api';
import {
LoggerService,
UrlReaderService,
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
import {
Entity,
getEntitySourceLocation,
@@ -25,7 +28,6 @@ import { InputError } from '@backstage/errors';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import path from 'path';
import { Logger } from 'winston';
import { PreparerResponse, RemoteProtocol } from './stages/prepare/types';
/**
@@ -145,7 +147,7 @@ export const getLocationForEntity = (
export const getDocFilesFromRepository = async (
reader: UrlReaderService,
entity: Entity,
opts?: { etag?: string; logger?: Logger },
opts?: { etag?: string; logger?: LoggerService },
): Promise<PreparerResponse> => {
const { target } = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity);
@@ -14,13 +14,12 @@
* limitations under the License.
*/
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { Generators } from './generators';
import { TechdocsGenerator } from './techdocs';
import { mockServices } from '@backstage/backend-test-utils';
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const logger = mockServices.logger.mock();
const mockEntity = {
apiVersion: 'version',
@@ -14,10 +14,8 @@
* limitations under the License.
*/
import { ContainerRunner } from '@backstage/backend-common';
import { Entity } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { Logger } from 'winston';
import { getGeneratorKey } from './helpers';
import { TechdocsGenerator } from './techdocs';
import {
@@ -25,6 +23,8 @@ import {
GeneratorBuilder,
SupportedGeneratorKey,
} from './types';
import { LoggerService } from '@backstage/backend-plugin-api';
import { TechDocsContainerRunner } from '../publish/types';
/**
* Collection of docs generators
@@ -41,8 +41,8 @@ export class Generators implements GeneratorBuilder {
static async fromConfig(
config: Config,
options: {
logger: Logger;
containerRunner?: ContainerRunner;
logger: LoggerService;
containerRunner?: TechDocsContainerRunner;
customGenerator?: TechdocsGenerator;
},
): Promise<GeneratorBuilder> {
@@ -37,7 +37,6 @@ import {
patchMkdocsYmlWithPlugins,
} from './mkdocsPatchers';
import yaml from 'js-yaml';
import { loggerToWinstonLogger } from '@backstage/backend-common';
const mockEntity = {
apiVersion: 'version',
@@ -93,7 +92,7 @@ const mkdocsYmlWithAdditionalPluginsWithConfig = fs.readFileSync(
const mkdocsYmlWithEnvTag = fs.readFileSync(
resolvePath(__filename, '../__fixtures__/mkdocs_with_env_tag.yml'),
);
const mockLogger = loggerToWinstonLogger(mockServices.logger.mock());
const mockLogger = mockServices.logger.mock();
const warn = jest.spyOn(mockLogger, 'warn');
const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({}));
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { isChildPath } from '@backstage/backend-plugin-api';
import { isChildPath, LoggerService } from '@backstage/backend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { assertError, ForwardedError } from '@backstage/errors';
import { ScmIntegrationRegistry } from '@backstage/integration';
@@ -24,7 +24,6 @@ import gitUrlParse from 'git-url-parse';
import yaml, { DEFAULT_SCHEMA, Type } from 'js-yaml';
import path, { resolve as resolvePath } from 'path';
import { PassThrough, Writable } from 'stream';
import { Logger } from 'winston';
import { ParsedLocationAnnotation } from '../../helpers';
import { DefaultMkdocsContent, SupportedGeneratorKey } from './types';
import { getFileTreeRecursively } from '../publish/helpers';
@@ -298,7 +297,7 @@ export const patchIndexPreBuild = async ({
docsDir = 'docs',
}: {
inputDir: string;
logger: Logger;
logger: LoggerService;
docsDir?: string;
}) => {
const docsPath = path.join(inputDir, docsDir);
@@ -342,7 +341,7 @@ export const patchIndexPreBuild = async ({
*/
export const createOrUpdateMetadata = async (
techdocsMetadataPath: string,
logger: Logger,
logger: LoggerService,
): Promise<void> => {
const techdocsMetadataDir = techdocsMetadataPath
.split(path.sep)
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Logger } from 'winston';
import fs from 'fs-extra';
import yaml from 'js-yaml';
import { ParsedLocationAnnotation } from '../../helpers';
import { getRepoUrlFromLocationAnnotation, MKDOCS_SCHEMA } from './helpers';
import { assertError } from '@backstage/errors';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { LoggerService } from '@backstage/backend-plugin-api';
type MkDocsObject = {
plugins?: string[];
@@ -30,7 +30,7 @@ type MkDocsObject = {
const patchMkdocsFile = async (
mkdocsYmlPath: string,
logger: Logger,
logger: LoggerService,
updateAction: (mkdocsYml: MkDocsObject) => boolean,
) => {
// We only want to override the mkdocs.yml if it has actually changed. This is relevant if
@@ -103,7 +103,7 @@ const patchMkdocsFile = async (
*/
export const patchMkdocsYmlPreBuild = async (
mkdocsYmlPath: string,
logger: Logger,
logger: LoggerService,
parsedLocationAnnotation: ParsedLocationAnnotation,
scmIntegrations: ScmIntegrationRegistry,
) => {
@@ -149,7 +149,7 @@ export const patchMkdocsYmlPreBuild = async (
*/
export const patchMkdocsYmlWithPlugins = async (
mkdocsYmlPath: string,
logger: Logger,
logger: LoggerService,
defaultPlugins: string[] = ['techdocs-core'],
) => {
await patchMkdocsFile(mkdocsYmlPath, logger, mkdocsYml => {
@@ -16,7 +16,6 @@
import { Config } from '@backstage/config';
import path from 'path';
import { Logger } from 'winston';
import {
ScmIntegrationRegistry,
ScmIntegrations,
@@ -43,7 +42,8 @@ import {
} from './types';
import { ForwardedError } from '@backstage/errors';
import { DockerContainerRunner } from './DockerContainerRunner';
import { ContainerRunner } from '@backstage/backend-common';
import { LoggerService } from '@backstage/backend-plugin-api';
import { TechDocsContainerRunner } from '../publish/types';
/**
* Generates documentation files
@@ -55,8 +55,8 @@ export class TechdocsGenerator implements GeneratorBase {
* and static so that techdocs-node consumers can use the same version.
*/
public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.4';
private readonly logger: Logger;
private readonly containerRunner?: ContainerRunner;
private readonly logger: LoggerService;
private readonly containerRunner?: TechDocsContainerRunner;
private readonly options: GeneratorConfig;
private readonly scmIntegrations: ScmIntegrationRegistry;
@@ -77,8 +77,8 @@ export class TechdocsGenerator implements GeneratorBase {
}
constructor(options: {
logger: Logger;
containerRunner?: ContainerRunner;
logger: LoggerService;
containerRunner?: TechDocsContainerRunner;
config: Config;
scmIntegrations: ScmIntegrationRegistry;
}) {
@@ -216,7 +216,7 @@ export class TechdocsGenerator implements GeneratorBase {
export function readGeneratorConfig(
config: Config,
logger: Logger,
logger: LoggerService,
): GeneratorConfig {
const legacyGeneratorType = config.getOptionalString(
'techdocs.generators.techdocs',
@@ -16,9 +16,9 @@
import { Entity } from '@backstage/catalog-model';
import { Writable } from 'stream';
import { Logger } from 'winston';
import { ParsedLocationAnnotation } from '../../helpers';
import { ContainerRunner } from '@backstage/backend-common';
import { LoggerService } from '@backstage/backend-plugin-api';
import { TechDocsContainerRunner } from '../publish/types';
// Determines where the generator will be run
export type GeneratorRunInType = 'docker' | 'local';
@@ -28,12 +28,12 @@ export type GeneratorRunInType = 'docker' | 'local';
* @public
*/
export type GeneratorOptions = {
logger: Logger;
logger: LoggerService;
/**
* @deprecated containerRunner is now instantiated in
* the generator and this option will be removed in the future
*/
containerRunner?: ContainerRunner;
containerRunner?: TechDocsContainerRunner;
};
/**
@@ -65,7 +65,7 @@ export type GeneratorRunOptions = {
outputDir: string;
parsedLocationAnnotation?: ParsedLocationAnnotation;
etag?: string;
logger: Logger;
logger: LoggerService;
logStream?: Writable;
siteOptions?: { name?: string };
runAsDefaultUser?: boolean;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { ConfigReader } from '@backstage/config';
import { DirectoryPreparer } from './dir';
@@ -31,7 +30,7 @@ jest.mock('../../helpers', () => ({
...jest.requireActual<{}>('../../helpers'),
}));
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const logger = mockServices.logger.mock();
const createMockEntity = (annotations: {}) => {
return {
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { InputError } from '@backstage/errors';
@@ -23,7 +22,6 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { Logger } from 'winston';
import { parseReferenceAnnotation, transformDirLocation } from '../../helpers';
import {
PreparerBase,
@@ -31,6 +29,7 @@ import {
PreparerOptions,
PreparerResponse,
} from './types';
import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api';
/**
* Preparer used to retrieve documentation files from a local directory
@@ -54,7 +53,7 @@ export class DirectoryPreparer implements PreparerBase {
private constructor(
config: Config,
_logger: Logger | null,
_logger: LoggerService | null,
reader: UrlReaderService,
) {
this.reader = reader;
@@ -15,8 +15,7 @@
*/
import type { Entity } from '@backstage/catalog-model';
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { Logger } from 'winston';
import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api';
/**
* A unique identifier of the tree blob, usually the commit SHA or etag from the target.
@@ -29,7 +28,7 @@ export type ETag = string;
* @public
*/
export type PreparerConfig = {
logger: Logger;
logger: LoggerService;
reader: UrlReaderService;
};
@@ -41,7 +40,7 @@ export type PreparerOptions = {
/**
* An instance of the logger
*/
logger?: Logger;
logger?: LoggerService;
/**
* see {@link ETag}
*/
@@ -15,9 +15,7 @@
*/
import { assertError } from '@backstage/errors';
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { Logger } from 'winston';
import { getDocFilesFromRepository } from '../../helpers';
import {
PreparerBase,
@@ -25,13 +23,14 @@ import {
PreparerOptions,
PreparerResponse,
} from './types';
import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api';
/**
* Preparer used to retrieve documentation files from a remote repository
* @public
*/
export class UrlPreparer implements PreparerBase {
private readonly logger: Logger;
private readonly logger: LoggerService;
private readonly reader: UrlReaderService;
/**
@@ -42,7 +41,7 @@ export class UrlPreparer implements PreparerBase {
return new UrlPreparer(options.reader, options.logger);
}
private constructor(reader: UrlReaderService, logger: Logger) {
private constructor(reader: UrlReaderService, logger: LoggerService) {
this.logger = logger;
this.reader = reader;
}
@@ -42,7 +42,6 @@ import {
createMockDirectory,
mockServices,
} from '@backstage/backend-test-utils';
import { loggerToWinstonLogger } from '@backstage/backend-common';
const env = process.env;
let s3Mock: AwsClientStub<S3Client>;
@@ -90,7 +89,7 @@ class ErrorReadable extends Readable {
}
}
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const logger = mockServices.logger.mock();
const loggerInfoSpy = jest.spyOn(logger, 'info');
const loggerErrorSpy = jest.spyOn(logger, 'error');
@@ -42,7 +42,6 @@ import JSON5 from 'json5';
import createLimiter from 'p-limit';
import path from 'path';
import { Readable } from 'stream';
import { Logger } from 'winston';
import {
bulkStorageOperation,
getCloudPathForLocalPath,
@@ -60,6 +59,7 @@ import {
ReadinessResponse,
TechDocsMetadata,
} from './types';
import { LoggerService } from '@backstage/backend-plugin-api';
const streamToBuffer = (stream: Readable): Promise<Buffer> => {
return new Promise((resolve, reject) => {
@@ -80,7 +80,7 @@ export class AwsS3Publish implements PublisherBase {
private readonly storageClient: S3Client;
private readonly bucketName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly logger: LoggerService;
private readonly bucketRootPath: string;
private readonly sse?: 'aws:kms' | 'AES256';
@@ -88,7 +88,7 @@ export class AwsS3Publish implements PublisherBase {
storageClient: S3Client;
bucketName: string;
legacyPathCasing: boolean;
logger: Logger;
logger: LoggerService;
bucketRootPath: string;
sse?: 'aws:kms' | 'AES256';
}) {
@@ -102,7 +102,7 @@ export class AwsS3Publish implements PublisherBase {
static async fromConfig(
config: Config,
logger: Logger,
logger: LoggerService,
): Promise<PublisherBase> {
let bucketName = '';
try {
@@ -524,7 +524,7 @@ export class AwsS3Publish implements PublisherBase {
}
try {
this.logger.verbose(`Migrating ${file}`);
this.logger.debug(`Migrating ${file}`);
await this.storageClient.send(
new CopyObjectCommand({
Bucket: this.bucketName,
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import express from 'express';
@@ -224,8 +223,8 @@ const getEntityRootDir = (entity: Entity) => {
return mockDir.resolve(namespace || DEFAULT_NAMESPACE, kind, name);
};
const logger = loggerToWinstonLogger(mockServices.logger.mock());
jest.spyOn(logger, 'error').mockReturnValue(logger);
const logger = mockServices.logger.mock();
jest.spyOn(logger, 'error');
const createPublisherFromConfig = ({
accountName = 'accountName',
@@ -26,7 +26,6 @@ import express from 'express';
import JSON5 from 'json5';
import limiterFactory from 'p-limit';
import { default as path, default as platformPath } from 'path';
import { Logger } from 'winston';
import {
bulkStorageOperation,
getCloudPathForLocalPath,
@@ -43,6 +42,7 @@ import {
ReadinessResponse,
TechDocsMetadata,
} from './types';
import { LoggerService } from '@backstage/backend-plugin-api';
// The number of batches that may be ongoing at the same time.
const BATCH_CONCURRENCY = 3;
@@ -51,13 +51,13 @@ export class AzureBlobStoragePublish implements PublisherBase {
private readonly storageClient: BlobServiceClient;
private readonly containerName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly logger: LoggerService;
constructor(options: {
storageClient: BlobServiceClient;
containerName: string;
legacyPathCasing: boolean;
logger: Logger;
logger: LoggerService;
}) {
this.storageClient = options.storageClient;
this.containerName = options.containerName;
@@ -65,7 +65,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
this.logger = options.logger;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
static fromConfig(config: Config, logger: LoggerService): PublisherBase {
let storageClient: BlobServiceClient;
let containerName = '';
try {
@@ -421,7 +421,7 @@ export class AzureBlobStoragePublish implements PublisherBase {
if (originalPath === newPath) return;
try {
this.logger.verbose(`Migrating ${originalPath}`);
this.logger.debug(`Migrating ${originalPath}`);
await this.renameBlob(originalPath, newPath, removeOriginal);
} catch (e) {
assertError(e);
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import express from 'express';
@@ -139,9 +138,9 @@ const getEntityRootDir = (entity: Entity) => {
return mockDir.resolve(namespace || DEFAULT_NAMESPACE, kind, name);
};
const logger = loggerToWinstonLogger(mockServices.logger.mock());
jest.spyOn(logger, 'info').mockReturnValue(logger);
jest.spyOn(logger, 'error').mockReturnValue(logger);
const logger = mockServices.logger.mock();
jest.spyOn(logger, 'info');
jest.spyOn(logger, 'error');
const createPublisherFromConfig = ({
bucketName = 'bucketName',
@@ -26,7 +26,6 @@ import express from 'express';
import JSON5 from 'json5';
import path from 'path';
import { Readable } from 'stream';
import { Logger } from 'winston';
import {
getFileTreeRecursively,
getHeadersForFileExtension,
@@ -45,19 +44,20 @@ import {
ReadinessResponse,
TechDocsMetadata,
} from './types';
import { LoggerService } from '@backstage/backend-plugin-api';
export class GoogleGCSPublish implements PublisherBase {
private readonly storageClient: Storage;
private readonly bucketName: string;
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly logger: LoggerService;
private readonly bucketRootPath: string;
constructor(options: {
storageClient: Storage;
bucketName: string;
legacyPathCasing: boolean;
logger: Logger;
logger: LoggerService;
bucketRootPath: string;
}) {
this.storageClient = options.storageClient;
@@ -67,7 +67,7 @@ export class GoogleGCSPublish implements PublisherBase {
this.bucketRootPath = options.bucketRootPath;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
static fromConfig(config: Config, logger: LoggerService): PublisherBase {
let bucketName = '';
try {
bucketName = config.getString('techdocs.publisher.googleGcs.bucketName');
@@ -18,10 +18,11 @@ export type {
PublisherBase,
PublisherType,
PublisherFactory,
PublisherBuilder,
PublishRequest,
PublishResponse,
MigrateRequest,
ReadinessResponse,
TechDocsMetadata,
PublisherBuilder,
TechDocsContainerRunner,
} from './types';
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
loggerToWinstonLogger,
PluginEndpointDiscovery,
} from '@backstage/backend-common';
import { overridePackagePathResolution } from '@backstage/backend-plugin-api/testUtils';
import { ConfigReader } from '@backstage/config';
import express from 'express';
@@ -28,6 +24,7 @@ import {
createMockDirectory,
mockServices,
} from '@backstage/backend-test-utils';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const createMockEntity = (annotations = {}, lowerCase = false) => {
return {
@@ -42,7 +39,7 @@ const createMockEntity = (annotations = {}, lowerCase = false) => {
};
};
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
const testDiscovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
@@ -56,7 +53,7 @@ overridePackagePathResolution({
},
});
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const logger = mockServices.logger.mock();
describe('local publisher', () => {
const mockDir = createMockDirectory();
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import {
DiscoveryService,
LoggerService,
resolvePackagePath,
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
@@ -29,7 +31,6 @@ import fs from 'fs-extra';
import os from 'os';
import createLimiter from 'p-limit';
import path from 'path';
import { Logger } from 'winston';
import {
PublisherBase,
PublishRequest,
@@ -51,13 +52,13 @@ import { ForwardedError } from '@backstage/errors';
*/
export class LocalPublish implements PublisherBase {
private readonly legacyPathCasing: boolean;
private readonly logger: Logger;
private readonly discovery: PluginEndpointDiscovery;
private readonly logger: LoggerService;
private readonly discovery: DiscoveryService;
private readonly staticDocsDir: string;
constructor(options: {
logger: Logger;
discovery: PluginEndpointDiscovery;
logger: LoggerService;
discovery: DiscoveryService;
legacyPathCasing: boolean;
staticDocsDir: string;
}) {
@@ -69,8 +70,8 @@ export class LocalPublish implements PublisherBase {
static fromConfig(
config: Config,
logger: Logger,
discovery: PluginEndpointDiscovery,
logger: LoggerService,
discovery: DiscoveryService,
): PublisherBase {
const legacyPathCasing =
config.getOptionalBoolean(
@@ -299,7 +300,7 @@ export class LocalPublish implements PublisherBase {
// Otherwise, copy or move the file.
await new Promise<void>(resolve => {
const migrate = removeOriginal ? fs.move : fs.copyFile;
this.logger.verbose(`Migrating ${relativeFile}`);
this.logger.debug(`Migrating ${relativeFile}`);
migrate(file, newFile, err => {
if (err) {
this.logger.warn(
@@ -17,20 +17,24 @@
import { assertError } from '@backstage/errors';
import { File } from '@google-cloud/storage';
import { Writable } from 'stream';
import { Logger } from 'winston';
import { lowerCaseEntityTripletInStoragePath } from '../helpers';
import { LoggerService } from '@backstage/backend-plugin-api';
/**
* Writable stream to handle object copy/move operations. This implementation
* ensures we don't read in files from GCS faster than GCS can copy/move them.
*/
export class MigrateWriteStream extends Writable {
protected logger: Logger;
protected logger: LoggerService;
protected removeOriginal: boolean;
protected maxConcurrency: number;
protected inFlight = 0;
constructor(logger: Logger, removeOriginal: boolean, concurrency: number) {
constructor(
logger: LoggerService,
removeOriginal: boolean,
concurrency: number,
) {
super({ objectMode: true });
this.logger = logger;
this.removeOriginal = removeOriginal;
@@ -66,7 +70,7 @@ export class MigrateWriteStream extends Writable {
const migrate = this.removeOriginal
? file.move.bind(file)
: file.copy.bind(file);
this.logger.verbose(`Migrating ${file.name}`);
this.logger.debug(`Migrating ${file.name}`);
migrate(newFile)
.catch(e =>
this.logger.warn(`Unable to migrate ${file.name}: ${e.message}`),
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { loggerToWinstonLogger } from '@backstage/backend-common';
import {
Entity,
CompoundEntityRef,
@@ -172,7 +171,7 @@ const getPosixEntityRootDir = (entity: Entity) => {
);
};
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const logger = mockServices.logger.mock();
let publisher: PublisherBase;
@@ -23,7 +23,7 @@ import path from 'path';
import { SwiftClient } from '@trendyol-js/openstack-swift-sdk';
import { NotFound } from '@trendyol-js/openstack-swift-sdk/lib/types';
import { Stream, Readable } from 'stream';
import { Logger } from 'winston';
import {
getFileTreeRecursively,
getHeadersForFileExtension,
@@ -37,6 +37,7 @@ import {
TechDocsMetadata,
} from './types';
import { assertError, ForwardedError } from '@backstage/errors';
import { LoggerService } from '@backstage/backend-plugin-api';
const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
return new Promise((resolve, reject) => {
@@ -61,19 +62,19 @@ const bufferToStream = (buffer: Buffer): Readable => {
export class OpenStackSwiftPublish implements PublisherBase {
private readonly storageClient: SwiftClient;
private readonly containerName: string;
private readonly logger: Logger;
private readonly logger: LoggerService;
constructor(options: {
storageClient: SwiftClient;
containerName: string;
logger: Logger;
logger: LoggerService;
}) {
this.storageClient = options.storageClient;
this.containerName = options.containerName;
this.logger = options.logger;
}
static fromConfig(config: Config, logger: Logger): PublisherBase {
static fromConfig(config: Config, logger: LoggerService): PublisherBase {
let containerName = '';
try {
containerName = config.getString(
@@ -326,7 +327,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
}
try {
this.logger.verbose(`Migrating ${file} to ${newPath}`);
this.logger.debug(`Migrating ${file} to ${newPath}`);
await this.storageClient.copy(
this.containerName,
file,
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
PluginEndpointDiscovery,
loggerToWinstonLogger,
} from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { Publisher } from './publish';
import { LocalPublish } from './local';
@@ -26,9 +22,10 @@ import { AzureBlobStoragePublish } from './azureBlobStorage';
import { OpenStackSwiftPublish } from './openStackSwift';
import { mockServices } from '@backstage/backend-test-utils';
import { PublisherBase } from './types';
import { DiscoveryService } from '@backstage/backend-plugin-api';
const logger = loggerToWinstonLogger(mockServices.logger.mock());
const discovery: jest.Mocked<PluginEndpointDiscovery> = {
const logger = mockServices.logger.mock();
const discovery: jest.Mocked<DiscoveryService> = {
getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7007'),
getExternalBaseUrl: jest.fn(),
};
@@ -13,19 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { Logger } from 'winston';
import { Writable } from 'stream';
import express from 'express';
import { Config } from '@backstage/config';
import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
/**
* Options for building publishers
* @public
*/
export type PublisherFactory = {
logger: Logger;
discovery: PluginEndpointDiscovery;
logger: LoggerService;
discovery: DiscoveryService;
customPublisher?: PublisherBase | undefined;
};
@@ -168,3 +168,36 @@ export type PublisherBuilder = {
register(type: PublisherType, publisher: PublisherBase): void;
get(config: Config): PublisherBase;
};
/**
* Handles the running of containers, on behalf of others.
*
* @public
*/
export interface TechDocsContainerRunner {
/**
* Runs a container image to completion.
*/
runContainer(opts: {
imageName: string;
command?: string | string[];
args: string[];
logStream?: Writable;
mountDirs?: Record<string, string>;
workingDir?: string;
envVars?: Record<string, string>;
pullImage?: boolean;
defaultUser?: boolean;
pullOptions?: {
authconfig?: {
username?: string;
password?: string;
auth?: string;
email?: string;
serveraddress?: string;
[key: string]: unknown;
};
[key: string]: unknown;
};
}): Promise<void>;
}