Merge branch 'master' of github.com:spotify/backstage into shmidt-i/app-catalog-tabs-routes-everything-is-connected

This commit is contained in:
Ivan Shmidt
2020-09-01 10:37:18 +02:00
23 changed files with 432 additions and 505 deletions
@@ -75,12 +75,7 @@ const columns: TableColumn<Entity>[] = [
<>
{entity.metadata.tags &&
entity.metadata.tags.map(t => (
<Chip
key={t}
label={t}
color="secondary"
style={{ marginBottom: '0px' }}
/>
<Chip key={t} label={t} style={{ marginBottom: '0px' }} />
))}
</>
),
@@ -64,7 +64,7 @@ export const ScaffolderPage: React.FC<{}> = () => {
}, [error, errorApi]);
return (
<Page theme={pageTheme.other}>
<Page theme={pageTheme.home}>
<Header
pageTitleOverride="Create a new component"
title={
@@ -95,7 +95,7 @@ export const ScaffolderPage: React.FC<{}> = () => {
<Typography variant="body2">
Shoot! Looks like you don't have any templates. Check out the
documentation{' '}
<Link href="docs/backstage/features/software-templates/adding-templates">
<Link href="https://backstage.io/docs/features/software-templates/adding-templates">
here!
</Link>
</Typography>
@@ -136,7 +136,7 @@ export const TemplatePage = () => {
}
return (
<Page theme={pageTheme.other}>
<Page theme={pageTheme.home}>
<Header
pageTitleOverride="Create a new component"
title={
@@ -0,0 +1,47 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { Generators, TechdocsGenerator } from './';
import { getVoidLogger } from '@backstage/backend-common';
const logger = getVoidLogger();
const mockEntity = {
apiVersion: 'version',
kind: 'TestKind',
metadata: {
name: 'testName',
},
};
describe('generators', () => {
it('should return error if no generator is registered', async () => {
const generators = new Generators();
expect(() => generators.get(mockEntity)).toThrowError(
'No generator registered for entity: "techdocs"',
);
});
it('should return correct registered generator', async () => {
const generators = new Generators();
const techdocs = new TechdocsGenerator(logger);
generators.register('techdocs', techdocs);
expect(generators.get(mockEntity)).toBe(techdocs);
});
});
@@ -15,32 +15,29 @@
*/
import {
GeneratorBase,
SupportedGeneratorKey,
GeneratorBuilder,
} from './types';
import { Entity } from '@backstage/catalog-model';
import { getGeneratorKey } from './helpers';
export class Generators implements GeneratorBuilder {
private generatorMap = new Map<SupportedGeneratorKey, GeneratorBase>();
register(templaterKey: SupportedGeneratorKey, templater: GeneratorBase) {
this.generatorMap.set(templaterKey, templater);
}
get(entity: Entity): GeneratorBase {
const generatorKey = getGeneratorKey(entity);
const generator = this.generatorMap.get(generatorKey);
if (!generator) {
throw new Error(
`No generator registered for entity: "${generatorKey}"`,
);
}
return generator;
}
GeneratorBase,
SupportedGeneratorKey,
GeneratorBuilder,
} from './types';
import { Entity } from '@backstage/catalog-model';
import { getGeneratorKey } from './helpers';
export class Generators implements GeneratorBuilder {
private generatorMap = new Map<SupportedGeneratorKey, GeneratorBase>();
register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) {
this.generatorMap.set(generatorKey, generator);
}
get(entity: Entity): GeneratorBase {
const generatorKey = getGeneratorKey(entity);
const generator = this.generatorMap.get(generatorKey);
if (!generator) {
throw new Error(`No generator registered for entity: "${generatorKey}"`);
}
return generator;
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2020 Spotify AB
*
* 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 Stream, { PassThrough } from 'stream';
import os from 'os';
import Docker from 'dockerode';
import { runDockerContainer, getGeneratorKey } from './helpers';
const mockEntity = {
apiVersion: 'version',
kind: 'TestKind',
metadata: {
name: 'testName',
},
};
const mockDocker = new Docker() as jest.Mocked<Docker>;
describe('helpers', () => {
describe('getGeneratorKey', () => {
it('should return techdocs as the only generator key', () => {
const key = getGeneratorKey(mockEntity);
expect(key).toBe('techdocs');
});
});
describe('runDockerContainer', () => {
beforeEach(() => {
jest.spyOn(mockDocker, 'pull').mockImplementation((async (
_image: string,
_something: any,
handler: (err: Error | undefined, stream: PassThrough) => void,
) => {
const mockStream = new PassThrough();
handler(undefined, mockStream);
mockStream.end();
}) as any);
jest
.spyOn(mockDocker, 'run')
.mockResolvedValue([{ Error: null, StatusCode: 0 }]);
});
const imageName = 'spotify/techdocs';
const args = ['build', '-d', '/result'];
const docsDir = os.tmpdir();
const resultDir = os.tmpdir();
it('should pull the techdocs docker container', async () => {
await runDockerContainer({
imageName,
args,
docsDir,
resultDir,
dockerClient: mockDocker,
});
expect(mockDocker.pull).toHaveBeenCalledWith(
imageName,
{},
expect.any(Function),
);
});
it('should run the techdocs docker container', async () => {
await runDockerContainer({
imageName,
args,
docsDir,
resultDir,
dockerClient: mockDocker,
});
expect(mockDocker.run).toHaveBeenCalledWith(
imageName,
args,
expect.any(Stream),
{
Volumes: {
'/content': {},
'/result': {},
},
WorkingDir: '/content',
HostConfig: {
Binds: [`${docsDir}:/content`, `${resultDir}:/result`],
},
},
);
});
});
});