Add examples for fetch:rails scaffolder action & improve related tests

Signed-off-by: parmar-abhinav <abhinav.parmar@infosys.com>
This commit is contained in:
parmar-abhinav
2024-07-13 17:30:14 +05:30
parent 22cb1fcf2a
commit 449def7c2c
5 changed files with 671 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-rails': patch
---
Add examples for fetch:rails scaffolder action & improve related tests
@@ -51,7 +51,8 @@
"@backstage/plugin-scaffolder-node": "workspace:^",
"@backstage/types": "workspace:^",
"command-exists": "^1.2.9",
"fs-extra": "^11.0.0"
"fs-extra": "^11.0.0",
"yaml": "^2.0.0"
},
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
@@ -0,0 +1,292 @@
/*
* 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.
*/
const mockRailsTemplater = { run: jest.fn() };
jest.mock('@backstage/plugin-scaffolder-node', () => ({
...jest.requireActual('@backstage/plugin-scaffolder-node'),
fetchContents: jest.fn(),
}));
jest.mock('./railsNewRunner', () => {
return {
RailsNewRunner: jest.fn().mockImplementation(() => {
return mockRailsTemplater;
}),
};
});
import { ContainerRunner, UrlReader } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { ScmIntegrations } from '@backstage/integration';
import { resolve as resolvePath } from 'path';
import { createFetchRailsAction } from './index';
import { fetchContents } from '@backstage/plugin-scaffolder-node';
import { createMockDirectory } from '@backstage/backend-test-utils';
import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';
import { Writable } from 'stream';
import { examples } from './index.examples';
import yaml from 'yaml';
describe('fetch:rails', () => {
const mockDir = createMockDirectory();
const integrations = ScmIntegrations.fromConfig(
new ConfigReader({
integrations: {
azure: [
{ host: 'dev.azure.com', token: 'tokenlols' },
{ host: 'myazurehostnotoken.com' },
],
},
}),
);
const mockContext = createMockActionContext({
input: {
url: 'https://rubyonrails.org/generator',
targetPath: 'something',
values: {
help: 'me',
},
},
templateInfo: {
baseUrl: 'somebase',
entityRef: 'template:default/myTemplate',
},
workspacePath: mockDir.path,
});
const mockReader: UrlReader = {
readUrl: jest.fn(),
readTree: jest.fn(),
search: jest.fn(),
};
const containerRunner: ContainerRunner = {
runContainer: jest.fn(),
};
const action = createFetchRailsAction({
integrations,
reader: mockReader,
containerRunner,
allowedImageNames: ['foo/rails-custom-image'],
});
beforeEach(() => {
mockDir.setContent({
result: '{}',
});
jest.clearAllMocks();
});
it(`should ${examples[0].description}`, async () => {
const input = yaml.parse(examples[0].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[1].description}`, async () => {
const input = yaml.parse(examples[1].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[2].description}`, async () => {
const input = yaml.parse(examples[2].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[3].description}`, async () => {
const input = yaml.parse(examples[3].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[4].description}`, async () => {
const input = yaml.parse(examples[4].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[5].description}`, async () => {
const input = yaml.parse(examples[5].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[6].description}`, async () => {
const input = yaml.parse(examples[6].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[7].description}`, async () => {
const input = yaml.parse(examples[7].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[8].description}`, async () => {
const input = yaml.parse(examples[8].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
it(`should ${examples[9].description}`, async () => {
const input = yaml.parse(examples[9].example).steps[0].input;
await action.handler({
...mockContext,
input: {
...mockContext.input,
...input,
},
});
expect(fetchContents).toHaveBeenCalledWith({
reader: mockReader,
integrations,
baseUrl: mockContext.templateInfo?.baseUrl,
fetchUrl: input.url,
outputPath: resolvePath(mockContext.workspacePath),
});
});
});
@@ -0,0 +1,370 @@
/*
* 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 { TemplateExample } from '@backstage/plugin-scaffolder-node';
import yaml from 'yaml';
export const examples: TemplateExample[] = [
{
description: 'Fetch and template a minimal Rails app',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: true,
skipBundle: true,
skipWebpackInstall: true,
skipTest: true,
skipActionCable: true,
skipActionMailer: true,
skipActionMailbox: true,
skipActiveStorage: true,
skipActionText: true,
skipActiveRecord: true,
force: true,
api: true,
webpacker: 'react',
database: 'postgresql',
railsVersion: 'dev',
},
},
},
},
],
}),
},
{
description: 'Fetch and template a Rails app with custom options',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
targetPath: 'custom-path',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: false,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: false,
api: false,
webpacker: 'vue',
database: 'mysql',
railsVersion: 'edge',
},
},
},
},
],
}),
},
{
description: 'Fetch and template a Rails app with Docker image',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: true,
skipBundle: true,
skipWebpackInstall: true,
skipTest: true,
skipActionCable: true,
skipActionMailer: true,
skipActionMailbox: true,
skipActiveStorage: true,
skipActionText: true,
skipActiveRecord: true,
force: true,
api: true,
webpacker: 'react',
database: 'postgresql',
railsVersion: 'dev',
},
imageName: 'custom/rails-image',
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with different database and action mailer settings',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: true,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: true,
api: true,
webpacker: 'angular',
database: 'sqlite3',
railsVersion: 'master',
},
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with full options including action mailer and active storage',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: false,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: false,
api: false,
webpacker: 'stimulus',
database: 'postgresql',
railsVersion: 'fromImage',
},
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with API-only stack and custom template path',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: true,
skipWebpackInstall: true,
skipTest: true,
skipActionCable: true,
skipActionMailer: true,
skipActionMailbox: true,
skipActiveStorage: true,
skipActionText: true,
skipActiveRecord: true,
force: true,
api: true,
webpacker: 'elm',
database: 'mysql',
railsVersion: 'dev',
template: 'path/to/custom/template',
},
},
},
},
],
}),
},
{
description: 'Fetch and template a Rails app with custom Webpack setup',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: false,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: false,
api: false,
webpacker: 'angular',
database: 'postgresql',
railsVersion: 'dev',
},
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with specific Rails version and Action Mailer enabled',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: false,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: true,
api: true,
webpacker: 'react',
database: 'sqlite3',
railsVersion: 'master',
},
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with specific database and Active Storage enabled',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: false,
skipWebpackInstall: false,
skipTest: false,
skipActionCable: false,
skipActionMailer: false,
skipActionMailbox: false,
skipActiveStorage: false,
skipActionText: false,
skipActiveRecord: false,
force: true,
api: true,
webpacker: 'vue',
database: 'postgresql',
railsVersion: 'fromImage',
},
},
},
},
],
}),
},
{
description:
'Fetch and template a Rails app with full options including database and skip options',
example: yaml.stringify({
steps: [
{
id: 'fetchTemplate',
action: 'fetch:rails',
name: 'Fetch and Template Rails App',
input: {
url: 'https://github.com/backstage/backstage',
values: {
railsArguments: {
minimal: false,
skipBundle: true,
skipWebpackInstall: true,
skipTest: true,
skipActionCable: true,
skipActionMailer: true,
skipActionMailbox: true,
skipActiveStorage: true,
skipActionText: true,
skipActiveRecord: true,
force: false,
api: false,
webpacker: 'stimulus',
database: 'sqlite3',
railsVersion: 'dev',
},
},
},
},
],
}),
},
];
@@ -27,6 +27,7 @@ import {
import { resolve as resolvePath } from 'path';
import { RailsNewRunner } from './railsNewRunner';
import { PassThrough } from 'stream';
import { examples } from './index.examples';
/**
* Creates the `fetch:rails` Scaffolder action.
@@ -56,6 +57,7 @@ export function createFetchRailsAction(options: {
id: 'fetch:rails',
description:
'Downloads a template from the given `url` into the workspace, and runs a rails new generator on it.',
examples,
schema: {
input: {
type: 'object',