Merge pull request #10174 from backstage/emmaindal/techdocs-clean-up-deprecations
[TechDocs] clean up deprecations
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-node': minor
|
||||
---
|
||||
|
||||
- `DirectoryPreparer` now uses private constructor. Use static fromConfig method to instantiate.
|
||||
- `UrlPreparer` now uses private constructor. Use static fromConfig method to instantiate.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': minor
|
||||
---
|
||||
|
||||
Removed deprecated exports, including:
|
||||
|
||||
- deprecated config `generators` is now deleted and fully replaced with `techdocs.generator`
|
||||
- deprecated config `generators.techdocs` is now deleted and fully replaced with `techdocs.generator.runIn`
|
||||
- deprecated config `techdocs.requestUrl` is now deleted
|
||||
- deprecated config `techdocs.storageUrl` is now deleted
|
||||
- deprecated `createHttpResponse` is now deleted and calls to `/sync/:namespace/:kind/:name` needs to be done by an EventSource.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': minor
|
||||
---
|
||||
|
||||
Removed deprecated exports, including:
|
||||
|
||||
- deprecated `DocsResultListItem` is now deleted and fully replaced with `TechDocsSearchResultListItem`
|
||||
- deprecated `TechDocsPage` is now deleted and fully replaced with `TechDocsReaderPage`
|
||||
- deprecated `TechDocsPageHeader` is now deleted and fully replaced with `TechDocsReaderPageHeader`
|
||||
- deprecated `TechDocsPageHeaderProps` is now deleted and fully replaced with `TechDocsReaderPageHeaderProps`
|
||||
- deprecated `TechDocsPageRenderFunction` is now deleted and fully replaced with `TechDocsReaderPageRenderFunction`
|
||||
- deprecated config `techdocs.requestUrl` is now deleted and fully replaced with the discoveryApi
|
||||
@@ -161,11 +161,6 @@ techdocs:
|
||||
# default value is 1000
|
||||
readTimeout: 500
|
||||
|
||||
# (Optional and Legacy) TechDocs makes API calls to techdocs-backend using this URL. e.g. get docs of an entity, get metadata, etc.
|
||||
# You don't have to specify this anymore.
|
||||
|
||||
requestUrl: http://localhost:7007/api/techdocs
|
||||
|
||||
# (Optional and Legacy) Just another route in techdocs-backend where TechDocs requests the static files from. This URL uses an HTTP middleware
|
||||
# to serve files from either a local directory or an External storage provider.
|
||||
# You don't have to specify this anymore.
|
||||
|
||||
@@ -7,4 +7,3 @@ backend:
|
||||
|
||||
techdocs:
|
||||
builder: 'external'
|
||||
requestUrl: http://localhost:3000/api
|
||||
|
||||
@@ -64,17 +64,11 @@ class TechDocsDevStorageApi implements TechDocsStorageApi {
|
||||
}
|
||||
|
||||
async getApiOrigin() {
|
||||
return (
|
||||
this.configApi.getOptionalString('techdocs.requestUrl') ??
|
||||
(await this.discoveryApi.getBaseUrl('techdocs'))
|
||||
);
|
||||
return await this.discoveryApi.getBaseUrl('techdocs');
|
||||
}
|
||||
|
||||
async getStorageUrl() {
|
||||
return (
|
||||
this.configApi.getOptionalString('techdocs.storageUrl') ??
|
||||
`${await this.discoveryApi.getBaseUrl('techdocs')}/static/docs`
|
||||
);
|
||||
return `${await this.discoveryApi.getBaseUrl('techdocs')}/static/docs`;
|
||||
}
|
||||
|
||||
async getBuilder() {
|
||||
@@ -134,10 +128,7 @@ class TechDocsDevApi implements TechDocsApi {
|
||||
}
|
||||
|
||||
async getApiOrigin() {
|
||||
return (
|
||||
this.configApi.getOptionalString('techdocs.requestUrl') ??
|
||||
(await this.discoveryApi.getBaseUrl('techdocs'))
|
||||
);
|
||||
return await this.discoveryApi.getBaseUrl('techdocs');
|
||||
}
|
||||
|
||||
async getEntityMetadata(_entityId: any) {
|
||||
|
||||
@@ -35,8 +35,8 @@ import { Content } from '@backstage/core-components';
|
||||
|
||||
import {
|
||||
Reader,
|
||||
TechDocsPage,
|
||||
TechDocsPageHeader,
|
||||
TechDocsReaderPage,
|
||||
TechDocsReaderPageHeader,
|
||||
} from '@backstage/plugin-techdocs';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
@@ -146,19 +146,19 @@ const DefaultTechDocsPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<TechDocsPage>
|
||||
<TechDocsReaderPage>
|
||||
{({ entityRef, onReady }) => (
|
||||
<>
|
||||
<TechDocsPageHeader
|
||||
<TechDocsReaderPageHeader
|
||||
entityRef={entityRef}
|
||||
techDocsMetadata={techDocsMetadata}
|
||||
>
|
||||
<TechDocsThemeToggle />
|
||||
</TechDocsPageHeader>
|
||||
</TechDocsReaderPageHeader>
|
||||
<TechDocsPageContent entityRef={entityRef} onReady={onReady} />
|
||||
</>
|
||||
)}
|
||||
</TechDocsPage>
|
||||
</TechDocsReaderPage>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ const PRODUCTION_CONFIG = {
|
||||
},
|
||||
techdocs: {
|
||||
builder: 'external',
|
||||
requestUrl: 'http://localhost:3000/api',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,7 +31,6 @@ const DEVELOPMENT_CONFIG = {
|
||||
},
|
||||
techdocs: {
|
||||
builder: 'external',
|
||||
requestUrl: 'http://localhost:7007/api',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Vendored
-24
@@ -46,17 +46,6 @@ export interface Config {
|
||||
pullImage?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Techdocs generator information
|
||||
* @deprecated Replaced with techdocs.generator
|
||||
*/
|
||||
generators?: {
|
||||
/**
|
||||
* @deprecated Use techdocs.generator.runIn
|
||||
*/
|
||||
techdocs: 'local' | 'docker';
|
||||
};
|
||||
|
||||
/**
|
||||
* Techdocs publisher information
|
||||
*/
|
||||
@@ -252,19 +241,6 @@ export interface Config {
|
||||
readTimeout?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @example http://localhost:7007/api/techdocs
|
||||
* @visibility frontend
|
||||
* @deprecated
|
||||
*/
|
||||
requestUrl?: string;
|
||||
|
||||
/**
|
||||
* @example http://localhost:7007/api/techdocs/static/docs
|
||||
* @deprecated
|
||||
*/
|
||||
storageUrl?: string;
|
||||
|
||||
/**
|
||||
* (Optional and not recommended) Prior to version [0.x.y] of TechDocs, docs
|
||||
* sites could only be accessed over paths with case-sensitive entity triplets
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
PluginEndpointDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { NotModifiedError } from '@backstage/errors';
|
||||
import {
|
||||
GeneratorBuilder,
|
||||
PreparerBuilder,
|
||||
@@ -31,12 +30,7 @@ import express, { Response } from 'express';
|
||||
import request from 'supertest';
|
||||
import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer';
|
||||
import { CachedEntityLoader } from './CachedEntityLoader';
|
||||
import {
|
||||
createEventStream,
|
||||
createHttpResponse,
|
||||
createRouter,
|
||||
RouterOptions,
|
||||
} from './router';
|
||||
import { createEventStream, createRouter, RouterOptions } from './router';
|
||||
import { TechDocsCache } from '../cache';
|
||||
import { DocsBuildStrategy } from './DocsBuildStrategy';
|
||||
|
||||
@@ -160,120 +154,6 @@ describe('createRouter', () => {
|
||||
});
|
||||
|
||||
describe('GET /sync/:namespace/:kind/:name', () => {
|
||||
describe('accept application/json', () => {
|
||||
it('should return not found if entity is not found', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(undefined);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/sync/default/Component/test')
|
||||
.send();
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should return not found if entity has no uid', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(
|
||||
entityWithoutMetadata,
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/sync/default/Component/test')
|
||||
.send();
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should not check for an update when shouldBuild returns false', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
|
||||
docsBuildStrategy.shouldBuild.mockResolvedValue(false);
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(entity);
|
||||
MockDocsSynchronizer.prototype.doCacheSync.mockImplementation(
|
||||
async ({ responseHandler }) =>
|
||||
responseHandler.finish({ updated: false }),
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/sync/default/Component/test')
|
||||
.send();
|
||||
|
||||
expect(response.status).toBe(304);
|
||||
});
|
||||
|
||||
it('should error if build is required and is missing preparer', async () => {
|
||||
const app = await createApp(recommendedOptions);
|
||||
|
||||
docsBuildStrategy.shouldBuild.mockResolvedValue(true);
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(entity);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/sync/default/Component/test')
|
||||
.send();
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.text).toMatch(
|
||||
/Invalid configuration\. docsBuildStrategy\.shouldBuild returned 'true', but no 'preparer' was provided to the router initialization./,
|
||||
);
|
||||
|
||||
expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should execute synchronization', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
|
||||
docsBuildStrategy.shouldBuild.mockResolvedValue(true);
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(entity);
|
||||
MockDocsSynchronizer.prototype.doSync.mockImplementation(
|
||||
async ({ responseHandler }) =>
|
||||
responseHandler.finish({ updated: true }),
|
||||
);
|
||||
|
||||
await request(app).get('/sync/default/Component/test').send();
|
||||
|
||||
expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(1);
|
||||
expect(MockDocsSynchronizer.prototype.doSync).toBeCalledWith({
|
||||
responseHandler: {
|
||||
log: expect.any(Function),
|
||||
error: expect.any(Function),
|
||||
finish: expect.any(Function),
|
||||
},
|
||||
entity,
|
||||
generators,
|
||||
preparers,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return on updated', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
|
||||
docsBuildStrategy.shouldBuild.mockResolvedValue(true);
|
||||
MockCachedEntityLoader.prototype.load.mockResolvedValue(entity);
|
||||
MockDocsSynchronizer.prototype.doSync.mockImplementation(
|
||||
async ({ responseHandler }) => {
|
||||
const { log, finish } = responseHandler;
|
||||
|
||||
log('Some log');
|
||||
|
||||
finish({ updated: true });
|
||||
},
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/sync/default/Component/test')
|
||||
.send();
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.get('content-type')).toMatch(/application\/json/);
|
||||
expect(response.text).toEqual(
|
||||
'{"message":"Docs updated or did not need updating"}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('accept text/event-stream', () => {
|
||||
it('should return not found if entity is not found', async () => {
|
||||
const app = await createApp(outOfTheBoxOptions);
|
||||
@@ -559,48 +439,3 @@ data: {"updated":true}
|
||||
expect(res.end).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createHttpResponse', () => {
|
||||
const res: jest.Mocked<Response> = {
|
||||
status: jest.fn(),
|
||||
json: jest.fn(),
|
||||
} as any;
|
||||
|
||||
let handlers: DocsSynchronizerSyncOpts;
|
||||
|
||||
beforeEach(() => {
|
||||
res.status.mockImplementation(() => res);
|
||||
handlers = createHttpResponse(res);
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should return CREATED if updated', async () => {
|
||||
handlers.finish({ updated: true });
|
||||
|
||||
expect(res.status).toBeCalledTimes(1);
|
||||
expect(res.status).toBeCalledWith(201);
|
||||
|
||||
expect(res.json).toBeCalledTimes(1);
|
||||
expect(res.json).toBeCalledWith({
|
||||
message: 'Docs updated or did not need updating',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NOT_MODIFIED if not updated', async () => {
|
||||
expect(() => handlers.finish({ updated: false })).toThrowError(
|
||||
NotModifiedError,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw custom error', async () => {
|
||||
expect(() => handlers.error(new Error('Some Error'))).toThrowError(
|
||||
/Some Error/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should ignore logs', async () => {
|
||||
expect(() => handlers.log('Some Message')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { NotFoundError, NotModifiedError } from '@backstage/errors';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import {
|
||||
GeneratorBuilder,
|
||||
getLocationForEntity,
|
||||
@@ -208,15 +208,7 @@ export async function createRouter(
|
||||
throw new NotFoundError('Entity metadata UID missing');
|
||||
}
|
||||
|
||||
let responseHandler: DocsSynchronizerSyncOpts;
|
||||
if (req.header('accept') !== 'text/event-stream') {
|
||||
console.warn(
|
||||
"The call to /sync/:namespace/:kind/:name wasn't done by an EventSource. This behavior is deprecated and will be removed soon. Make sure to update the @backstage/plugin-techdocs package in the frontend to the latest version.",
|
||||
);
|
||||
responseHandler = createHttpResponse(res);
|
||||
} else {
|
||||
responseHandler = createEventStream(res);
|
||||
}
|
||||
const responseHandler: DocsSynchronizerSyncOpts = createEventStream(res);
|
||||
|
||||
// By default, techdocs-backend will only try to build documentation for an entity if techdocs.builder is set to
|
||||
// 'local'. If set to 'external', it will assume that an external process (e.g. CI/CD pipeline
|
||||
@@ -345,26 +337,3 @@ export function createEventStream(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use event-stream implementation of the sync endpoint
|
||||
*/
|
||||
export function createHttpResponse(
|
||||
res: Response<any, any>,
|
||||
): DocsSynchronizerSyncOpts {
|
||||
return {
|
||||
log: () => {},
|
||||
error: e => {
|
||||
throw e;
|
||||
},
|
||||
finish: ({ updated }) => {
|
||||
if (!updated) {
|
||||
throw new NotModifiedError();
|
||||
}
|
||||
|
||||
res
|
||||
.status(201)
|
||||
.json({ message: 'Docs updated or did not need updating' });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -60,11 +60,10 @@ export async function startStandaloneServer(
|
||||
|
||||
logger.debug('Creating application...');
|
||||
const preparers = new Preparers();
|
||||
const directoryPreparer = new DirectoryPreparer(
|
||||
config,
|
||||
const directoryPreparer = DirectoryPreparer.fromConfig(config, {
|
||||
logger,
|
||||
mockUrlReader,
|
||||
);
|
||||
reader: mockUrlReader,
|
||||
});
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
const dockerClient = new Docker();
|
||||
|
||||
@@ -19,8 +19,6 @@ import { Writable } from 'stream';
|
||||
|
||||
// @public
|
||||
export class DirectoryPreparer implements PreparerBase {
|
||||
// @deprecated
|
||||
constructor(config: Config, _logger: Logger | null, reader: UrlReader);
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
{ logger, reader }: PreparerConfig,
|
||||
@@ -250,8 +248,6 @@ export const transformDirLocation: (
|
||||
|
||||
// @public
|
||||
export class UrlPreparer implements PreparerBase {
|
||||
// @deprecated
|
||||
constructor(reader: UrlReader, logger: Logger);
|
||||
static fromConfig({ reader, logger }: PreparerConfig): UrlPreparer;
|
||||
prepare(entity: Entity, options?: PreparerOptions): Promise<PreparerResponse>;
|
||||
}
|
||||
|
||||
@@ -52,11 +52,10 @@ const mockUrlReader: jest.Mocked<UrlReader> = {
|
||||
|
||||
describe('directory preparer', () => {
|
||||
it('should merge managed-by-location and techdocs-ref when techdocs-ref is relative', async () => {
|
||||
const directoryPreparer = new DirectoryPreparer(
|
||||
mockConfig,
|
||||
const directoryPreparer = DirectoryPreparer.fromConfig(mockConfig, {
|
||||
logger,
|
||||
mockUrlReader,
|
||||
);
|
||||
reader: mockUrlReader,
|
||||
});
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/managed-by-location':
|
||||
@@ -69,11 +68,10 @@ describe('directory preparer', () => {
|
||||
});
|
||||
|
||||
it('should reject when techdocs-ref is absolute', async () => {
|
||||
const directoryPreparer = new DirectoryPreparer(
|
||||
mockConfig,
|
||||
const directoryPreparer = DirectoryPreparer.fromConfig(mockConfig, {
|
||||
logger,
|
||||
mockUrlReader,
|
||||
);
|
||||
reader: mockUrlReader,
|
||||
});
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/managed-by-location':
|
||||
@@ -87,11 +85,10 @@ describe('directory preparer', () => {
|
||||
});
|
||||
|
||||
it('should reject when managed-by-location has an unknown type', async () => {
|
||||
const directoryPreparer = new DirectoryPreparer(
|
||||
mockConfig,
|
||||
const directoryPreparer = DirectoryPreparer.fromConfig(mockConfig, {
|
||||
logger,
|
||||
mockUrlReader,
|
||||
);
|
||||
reader: mockUrlReader,
|
||||
});
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/managed-by-location':
|
||||
|
||||
@@ -39,12 +39,6 @@ export class DirectoryPreparer implements PreparerBase {
|
||||
private readonly scmIntegrations: ScmIntegrationRegistry;
|
||||
private readonly reader: UrlReader;
|
||||
|
||||
/** @deprecated use static fromConfig method instead */
|
||||
constructor(config: Config, _logger: Logger | null, reader: UrlReader) {
|
||||
this.reader = reader;
|
||||
this.scmIntegrations = ScmIntegrations.fromConfig(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a directory preparer instance
|
||||
* @param config - A backstage config
|
||||
@@ -57,6 +51,15 @@ export class DirectoryPreparer implements PreparerBase {
|
||||
return new DirectoryPreparer(config, logger, reader);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
config: Config,
|
||||
_logger: Logger | null,
|
||||
reader: UrlReader,
|
||||
) {
|
||||
this.reader = reader;
|
||||
this.scmIntegrations = ScmIntegrations.fromConfig(config);
|
||||
}
|
||||
|
||||
/** {@inheritDoc PreparerBase.prepare} */
|
||||
async prepare(
|
||||
entity: Entity,
|
||||
|
||||
@@ -44,18 +44,17 @@ export class Preparers implements PreparerBuilder {
|
||||
): Promise<PreparerBuilder> {
|
||||
const preparers = new Preparers();
|
||||
|
||||
const urlPreparer = new UrlPreparer(reader, logger);
|
||||
const urlPreparer = UrlPreparer.fromConfig({ reader, logger });
|
||||
preparers.register('url', urlPreparer);
|
||||
|
||||
/**
|
||||
* Dir preparer is a syntactic sugar for users to define techdocs-ref annotation.
|
||||
* When using dir preparer, the docs will be fetched using URL Reader.
|
||||
*/
|
||||
const directoryPreparer = new DirectoryPreparer(
|
||||
backstageConfig,
|
||||
const directoryPreparer = DirectoryPreparer.fromConfig(backstageConfig, {
|
||||
logger,
|
||||
reader,
|
||||
);
|
||||
});
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
return preparers;
|
||||
|
||||
@@ -34,12 +34,6 @@ export class UrlPreparer implements PreparerBase {
|
||||
private readonly logger: Logger;
|
||||
private readonly reader: UrlReader;
|
||||
|
||||
/** @deprecated use static fromConfig method instead */
|
||||
constructor(reader: UrlReader, logger: Logger) {
|
||||
this.logger = logger;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a directory preparer instance
|
||||
* @param config - A URL preparer config containing the a logger and reader
|
||||
@@ -48,6 +42,11 @@ export class UrlPreparer implements PreparerBase {
|
||||
return new UrlPreparer(reader, logger);
|
||||
}
|
||||
|
||||
private constructor(reader: UrlReader, logger: Logger) {
|
||||
this.logger = logger;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/** {@inheritDoc PreparerBase.prepare} */
|
||||
async prepare(
|
||||
entity: Entity,
|
||||
|
||||
@@ -54,7 +54,6 @@ const createPublisherFromConfig = ({
|
||||
} = {}) => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'awsS3',
|
||||
awsS3: {
|
||||
|
||||
@@ -51,7 +51,6 @@ const createPublisherFromConfig = ({
|
||||
} = {}) => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'azureBlobStorage',
|
||||
azureBlobStorage: {
|
||||
|
||||
@@ -52,7 +52,6 @@ const createPublisherFromConfig = ({
|
||||
} = {}) => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'googleGcs',
|
||||
googleGcs: {
|
||||
|
||||
@@ -84,7 +84,6 @@ beforeEach(() => {
|
||||
mockFs.restore();
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'openStackSwift',
|
||||
openStackSwift: {
|
||||
@@ -114,7 +113,6 @@ describe('OpenStackSwiftPublish', () => {
|
||||
it('should reject incorrect config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'openStackSwift',
|
||||
openStackSwift: {
|
||||
|
||||
@@ -37,11 +37,7 @@ describe('Publisher', () => {
|
||||
});
|
||||
|
||||
it('should create local publisher by default', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
},
|
||||
});
|
||||
const mockConfig = new ConfigReader({});
|
||||
|
||||
const publisher = await Publisher.fromConfig(mockConfig, {
|
||||
logger,
|
||||
@@ -53,7 +49,6 @@ describe('Publisher', () => {
|
||||
it('should create local publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'local',
|
||||
},
|
||||
@@ -70,7 +65,6 @@ describe('Publisher', () => {
|
||||
it('should create google gcs publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'googleGcs',
|
||||
googleGcs: {
|
||||
@@ -91,7 +85,6 @@ describe('Publisher', () => {
|
||||
it('should create AWS S3 publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'awsS3',
|
||||
awsS3: {
|
||||
@@ -115,7 +108,6 @@ describe('Publisher', () => {
|
||||
it('should create Azure Blob Storage publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'azureBlobStorage',
|
||||
azureBlobStorage: {
|
||||
@@ -143,7 +135,6 @@ describe('Publisher', () => {
|
||||
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'azureBlobStorage',
|
||||
azureBlobStorage: {
|
||||
@@ -166,7 +157,6 @@ describe('Publisher', () => {
|
||||
it('should create Open Stack Swift publisher from config', async () => {
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7007',
|
||||
publisher: {
|
||||
type: 'openStackSwift',
|
||||
openStackSwift: {
|
||||
|
||||
@@ -41,11 +41,6 @@ export type DocsCardGridProps = {
|
||||
entities: Entity[] | undefined;
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const DocsResultListItem: (
|
||||
props: TechDocsSearchResultListItemProps,
|
||||
) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const DocsTable: {
|
||||
(props: DocsTableProps): JSX.Element | null;
|
||||
@@ -185,6 +180,7 @@ export type TabsConfig = TabConfig[];
|
||||
|
||||
// @public
|
||||
export interface TechDocsApi {
|
||||
// (undocumented)
|
||||
getApiOrigin(): Promise<string>;
|
||||
// (undocumented)
|
||||
getEntityMetadata(
|
||||
@@ -243,23 +239,9 @@ export type TechDocsMetadata = {
|
||||
site_description: string;
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const TechDocsPage: (props: TechDocsReaderPageProps) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const TechdocsPage: () => JSX.Element;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const TechDocsPageHeader: (
|
||||
props: TechDocsReaderPageHeaderProps,
|
||||
) => JSX.Element;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type TechDocsPageHeaderProps = TechDocsReaderPageHeaderProps;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type TechDocsPageRenderFunction = TechDocsReaderPageRenderFunction;
|
||||
|
||||
// @public
|
||||
export const TechDocsPageWrapper: (
|
||||
props: TechDocsPageWrapperProps,
|
||||
@@ -348,6 +330,7 @@ export type TechDocsSearchResultListItemProps = {
|
||||
|
||||
// @public
|
||||
export interface TechDocsStorageApi {
|
||||
// (undocumented)
|
||||
getApiOrigin(): Promise<string>;
|
||||
// (undocumented)
|
||||
getBaseUrl(
|
||||
|
||||
Vendored
-7
@@ -33,13 +33,6 @@ export interface Config {
|
||||
*/
|
||||
legacyUseCaseSensitiveTripletPaths?: boolean;
|
||||
|
||||
/**
|
||||
* @example http://localhost:7007/api/techdocs
|
||||
* @visibility frontend
|
||||
* @deprecated
|
||||
*/
|
||||
requestUrl?: string;
|
||||
|
||||
sanitizer?: {
|
||||
/**
|
||||
* Allows iframe tag only for listed hosts
|
||||
|
||||
@@ -49,9 +49,6 @@ export type SyncResult = 'cached' | 'updated';
|
||||
* @public
|
||||
*/
|
||||
export interface TechDocsStorageApi {
|
||||
/**
|
||||
* Set to techdocs.requestUrl as the URL for techdocs-backend API.
|
||||
*/
|
||||
getApiOrigin(): Promise<string>;
|
||||
getStorageUrl(): Promise<string>;
|
||||
getBuilder(): Promise<string>;
|
||||
@@ -73,9 +70,6 @@ export interface TechDocsStorageApi {
|
||||
* @public
|
||||
*/
|
||||
export interface TechDocsApi {
|
||||
/**
|
||||
* Set to techdocs.requestUrl as the URL for techdocs-backend API.
|
||||
*/
|
||||
getApiOrigin(): Promise<string>;
|
||||
getTechDocsMetadata(entityId: CompoundEntityRef): Promise<TechDocsMetadata>;
|
||||
getEntityMetadata(
|
||||
|
||||
@@ -35,9 +35,7 @@ const mockEntity = {
|
||||
|
||||
describe('TechDocsStorageClient', () => {
|
||||
const mockBaseUrl = 'http://backstage:9191/api/techdocs';
|
||||
const configApi = new MockConfigApi({
|
||||
techdocs: { requestUrl: 'http://backstage:9191/api/techdocs' },
|
||||
});
|
||||
const configApi = new MockConfigApi({});
|
||||
const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl);
|
||||
const identityApi: jest.Mocked<IdentityApi> = {
|
||||
getCredentials: jest.fn(),
|
||||
|
||||
@@ -47,10 +47,7 @@ export class TechDocsClient implements TechDocsApi {
|
||||
}
|
||||
|
||||
async getApiOrigin(): Promise<string> {
|
||||
return (
|
||||
this.configApi.getOptionalString('techdocs.requestUrl') ??
|
||||
(await this.discoveryApi.getBaseUrl('techdocs'))
|
||||
);
|
||||
return await this.discoveryApi.getBaseUrl('techdocs');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,10 +123,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
|
||||
}
|
||||
|
||||
async getApiOrigin(): Promise<string> {
|
||||
return (
|
||||
this.configApi.getOptionalString('techdocs.requestUrl') ??
|
||||
(await this.discoveryApi.getBaseUrl('techdocs'))
|
||||
);
|
||||
return await this.discoveryApi.getBaseUrl('techdocs');
|
||||
}
|
||||
|
||||
async getStorageUrl(): Promise<string> {
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { LegacyTechDocsHome } from './LegacyTechDocsHome';
|
||||
|
||||
import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
|
||||
import { ConfigApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
const mockCatalogApi = {
|
||||
getEntityByRef: jest.fn(),
|
||||
getEntities: async () => ({
|
||||
items: [
|
||||
{
|
||||
apiVersion: 'version',
|
||||
kind: 'User',
|
||||
metadata: {
|
||||
name: 'owned',
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
} as Partial<CatalogApi>;
|
||||
|
||||
describe('Legacy TechDocs Home', () => {
|
||||
const configApi: ConfigApi = new ConfigReader({
|
||||
organization: {
|
||||
name: 'My Company',
|
||||
},
|
||||
});
|
||||
|
||||
const apiRegistry = TestApiRegistry.from(
|
||||
[catalogApiRef, mockCatalogApi],
|
||||
[configApiRef, configApi],
|
||||
);
|
||||
|
||||
it('should render a TechDocs home page', async () => {
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<LegacyTechDocsHome />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Header
|
||||
expect(await screen.findByText('Documentation')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(/Documentation available in My Company/i),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Explore Content
|
||||
expect(await screen.findByTestId('docs-explore')).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link TechDocsCustomHome} instead.
|
||||
*/
|
||||
export const LegacyTechDocsHome = () => {
|
||||
const tabsConfig = [
|
||||
{
|
||||
label: 'Overview',
|
||||
panels: [
|
||||
{
|
||||
title: 'Overview',
|
||||
description:
|
||||
'Explore your internal technical ecosystem through documentation.',
|
||||
panelType: 'DocsCardGrid' as PanelType,
|
||||
filterPredicate: () => true,
|
||||
},
|
||||
// uncomment this if you would like to have a secondary panel with owned documents
|
||||
// {
|
||||
// title: 'Owned',
|
||||
// description: 'Explore your owned internal documentation.',
|
||||
// panelType: 'DocsCardGrid' as PanelType,
|
||||
// filterPredicate: 'ownedByUser',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Owned Documents',
|
||||
panels: [
|
||||
{
|
||||
title: 'Owned documents',
|
||||
description: 'Access your documentation.',
|
||||
panelType: 'DocsTable' as PanelType,
|
||||
// ownedByUser filters out entities owned by signed in user
|
||||
filterPredicate: 'ownedByUser',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
return <TechDocsCustomHome tabsConfig={tabsConfig} />;
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { techdocsApiRef } from '../../api';
|
||||
import { TechDocsNotFound } from './TechDocsNotFound';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { Page, Content } from '@backstage/core-components';
|
||||
import { Reader } from './Reader';
|
||||
import { TechDocsReaderPageHeader } from './TechDocsReaderPageHeader';
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link TechDocsReaderPage} instead.
|
||||
*/
|
||||
export const LegacyTechDocsPage = () => {
|
||||
const [documentReady, setDocumentReady] = useState<boolean>(false);
|
||||
const { namespace, kind, name } = useParams();
|
||||
|
||||
const techdocsApi = useApi(techdocsApiRef);
|
||||
|
||||
const { value: techdocsMetadataValue } = useAsync(() => {
|
||||
if (documentReady) {
|
||||
return techdocsApi.getTechDocsMetadata({ kind, namespace, name });
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}, [kind, namespace, name, techdocsApi, documentReady]);
|
||||
|
||||
const { value: entityMetadataValue, error: entityMetadataError } =
|
||||
useAsync(() => {
|
||||
return techdocsApi.getEntityMetadata({ kind, namespace, name });
|
||||
}, [kind, namespace, name, techdocsApi]);
|
||||
|
||||
const onReady = useCallback(() => {
|
||||
setDocumentReady(true);
|
||||
}, [setDocumentReady]);
|
||||
|
||||
if (entityMetadataError) {
|
||||
return <TechDocsNotFound errorMessage={entityMetadataError.message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Page themeId="documentation">
|
||||
<TechDocsReaderPageHeader
|
||||
techDocsMetadata={techdocsMetadataValue}
|
||||
entityMetadata={entityMetadataValue}
|
||||
entityRef={{
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
}}
|
||||
/>
|
||||
<Content data-testid="techdocs-content">
|
||||
<Reader
|
||||
onReady={onReady}
|
||||
entityRef={{
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
}}
|
||||
/>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -18,12 +18,13 @@ import React, { useCallback, useState } from 'react';
|
||||
import { useOutlet } from 'react-router';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { Reader } from './Reader';
|
||||
import { TechDocsReaderPageHeader } from './TechDocsReaderPageHeader';
|
||||
import { techdocsApiRef } from '../../api';
|
||||
import { LegacyTechDocsPage } from './LegacyTechDocsPage';
|
||||
import { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { useApi, useApp } from '@backstage/core-plugin-api';
|
||||
import { Page } from '@backstage/core-components';
|
||||
import { Page, Content } from '@backstage/core-components';
|
||||
|
||||
/**
|
||||
* Helper function that gives the children of {@link TechDocsReaderPage} access to techdocs and entity metadata
|
||||
@@ -79,7 +80,32 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => {
|
||||
|
||||
if (entityMetadataError) return <NotFoundErrorPage />;
|
||||
|
||||
if (!children) return outlet || <LegacyTechDocsPage />;
|
||||
if (!children)
|
||||
return (
|
||||
outlet || (
|
||||
<Page themeId="documentation">
|
||||
<TechDocsReaderPageHeader
|
||||
techDocsMetadata={techdocsMetadataValue}
|
||||
entityMetadata={entityMetadataValue}
|
||||
entityRef={{
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
}}
|
||||
/>
|
||||
<Content data-testid="techdocs-content">
|
||||
<Reader
|
||||
onReady={onReady}
|
||||
entityRef={{
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
}}
|
||||
/>
|
||||
</Content>
|
||||
</Page>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<Page themeId="documentation">
|
||||
@@ -94,16 +120,3 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => {
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link TechDocsReaderPage} instead
|
||||
*/
|
||||
export const TechDocsPage = TechDocsReaderPage;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link TechDocsReaderPageRenderFunction} instead
|
||||
*/
|
||||
|
||||
export type TechDocsPageRenderFunction = TechDocsReaderPageRenderFunction;
|
||||
|
||||
@@ -122,16 +122,3 @@ export const TechDocsReaderPageHeader = (
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link TechDocsReaderPageHeader} instead
|
||||
*/
|
||||
export const TechDocsPageHeader = TechDocsReaderPageHeader;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link TechDocsReaderPageHeader} instead
|
||||
*/
|
||||
|
||||
export type TechDocsPageHeaderProps = TechDocsReaderPageHeaderProps;
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
export * from './Reader';
|
||||
export type {
|
||||
TechDocsReaderPageProps,
|
||||
TechDocsPageRenderFunction,
|
||||
TechDocsReaderPageRenderFunction,
|
||||
} from './TechDocsReaderPage';
|
||||
export { TechDocsPage } from './TechDocsReaderPage';
|
||||
export * from './TechDocsReaderPageHeader';
|
||||
export * from './TechDocsStateIndicator';
|
||||
|
||||
|
||||
@@ -101,9 +101,3 @@ export const TechDocsSearchResultListItem = (
|
||||
</LinkWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated use {@link TechDocsSearchResultListItem} instead
|
||||
*/
|
||||
export const DocsResultListItem = TechDocsSearchResultListItem;
|
||||
|
||||
Reference in New Issue
Block a user