cli/new: update template loading to match new design
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -18,26 +18,56 @@ import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { loadPortableTemplate } from './loadPortableTemplate';
|
||||
|
||||
describe('loadTemplate', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockDir.clear();
|
||||
});
|
||||
|
||||
it('should load a valid template', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `
|
||||
template: template1
|
||||
mockDir.setContent({
|
||||
'path/to/template.yaml': `
|
||||
name: template1
|
||||
role: frontend-plugin
|
||||
parameters:
|
||||
foo: bar
|
||||
`,
|
||||
'path/to/template1/hello.txt': 'hello world',
|
||||
},
|
||||
'path/to/hello.txt': 'hello world',
|
||||
});
|
||||
|
||||
const result = await loadPortableTemplate({
|
||||
id: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template.yaml'),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
name: 'template1',
|
||||
role: 'frontend-plugin',
|
||||
files: [{ path: 'hello.txt', content: 'hello world' }],
|
||||
parameters: { foo: 'bar' },
|
||||
templateValues: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should load a valid template with files in a separate dir', async () => {
|
||||
mockDir.setContent({
|
||||
'path/to/template.yaml': `
|
||||
name: template1
|
||||
role: frontend-plugin
|
||||
files: content
|
||||
parameters:
|
||||
foo: bar
|
||||
`,
|
||||
'path/to/content/hello.txt': 'hello world',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
id: 'template1',
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template.yaml'),
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
name: 'template1',
|
||||
role: 'frontend-plugin',
|
||||
files: [{ path: 'hello.txt', content: 'hello world' }],
|
||||
parameters: { foo: 'bar' },
|
||||
@@ -46,11 +76,11 @@ describe('loadTemplate', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if template file does not exist', async () => {
|
||||
const mockDir = createMockDirectory();
|
||||
mockDir.setContent({});
|
||||
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
@@ -59,15 +89,13 @@ describe('loadTemplate', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if template definition is invalid', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `invalid: definition`,
|
||||
},
|
||||
mockDir.setContent({
|
||||
'path/to/template1.yaml': `invalid: definition`,
|
||||
});
|
||||
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
@@ -78,34 +106,24 @@ describe('loadTemplate', () => {
|
||||
it('should throw an error if target is a remote URL', async () => {
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
target: 'http://example.com',
|
||||
}),
|
||||
).rejects.toThrow('Remote templates are not supported yet');
|
||||
});
|
||||
|
||||
it('should throw an error if target directory does not exist', async () => {
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
name: 'template1',
|
||||
target: 'http://example.com',
|
||||
}),
|
||||
).rejects.toThrow('Remote templates are not supported yet');
|
||||
});
|
||||
|
||||
it('should throw an error if the package role is invalid', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `
|
||||
template: template1
|
||||
role: invalid-role
|
||||
`,
|
||||
},
|
||||
mockDir.setContent({
|
||||
'path/to/template1.yaml': `
|
||||
name: x
|
||||
role: invalid-role
|
||||
files: template1
|
||||
`,
|
||||
});
|
||||
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
@@ -116,18 +134,17 @@ describe('loadTemplate', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if template directory does not exist', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `
|
||||
template: template1
|
||||
role: frontend-plugin
|
||||
`,
|
||||
},
|
||||
mockDir.setContent({
|
||||
'path/to/template1.yaml': `
|
||||
name: x
|
||||
role: frontend-plugin
|
||||
files: template1
|
||||
`,
|
||||
});
|
||||
|
||||
await expect(
|
||||
loadPortableTemplate({
|
||||
id: 'template1',
|
||||
name: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
|
||||
@@ -32,8 +32,10 @@ import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
const templateDefinitionSchema = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
role: z.enum(TEMPLATE_ROLES),
|
||||
template: z.string(),
|
||||
description: z.string().optional(),
|
||||
files: z.string().optional(),
|
||||
parameters: z.record(z.string()).optional(),
|
||||
templateValues: z.record(z.string()).optional(),
|
||||
})
|
||||
@@ -63,9 +65,14 @@ export async function loadPortableTemplate(
|
||||
);
|
||||
}
|
||||
|
||||
const { role, template, parameters = {}, templateValues = {} } = parsed.data;
|
||||
const {
|
||||
role,
|
||||
files = '.',
|
||||
parameters = {},
|
||||
templateValues = {},
|
||||
} = parsed.data;
|
||||
|
||||
const templatePath = resolvePath(dirname(pointer.target), template);
|
||||
const templatePath = resolvePath(dirname(pointer.target), files);
|
||||
const filePaths = await recursiveReaddir(templatePath).catch(error => {
|
||||
throw new ForwardedError(
|
||||
`Failed to load template contents from '${templatePath}'`,
|
||||
@@ -73,10 +80,13 @@ export async function loadPortableTemplate(
|
||||
);
|
||||
});
|
||||
|
||||
const files = new Array<PortableTemplateFile>();
|
||||
const loadedFiles = new Array<PortableTemplateFile>();
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
const path = relativePath(templatePath, filePath);
|
||||
if (filePath === pointer.target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const content = await fs.readFile(filePath, 'utf-8').catch(error => {
|
||||
throw new ForwardedError(
|
||||
@@ -86,11 +96,21 @@ export async function loadPortableTemplate(
|
||||
});
|
||||
|
||||
if (path.endsWith('.hbs')) {
|
||||
files.push({ path: path.slice(0, -4), content, syntax: 'handlebars' });
|
||||
loadedFiles.push({
|
||||
path: path.slice(0, -4),
|
||||
content,
|
||||
syntax: 'handlebars',
|
||||
});
|
||||
} else {
|
||||
files.push({ path, content });
|
||||
loadedFiles.push({ path, content });
|
||||
}
|
||||
}
|
||||
|
||||
return { id: pointer.id, role, files, parameters, templateValues };
|
||||
return {
|
||||
name: pointer.name,
|
||||
role,
|
||||
files: loadedFiles,
|
||||
parameters,
|
||||
templateValues,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,15 +38,15 @@ export async function selectTemplateInteractively(
|
||||
message: 'What do you want to create?',
|
||||
choices: config.templatePointers.map(t =>
|
||||
t.description
|
||||
? { name: `${t.id} - ${t.description}`, value: t.id }
|
||||
: t.id,
|
||||
? { name: `${t.name} - ${t.description}`, value: t.name }
|
||||
: t.name,
|
||||
),
|
||||
},
|
||||
]);
|
||||
selectedId = answers.id;
|
||||
}
|
||||
|
||||
const template = config.templatePointers.find(t => t.id === selectedId);
|
||||
const template = config.templatePointers.find(t => t.name === selectedId);
|
||||
if (!template) {
|
||||
throw new Error(`Template '${selectedId}' not found`);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export type PortableTemplateFile = {
|
||||
};
|
||||
|
||||
export type PortableTemplate = {
|
||||
id: string;
|
||||
name: string;
|
||||
role: PortableTemplateRole;
|
||||
files: PortableTemplateFile[];
|
||||
parameters: Record<string, string>;
|
||||
|
||||
Reference in New Issue
Block a user