Merge branch 'master' of https://github.com/spotify/backstage into lintMod

This commit is contained in:
Debajyoti Halder
2021-02-03 11:05:45 +05:30
43 changed files with 396 additions and 260 deletions
@@ -13,34 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-restricted-syntax */
import fs from 'fs-extra';
import path from 'path';
import {
getVoidLogger,
PluginEndpointDiscovery,
} from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import mockFs from 'mock-fs';
import * as os from 'os';
import { LocalPublish } from './local';
jest.mock('fs-extra', () => {
const fsOriginal = jest.requireActual('fs-extra');
return {
...fsOriginal,
access: jest.fn().mockImplementation((paths, checkType, callback) => {
if (
paths.includes('http://localhost:7000/static') &&
checkType === fs.constants.F_OK
) {
callback();
} else {
callback(new Error());
}
}),
};
});
const createMockEntity = (annotations = {}) => {
return {
apiVersion: 'version',
@@ -56,43 +37,33 @@ const createMockEntity = (annotations = {}) => {
const logger = getVoidLogger();
const tmpDir =
os.platform() === 'win32' ? 'C:\\tmp\\generatedDir' : '/tmp/generatedDir';
describe('local publisher', () => {
it('should publish generated documentation dir', async () => {
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7000'),
getExternalBaseUrl: jest.fn(),
};
const mockConfig = new ConfigReader({
techdocs: {
requestUrl: 'http://localhost:7000',
storageUrl: 'http://localhost:7000/static/docs',
mockFs({
[tmpDir]: {
'index.html': '',
},
});
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
getBaseUrl: jest
.fn()
.mockResolvedValue('http://localhost:7000/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
const mockConfig = new ConfigReader({});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
const mockEntity = createMockEntity();
const tempDir = fs.mkdtempSync(`${__dirname}/test-component-folder-`);
expect(tempDir).toBeTruthy();
fs.closeSync(fs.openSync(path.join(tempDir, '/mock-file'), 'w'));
await publisher.publish({ entity: mockEntity, directory: tempDir });
const publishDir = path.resolve(
__dirname,
`../../../../../plugins/techdocs-backend/static/docs/${mockEntity.metadata.name}`,
);
const resultDir = path.resolve(
__dirname,
`../../../../../plugins/techdocs-backend/static/docs/default/${mockEntity.kind}/${mockEntity.metadata.name}`,
);
expect(fs.existsSync(resultDir)).toBeTruthy();
expect(fs.existsSync(path.join(resultDir, '/mock-file'))).toBeTruthy();
await publisher.publish({ entity: mockEntity, directory: tmpDir });
expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true);
fs.removeSync(publishDir);
fs.removeSync(tempDir);
mockFs.restore();
});
});
@@ -13,18 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fetch from 'cross-fetch';
import {
PluginEndpointDiscovery,
resolvePackagePath,
} from '@backstage/backend-common';
import { Entity, EntityName } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import express from 'express';
import fs from 'fs-extra';
import path from 'path';
import os from 'os';
import path from 'path';
import { Logger } from 'winston';
import { Entity, EntityName } from '@backstage/catalog-model';
import {
resolvePackagePath,
PluginEndpointDiscovery,
} from '@backstage/backend-common';
import { Config } from '@backstage/config';
import {
PublisherBase,
PublishRequest,
@@ -52,17 +51,14 @@ try {
* called "static" at the root of techdocs-backend plugin.
*/
export class LocalPublish implements PublisherBase {
private readonly config: Config;
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(
config: Config,
logger: Logger,
discovery: PluginEndpointDiscovery,
// @ts-ignore
private readonly config: Config,
private readonly logger: Logger,
private readonly discovery: PluginEndpointDiscovery,
) {
this.config = config;
this.logger = logger;
@@ -107,34 +103,25 @@ export class LocalPublish implements PublisherBase {
});
}
fetchTechDocsMetadata(entityName: EntityName): Promise<TechDocsMetadata> {
return new Promise((resolve, reject) => {
this.discovery.getBaseUrl('techdocs').then(techdocsApiUrl => {
const storageUrl = new URL(
new URL(this.config.getString('techdocs.storageUrl')).pathname,
techdocsApiUrl,
).toString();
async fetchTechDocsMetadata(
entityName: EntityName,
): Promise<TechDocsMetadata> {
const metadataPath = path.join(
staticDocsDir,
entityName.namespace,
entityName.kind,
entityName.name,
'techdocs_metadata.json',
);
const entityRootDir = `${entityName.namespace}/${entityName.kind}/${entityName.name}`;
const metadataURL = `${storageUrl}/${entityRootDir}/techdocs_metadata.json`;
fetch(metadataURL)
.then(response =>
response
.json()
.then(techdocsMetadata => resolve(techdocsMetadata))
.catch(err => {
reject(
`Unable to parse metadata JSON for ${entityRootDir}. Error: ${err}`,
);
}),
)
.catch(err => {
reject(
`Unable to fetch metadata for ${entityRootDir}. Error ${err}`,
);
});
});
});
try {
return await fs.readJson(metadataPath);
} catch (err) {
this.logger.error(
`Unable to read techdocs_metadata.json at ${metadataPath}. Error: ${err}`,
);
throw new Error(err.message);
}
}
docsRouter(): express.Handler {
@@ -143,24 +130,21 @@ export class LocalPublish implements PublisherBase {
async hasDocsBeenGenerated(entity: Entity): Promise<boolean> {
const namespace = entity.metadata.namespace ?? 'default';
return new Promise(resolve => {
this.discovery.getBaseUrl('techdocs').then(techdocsApiUrl => {
const storageUrl = new URL(
new URL(this.config.getString('techdocs.storageUrl')).pathname,
techdocsApiUrl,
).toString();
const entityRootDir = `${namespace}/${entity.kind}/${entity.metadata.name}`;
const indexHtmlUrl = `${storageUrl}/${entityRootDir}/index.html`;
// Check if the file exists
fs.access(indexHtmlUrl, fs.constants.F_OK, err => {
if (err) {
resolve(false);
} else {
resolve(true);
}
});
});
});
const indexHtmlPath = path.join(
staticDocsDir,
namespace,
entity.kind,
entity.metadata.name,
'index.html',
);
// Check if the file exists
try {
fs.access(indexHtmlPath, fs.constants.F_OK);
return true;
} catch (err) {
return false;
}
}
}