Merge branch 'master' of github.com:spotify/backstage into shmidt-i/app-catalog-tabs-routes-everything-is-connected
This commit is contained in:
@@ -92,6 +92,7 @@ dist
|
||||
|
||||
# Microsite build output
|
||||
microsite/build
|
||||
microsite/i18n
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
|
||||
@@ -6,7 +6,9 @@ title: Add to Marketplace
|
||||
## Adding a Plugin to the Marketplace
|
||||
|
||||
To add a new plugin to the [plugin marketplace](https://backstage.io/plugins)
|
||||
create a file in `data/plugins` with your plugin's information. Example:
|
||||
create a file in
|
||||
[`microsite/data/plugins`](https://github.com/spotify/backstage/tree/master/microsite/data/plugins)
|
||||
with your plugin's information. Example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: API Docs
|
||||
author: SDA SE
|
||||
authorUrl: https://sda.se/
|
||||
category: Discovery
|
||||
description: Components to discover and display API entities as an extension to the catalog plugin.
|
||||
documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md
|
||||
iconUrl: https://github.com/spotify/backstage/raw/master/plugins/api-docs/docs/entity_tab_api.png
|
||||
npmPackageName: '@backstage/plugin-api-docs'
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: GitHub Pull Requests
|
||||
author: roadie.io
|
||||
authorUrl: https://roadie.io/
|
||||
category: CI
|
||||
description: View GitHub pull requests for your service in Backstage.
|
||||
documentation: https://roadie.io/backstage/plugins/github-pull-requests
|
||||
iconUrl: https://roadie.io/static/7f13bb8d861d8dedc5112fb939d215f9/351f2/GitHub-Mark-Light-120px-plus.png
|
||||
npmPackageName: '@roadiehq/backstage-plugin-github-pull-requests'
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
{
|
||||
"_comment": "This file is auto-generated by write-translations.js",
|
||||
"localized-strings": {
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"tagline": "An open platform for building developer portals",
|
||||
"docs": {
|
||||
"api/backend": {
|
||||
"title": "Backend"
|
||||
},
|
||||
"api/utility-apis": {
|
||||
"title": "Utility APIs"
|
||||
},
|
||||
"architecture-decisions/adrs-adr001": {
|
||||
"title": "ADR001: Architecture Decision Record (ADR) log",
|
||||
"sidebar_label": "ADR001"
|
||||
},
|
||||
"architecture-decisions/adrs-adr002": {
|
||||
"title": "ADR002: Default Software Catalog File Format",
|
||||
"sidebar_label": "ADR002"
|
||||
},
|
||||
"architecture-decisions/adrs-adr003": {
|
||||
"title": "ADR003: Avoid Default Exports and Prefer Named Exports",
|
||||
"sidebar_label": "ADR003"
|
||||
},
|
||||
"architecture-decisions/adrs-adr004": {
|
||||
"title": "ADR004: Module Export Structure",
|
||||
"sidebar_label": "ADR004"
|
||||
},
|
||||
"architecture-decisions/adrs-adr005": {
|
||||
"title": "ADR005: Catalog Core Entities",
|
||||
"sidebar_label": "ADR005"
|
||||
},
|
||||
"architecture-decisions/adrs-adr006": {
|
||||
"title": "ADR006: Avoid React.FC and React.SFC",
|
||||
"sidebar_label": "ADR006"
|
||||
},
|
||||
"architecture-decisions/adrs-adr007": {
|
||||
"title": "ADR007: Use MSW to mock http requests",
|
||||
"sidebar_label": "ADR007"
|
||||
},
|
||||
"architecture-decisions/adrs-adr008": {
|
||||
"title": "ADR008: Default Catalog File Name",
|
||||
"sidebar_label": "ADR008"
|
||||
},
|
||||
"architecture-decisions/adrs-overview": {
|
||||
"title": "Architecture Decision Records (ADR)",
|
||||
"sidebar_label": "Overview"
|
||||
},
|
||||
"auth/add-auth-provider": {
|
||||
"title": "Adding authentication providers"
|
||||
},
|
||||
"auth/auth-backend-classes": {
|
||||
"title": "Auth backend classes"
|
||||
},
|
||||
"auth/auth-backend": {
|
||||
"title": "Auth backend"
|
||||
},
|
||||
"auth/glossary": {
|
||||
"title": "Glossary"
|
||||
},
|
||||
"auth/index": {
|
||||
"title": "User Authentication and Authorization in Backstage"
|
||||
},
|
||||
"auth/oauth": {
|
||||
"title": "OAuth and OpenID Connect"
|
||||
},
|
||||
"conf/defining": {
|
||||
"title": "Defining Configuration for your Plugin"
|
||||
},
|
||||
"conf/index": {
|
||||
"title": "Static Configuration in Backstage"
|
||||
},
|
||||
"conf/reading": {
|
||||
"title": "Reading Backstage Configuration"
|
||||
},
|
||||
"conf/writing": {
|
||||
"title": "Writing Backstage Configuration Files"
|
||||
},
|
||||
"dls/contributing-to-storybook": {
|
||||
"title": "Contributing to Storybook"
|
||||
},
|
||||
"dls/design": {
|
||||
"title": "Design"
|
||||
},
|
||||
"dls/figma": {
|
||||
"title": "Figma"
|
||||
},
|
||||
"FAQ": {
|
||||
"title": "FAQ"
|
||||
},
|
||||
"features/software-catalog/software-catalog-api": {
|
||||
"title": "API"
|
||||
},
|
||||
"features/software-catalog/descriptor-format": {
|
||||
"title": "Descriptor Format of Catalog Entities",
|
||||
"sidebar_label": "YAML File Format"
|
||||
},
|
||||
"features/software-catalog/extending-the-model": {
|
||||
"title": "Extending the model"
|
||||
},
|
||||
"features/software-catalog/external-integrations": {
|
||||
"title": "External integrations"
|
||||
},
|
||||
"features/software-catalog/software-catalog-overview": {
|
||||
"title": "Backstage Service Catalog (alpha)",
|
||||
"sidebar_label": "Backstage Service Catalog"
|
||||
},
|
||||
"features/software-catalog/installation": {
|
||||
"title": "features/software-catalog/installation"
|
||||
},
|
||||
"features/software-catalog/system-model": {
|
||||
"title": "System Model"
|
||||
},
|
||||
"features/software-templates/adding-templates": {
|
||||
"title": "Adding your own Templates"
|
||||
},
|
||||
"features/software-templates/extending/extending-preparer": {
|
||||
"title": "Create your own Preparer"
|
||||
},
|
||||
"features/software-templates/extending/extending-publisher": {
|
||||
"title": "Create your own Publisher"
|
||||
},
|
||||
"features/software-templates/extending/extending-templater": {
|
||||
"title": "Creating your own Templater"
|
||||
},
|
||||
"features/software-templates/extending/extending-index": {
|
||||
"title": "Extending the Scaffolder"
|
||||
},
|
||||
"features/software-templates/software-templates-index": {
|
||||
"title": "Software Templates"
|
||||
},
|
||||
"features/software-templates/installation": {
|
||||
"title": "features/software-templates/installation"
|
||||
},
|
||||
"features/techdocs/concepts": {
|
||||
"title": "Concepts"
|
||||
},
|
||||
"features/techdocs/creating-and-publishing": {
|
||||
"title": "Creating and publishing your docs",
|
||||
"sidebar_label": "Creating and Publishing Documentation"
|
||||
},
|
||||
"features/techdocs/faqs": {
|
||||
"title": "TechDocs FAQ",
|
||||
"sidebar_label": "FAQ"
|
||||
},
|
||||
"features/techdocs/getting-started": {
|
||||
"title": "Getting Started"
|
||||
},
|
||||
"features/techdocs/techdocs-overview": {
|
||||
"title": "TechDocs Documentation",
|
||||
"sidebar_label": "Overview"
|
||||
},
|
||||
"getting-started/app-custom-theme": {
|
||||
"title": "Customize the look-and-feel of your App"
|
||||
},
|
||||
"getting-started/configure-app-with-plugins": {
|
||||
"title": "Configuring App with plugins"
|
||||
},
|
||||
"getting-started/create-an-app": {
|
||||
"title": "Create an App"
|
||||
},
|
||||
"getting-started/deployment-k8s": {
|
||||
"title": "Kubernetes"
|
||||
},
|
||||
"getting-started/deployment-other": {
|
||||
"title": "Other"
|
||||
},
|
||||
"getting-started/development-environment": {
|
||||
"title": "Development Environment"
|
||||
},
|
||||
"getting-started/index": {
|
||||
"title": "Running Backstage Locally"
|
||||
},
|
||||
"getting-started/installation": {
|
||||
"title": "Installation"
|
||||
},
|
||||
"overview/adopting": {
|
||||
"title": "Strategies for adopting"
|
||||
},
|
||||
"overview/architecture-overview": {
|
||||
"title": "Architecture overview"
|
||||
},
|
||||
"overview/architecture-terminology": {
|
||||
"title": "Architecture terminology"
|
||||
},
|
||||
"overview/background": {
|
||||
"title": "The Spotify Story"
|
||||
},
|
||||
"overview/roadmap": {
|
||||
"title": "Project roadmap"
|
||||
},
|
||||
"overview/support": {
|
||||
"title": "Support and community"
|
||||
},
|
||||
"overview/vision": {
|
||||
"title": "Vision"
|
||||
},
|
||||
"overview/what-is-backstage": {
|
||||
"title": "What is Backstage?"
|
||||
},
|
||||
"plugins/add-to-marketplace": {
|
||||
"title": "Add to Marketplace"
|
||||
},
|
||||
"plugins/backend-plugin": {
|
||||
"title": "Backend plugin"
|
||||
},
|
||||
"plugins/call-existing-api": {
|
||||
"title": "Call Existing API"
|
||||
},
|
||||
"plugins/create-a-plugin": {
|
||||
"title": "Create a Backstage Plugin"
|
||||
},
|
||||
"plugins/existing-plugins": {
|
||||
"title": "Existing plugins"
|
||||
},
|
||||
"plugins/index": {
|
||||
"title": "Intro to plugins"
|
||||
},
|
||||
"plugins/integrating-plugin-into-service-catalog": {
|
||||
"title": "Integrate into the catalog service"
|
||||
},
|
||||
"plugins/plugin-development": {
|
||||
"title": "Plugin Development"
|
||||
},
|
||||
"plugins/proxying": {
|
||||
"title": "Proxying"
|
||||
},
|
||||
"plugins/publish-private": {
|
||||
"title": "Publish private"
|
||||
},
|
||||
"plugins/publishing": {
|
||||
"title": "Publishing"
|
||||
},
|
||||
"plugins/structure-of-a-plugin": {
|
||||
"title": "Structure of a Plugin"
|
||||
},
|
||||
"plugins/testing": {
|
||||
"title": "Testing with Jest"
|
||||
},
|
||||
"README": {
|
||||
"title": "README"
|
||||
},
|
||||
"reference/createPlugin-feature-flags": {
|
||||
"title": "createPlugin - feature flags"
|
||||
},
|
||||
"reference/createPlugin-router": {
|
||||
"title": "createPlugin - router"
|
||||
},
|
||||
"reference/createPlugin": {
|
||||
"title": "createPlugin"
|
||||
},
|
||||
"reference/utility-apis/AlertApi": {
|
||||
"title": "reference/utility-apis/AlertApi"
|
||||
},
|
||||
"reference/utility-apis/AppThemeApi": {
|
||||
"title": "reference/utility-apis/AppThemeApi"
|
||||
},
|
||||
"reference/utility-apis/BackstageIdentityApi": {
|
||||
"title": "reference/utility-apis/BackstageIdentityApi"
|
||||
},
|
||||
"reference/utility-apis/Config": {
|
||||
"title": "reference/utility-apis/Config"
|
||||
},
|
||||
"reference/utility-apis/ErrorApi": {
|
||||
"title": "reference/utility-apis/ErrorApi"
|
||||
},
|
||||
"reference/utility-apis/FeatureFlagsApi": {
|
||||
"title": "reference/utility-apis/FeatureFlagsApi"
|
||||
},
|
||||
"reference/utility-apis/IdentityApi": {
|
||||
"title": "reference/utility-apis/IdentityApi"
|
||||
},
|
||||
"reference/utility-apis/OAuthApi": {
|
||||
"title": "reference/utility-apis/OAuthApi"
|
||||
},
|
||||
"reference/utility-apis/OAuthRequestApi": {
|
||||
"title": "reference/utility-apis/OAuthRequestApi"
|
||||
},
|
||||
"reference/utility-apis/OpenIdConnectApi": {
|
||||
"title": "reference/utility-apis/OpenIdConnectApi"
|
||||
},
|
||||
"reference/utility-apis/ProfileInfoApi": {
|
||||
"title": "reference/utility-apis/ProfileInfoApi"
|
||||
},
|
||||
"reference/utility-apis/README": {
|
||||
"title": "Utility API References"
|
||||
},
|
||||
"reference/utility-apis/SessionStateApi": {
|
||||
"title": "reference/utility-apis/SessionStateApi"
|
||||
},
|
||||
"reference/utility-apis/StorageApi": {
|
||||
"title": "reference/utility-apis/StorageApi"
|
||||
},
|
||||
"tutorials/journey": {
|
||||
"title": "Future developer journey"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"GitHub": "GitHub",
|
||||
"Docs": "Docs",
|
||||
"Plugins": "Plugins",
|
||||
"Blog": "Blog",
|
||||
"Demos": "Demos",
|
||||
"Newsletter": "Newsletter"
|
||||
},
|
||||
"categories": {
|
||||
"Overview": "Overview",
|
||||
"Getting Started": "Getting Started",
|
||||
"Features": "Features",
|
||||
"Plugins": "Plugins",
|
||||
"Configuration": "Configuration",
|
||||
"Auth and identity": "Auth and identity",
|
||||
"Designing for Backstage": "Designing for Backstage",
|
||||
"API references": "API references",
|
||||
"Tutorials": "Tutorials",
|
||||
"Architecture Decision Records (ADRs)": "Architecture Decision Records (ADRs)",
|
||||
"Contribute": "Contribute",
|
||||
"Support": "Support",
|
||||
"FAQ": "FAQ"
|
||||
}
|
||||
},
|
||||
"pages-strings": {
|
||||
"Help Translate|recruit community translators for your project": "Help Translate",
|
||||
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
|
||||
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@ td {
|
||||
code {
|
||||
font-family: IBM Plex Mono, Menlo, Monaco, Consolas, Courier New, monospace;
|
||||
font-weight: 500;
|
||||
background-color: #0e0e0e;
|
||||
background-color: #272822;
|
||||
}
|
||||
|
||||
/* .stripe {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { createDatabase } from './connection';
|
||||
import { createDatabaseClient } from './connection';
|
||||
|
||||
describe('database connection', () => {
|
||||
const createConfig = (data: any) =>
|
||||
@@ -26,10 +26,10 @@ describe('database connection', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
describe(createDatabase, () => {
|
||||
describe(createDatabaseClient, () => {
|
||||
it('returns a postgres connection', () => {
|
||||
expect(
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
@@ -45,7 +45,7 @@ describe('database connection', () => {
|
||||
|
||||
it('returns an sqlite connection', () => {
|
||||
expect(
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
client: 'sqlite3',
|
||||
connection: ':memory:',
|
||||
@@ -56,7 +56,7 @@ describe('database connection', () => {
|
||||
|
||||
it('tries to create a mysql connection as a passthrough', () => {
|
||||
expect(() =>
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
client: 'mysql',
|
||||
connection: {
|
||||
@@ -72,7 +72,7 @@ describe('database connection', () => {
|
||||
|
||||
it('accepts overrides', () => {
|
||||
expect(
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
@@ -93,7 +93,7 @@ describe('database connection', () => {
|
||||
|
||||
it('throws an error without a client', () => {
|
||||
expect(() =>
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
connection: '',
|
||||
}),
|
||||
@@ -103,7 +103,7 @@ describe('database connection', () => {
|
||||
|
||||
it('throws an error without a connection', () => {
|
||||
expect(() =>
|
||||
createDatabase(
|
||||
createDatabaseClient(
|
||||
createConfig({
|
||||
client: 'pg',
|
||||
}),
|
||||
|
||||
@@ -15,30 +15,36 @@
|
||||
*/
|
||||
|
||||
import knex from 'knex';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Config } from '@backstage/config';
|
||||
import { mergeDatabaseConfig } from './config';
|
||||
import { createPgDatabase } from './postgres';
|
||||
import { createSqlite3Database } from './sqlite3';
|
||||
import { createPgDatabaseClient } from './postgres';
|
||||
import { createSqliteDatabaseClient } from './sqlite3';
|
||||
|
||||
type DatabaseClient = 'pg' | 'sqlite3' | string;
|
||||
|
||||
/**
|
||||
* Creates a knex database connection
|
||||
*
|
||||
* @param config The database config
|
||||
* @param dbConfig The database config
|
||||
* @param overrides Additional options to merge with the config
|
||||
*/
|
||||
export function createDatabase(
|
||||
config: ConfigReader,
|
||||
export function createDatabaseClient(
|
||||
dbConfig: Config,
|
||||
overrides?: Partial<knex.Config>,
|
||||
) {
|
||||
const client: DatabaseClient = config.getString('client');
|
||||
const client: DatabaseClient = dbConfig.getString('client');
|
||||
|
||||
if (client === 'pg') {
|
||||
return createPgDatabase(config, overrides);
|
||||
return createPgDatabaseClient(dbConfig, overrides);
|
||||
} else if (client === 'sqlite3') {
|
||||
return createSqlite3Database(config);
|
||||
return createSqliteDatabaseClient(dbConfig);
|
||||
}
|
||||
|
||||
return knex(mergeDatabaseConfig(config.get(), overrides));
|
||||
return knex(mergeDatabaseConfig(dbConfig.get(), overrides));
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for createDatabaseClient
|
||||
* @deprecated Use createDatabaseClient instead
|
||||
*/
|
||||
export const createDatabase = createDatabaseClient;
|
||||
|
||||
@@ -14,15 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Config, ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
parsePgConnectionString,
|
||||
createPgDatabaseClient,
|
||||
buildPgDatabaseConfig,
|
||||
createPgDatabase,
|
||||
getPgConnectionConfig,
|
||||
parsePgConnectionString,
|
||||
} from './postgres';
|
||||
|
||||
describe('postgres', () => {
|
||||
const createConfig = (connection: any) =>
|
||||
const createMockConnection = () => ({
|
||||
host: 'acme',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
database: 'foodb',
|
||||
});
|
||||
|
||||
const createMockConnectionString = () =>
|
||||
'postgresql://foo:bar@acme:5432/foodb';
|
||||
|
||||
const createConfig = (connection: any): Config =>
|
||||
ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
@@ -35,86 +46,58 @@ describe('postgres', () => {
|
||||
|
||||
describe(buildPgDatabaseConfig, () => {
|
||||
it('builds a postgres config', () => {
|
||||
expect(
|
||||
buildPgDatabaseConfig(
|
||||
createConfig({
|
||||
host: 'acme',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
port: '5432',
|
||||
database: 'foodb',
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
const mockConnection = createMockConnection();
|
||||
|
||||
expect(buildPgDatabaseConfig(createConfig(mockConnection))).toEqual({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'acme',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
port: '5432',
|
||||
database: 'foodb',
|
||||
},
|
||||
connection: mockConnection,
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('builds a connection string config', () => {
|
||||
expect(
|
||||
buildPgDatabaseConfig(
|
||||
createConfig('postgresql://foo:bar@acme:5432/foodb'),
|
||||
),
|
||||
).toEqual({
|
||||
client: 'pg',
|
||||
connection: 'postgresql://foo:bar@acme:5432/foodb',
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
const mockConnectionString = createMockConnectionString();
|
||||
|
||||
expect(buildPgDatabaseConfig(createConfig(mockConnectionString))).toEqual(
|
||||
{
|
||||
client: 'pg',
|
||||
connection: mockConnectionString,
|
||||
useNullAsDefault: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('overrides the database name', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
|
||||
expect(
|
||||
buildPgDatabaseConfig(
|
||||
createConfig({
|
||||
host: 'somehost',
|
||||
user: 'postgres',
|
||||
password: 'pass',
|
||||
database: 'foo',
|
||||
}),
|
||||
{ connection: { database: 'foodb' } },
|
||||
),
|
||||
buildPgDatabaseConfig(createConfig(mockConnection), {
|
||||
connection: { database: 'other_db' },
|
||||
}),
|
||||
).toEqual({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'somehost',
|
||||
user: 'postgres',
|
||||
password: 'pass',
|
||||
database: 'foodb',
|
||||
...mockConnection,
|
||||
database: 'other_db',
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('adds additional config settings', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
|
||||
expect(
|
||||
buildPgDatabaseConfig(
|
||||
createConfig({
|
||||
host: 'somehost',
|
||||
user: 'postgres',
|
||||
password: 'pass',
|
||||
database: 'foo',
|
||||
}),
|
||||
{
|
||||
connection: { database: 'foodb' },
|
||||
pool: { min: 0, max: 7 },
|
||||
debug: true,
|
||||
},
|
||||
),
|
||||
buildPgDatabaseConfig(createConfig(mockConnection), {
|
||||
connection: { database: 'other_db' },
|
||||
pool: { min: 0, max: 7 },
|
||||
debug: true,
|
||||
}),
|
||||
).toEqual({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'somehost',
|
||||
user: 'postgres',
|
||||
password: 'pass',
|
||||
database: 'foodb',
|
||||
...mockConnection,
|
||||
database: 'other_db',
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
pool: { min: 0, max: 7 },
|
||||
@@ -123,37 +106,72 @@ describe('postgres', () => {
|
||||
});
|
||||
|
||||
it('overrides the database from connection string', () => {
|
||||
const mockConnectionString = createMockConnectionString();
|
||||
const mockConnection = createMockConnection();
|
||||
|
||||
expect(
|
||||
buildPgDatabaseConfig(
|
||||
createConfig('postgresql://postgres:pass@localhost:5432/dbname'),
|
||||
{ connection: { database: 'foodb' } },
|
||||
),
|
||||
buildPgDatabaseConfig(createConfig(mockConnectionString), {
|
||||
connection: { database: 'other_db' },
|
||||
}),
|
||||
).toEqual({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'localhost',
|
||||
user: 'postgres',
|
||||
password: 'pass',
|
||||
...mockConnection,
|
||||
port: '5432',
|
||||
database: 'foodb',
|
||||
database: 'other_db',
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(createPgDatabase, () => {
|
||||
describe(getPgConnectionConfig, () => {
|
||||
it('returns the connection object back', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
const config = createConfig(mockConnection);
|
||||
|
||||
expect(getPgConnectionConfig(config)).toEqual(mockConnection);
|
||||
});
|
||||
|
||||
it('does not parse the connection string', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
const config = createConfig(mockConnection);
|
||||
|
||||
expect(getPgConnectionConfig(config, true)).toEqual(mockConnection);
|
||||
});
|
||||
|
||||
it('automatically parses the connection string', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
const mockConnectionString = createMockConnectionString();
|
||||
const config = createConfig(mockConnectionString);
|
||||
|
||||
expect(getPgConnectionConfig(config)).toEqual({
|
||||
...mockConnection,
|
||||
port: '5432',
|
||||
});
|
||||
});
|
||||
|
||||
it('parses the connection string', () => {
|
||||
const mockConnection = createMockConnection();
|
||||
const mockConnectionString = createMockConnectionString();
|
||||
const config = createConfig(mockConnectionString);
|
||||
|
||||
expect(getPgConnectionConfig(config, true)).toEqual({
|
||||
...mockConnection,
|
||||
port: '5432',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(createPgDatabaseClient, () => {
|
||||
it('creates a postgres knex instance', () => {
|
||||
expect(
|
||||
createPgDatabase(
|
||||
createPgDatabaseClient(
|
||||
createConfig({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
host: 'acme',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
database: 'foodb',
|
||||
},
|
||||
host: 'acme',
|
||||
user: 'foo',
|
||||
password: 'bar',
|
||||
database: 'foodb',
|
||||
}),
|
||||
),
|
||||
).toBeTruthy();
|
||||
@@ -161,7 +179,7 @@ describe('postgres', () => {
|
||||
|
||||
it('attempts to read an ssl cert', () => {
|
||||
expect(() =>
|
||||
createPgDatabase(
|
||||
createPgDatabaseClient(
|
||||
createConfig(
|
||||
'postgresql://postgres:pass@localhost:5432/dbname?sslrootcert=/path/to/file',
|
||||
),
|
||||
|
||||
@@ -14,18 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import knex from 'knex';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import knex, { PgConnectionConfig } from 'knex';
|
||||
import { Config } from '@backstage/config';
|
||||
import { mergeDatabaseConfig } from './config';
|
||||
|
||||
/**
|
||||
* Creates a knex sqlite3 database connection
|
||||
* Creates a knex postgres database connection
|
||||
*
|
||||
* @param dbConfig The database config
|
||||
* @param overrides Additional options to merge with the config
|
||||
*/
|
||||
export function createPgDatabase(
|
||||
dbConfig: ConfigReader,
|
||||
export function createPgDatabaseClient(
|
||||
dbConfig: Config,
|
||||
overrides?: knex.Config,
|
||||
) {
|
||||
const knexConfig = buildPgDatabaseConfig(dbConfig, overrides);
|
||||
@@ -40,26 +40,43 @@ export function createPgDatabase(
|
||||
* @param overrides Additional options to merge with the config
|
||||
*/
|
||||
export function buildPgDatabaseConfig(
|
||||
dbConfig: ConfigReader,
|
||||
dbConfig: Config,
|
||||
overrides?: knex.Config,
|
||||
) {
|
||||
const connection = dbConfig.get('connection') as any;
|
||||
|
||||
return mergeDatabaseConfig(
|
||||
dbConfig.get(),
|
||||
{
|
||||
// Only parse the connection string when overrides are provided
|
||||
connection:
|
||||
overrides &&
|
||||
(typeof connection === 'string' || connection instanceof String)
|
||||
? parsePgConnectionString(connection as string)
|
||||
: connection,
|
||||
connection: getPgConnectionConfig(dbConfig, !!overrides),
|
||||
useNullAsDefault: true,
|
||||
},
|
||||
overrides,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the postgres connection config
|
||||
*
|
||||
* @param dbConfig The database config
|
||||
* @param parseConnectionString Flag to explictly control connection string parsing
|
||||
*/
|
||||
export function getPgConnectionConfig(
|
||||
dbConfig: Config,
|
||||
parseConnectionString?: boolean,
|
||||
): PgConnectionConfig | string {
|
||||
const connection = dbConfig.get('connection') as any;
|
||||
const isConnectionString =
|
||||
typeof connection === 'string' || connection instanceof String;
|
||||
const autoParse = typeof parseConnectionString !== 'boolean';
|
||||
|
||||
const shouldParseConnectionString = autoParse
|
||||
? isConnectionString
|
||||
: parseConnectionString && isConnectionString;
|
||||
|
||||
return shouldParseConnectionString
|
||||
? parsePgConnectionString(connection as string)
|
||||
: connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a connection string using pg-connection-string
|
||||
*
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { buildSqlite3DatabaseConfig, createSqlite3Database } from './sqlite3';
|
||||
import {
|
||||
buildSqliteDatabaseConfig,
|
||||
createSqliteDatabaseClient,
|
||||
} from './sqlite3';
|
||||
|
||||
describe('sqlite3', () => {
|
||||
const createConfig = (connection: any) =>
|
||||
@@ -29,9 +32,9 @@ describe('sqlite3', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
describe(buildSqlite3DatabaseConfig, () => {
|
||||
describe(buildSqliteDatabaseConfig, () => {
|
||||
it('buidls a string connection', () => {
|
||||
expect(buildSqlite3DatabaseConfig(createConfig(':memory:'))).toEqual({
|
||||
expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({
|
||||
client: 'sqlite3',
|
||||
connection: ':memory:',
|
||||
useNullAsDefault: true,
|
||||
@@ -40,7 +43,7 @@ describe('sqlite3', () => {
|
||||
|
||||
it('builds a filename connection', () => {
|
||||
expect(
|
||||
buildSqlite3DatabaseConfig(
|
||||
buildSqliteDatabaseConfig(
|
||||
createConfig({
|
||||
filename: '/path/to/foo',
|
||||
}),
|
||||
@@ -56,7 +59,7 @@ describe('sqlite3', () => {
|
||||
|
||||
it('replaces the connection with an override', () => {
|
||||
expect(
|
||||
buildSqlite3DatabaseConfig(createConfig(':memory:'), {
|
||||
buildSqliteDatabaseConfig(createConfig(':memory:'), {
|
||||
connection: { filename: '/path/to/foo' },
|
||||
}),
|
||||
).toEqual({
|
||||
@@ -69,10 +72,10 @@ describe('sqlite3', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(createSqlite3Database, () => {
|
||||
describe(createSqliteDatabaseClient, () => {
|
||||
it('creates an in memory knex instance', () => {
|
||||
expect(
|
||||
createSqlite3Database(
|
||||
createSqliteDatabaseClient(
|
||||
createConfig({
|
||||
client: 'sqlite3',
|
||||
connection: ':memory:',
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import knex from 'knex';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Config } from '@backstage/config';
|
||||
import { mergeDatabaseConfig } from './config';
|
||||
|
||||
/**
|
||||
@@ -24,11 +24,11 @@ import { mergeDatabaseConfig } from './config';
|
||||
* @param dbConfig The database config
|
||||
* @param overrides Additional options to merge with the config
|
||||
*/
|
||||
export function createSqlite3Database(
|
||||
dbConfig: ConfigReader,
|
||||
export function createSqliteDatabaseClient(
|
||||
dbConfig: Config,
|
||||
overrides?: knex.Config,
|
||||
) {
|
||||
const knexConfig = buildSqlite3DatabaseConfig(dbConfig, overrides);
|
||||
const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides);
|
||||
const database = knex(knexConfig);
|
||||
|
||||
database.client.pool.on('createSuccess', (_eventId: any, resource: any) => {
|
||||
@@ -44,8 +44,8 @@ export function createSqlite3Database(
|
||||
* @param dbConfig The database config
|
||||
* @param overrides Additional options to merge with the config
|
||||
*/
|
||||
export function buildSqlite3DatabaseConfig(
|
||||
dbConfig: ConfigReader,
|
||||
export function buildSqliteDatabaseConfig(
|
||||
dbConfig: Config,
|
||||
overrides?: knex.Config,
|
||||
) {
|
||||
return mergeDatabaseConfig(
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
createDatabase,
|
||||
createDatabaseClient,
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
getRootLogger,
|
||||
@@ -48,11 +48,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) {
|
||||
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
const logger = getRootLogger().child({ type: 'plugin', plugin });
|
||||
const database = createDatabase(config.getConfig('backend.database'), {
|
||||
connection: {
|
||||
database: `backstage_plugin_${plugin}`,
|
||||
const database = createDatabaseClient(
|
||||
config.getConfig('backend.database'),
|
||||
{
|
||||
connection: {
|
||||
database: `backstage_plugin_${plugin}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
return { logger, database, config };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function buildBundle(options: BuildOptions) {
|
||||
const { statsJsonEnabled } = options;
|
||||
|
||||
const paths = resolveBundlingPaths(options);
|
||||
const config = createConfig(paths, {
|
||||
const config = await createConfig(paths, {
|
||||
...options,
|
||||
checksEnabled: false,
|
||||
isDev: false,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin';
|
||||
@@ -25,6 +26,9 @@ import { Config } from '@backstage/config';
|
||||
import { BundlingPaths } from './paths';
|
||||
import { transforms } from './transforms';
|
||||
import { BundlingOptions, BackendBundlingOptions } from './types';
|
||||
import { version } from '../../lib/version';
|
||||
import { paths as cliPaths } from '../../lib/paths';
|
||||
import { runPlain } from '../run';
|
||||
|
||||
export function resolveBaseUrl(config: Config): URL {
|
||||
const baseUrl = config.getString('app.baseUrl');
|
||||
@@ -35,10 +39,40 @@ export function resolveBaseUrl(config: Config): URL {
|
||||
}
|
||||
}
|
||||
|
||||
export function createConfig(
|
||||
async function readBuildInfo() {
|
||||
const timestamp = Date.now();
|
||||
|
||||
let commit = 'unknown';
|
||||
try {
|
||||
commit = await runPlain('git', 'rev-parse', 'HEAD');
|
||||
} catch (error) {
|
||||
console.warn(`WARNING: Failed to read git commit, ${error}`);
|
||||
}
|
||||
|
||||
let gitVersion = 'unknown';
|
||||
try {
|
||||
gitVersion = await runPlain('git', 'describe', '--always');
|
||||
} catch (error) {
|
||||
console.warn(`WARNING: Failed to describe git version, ${error}`);
|
||||
}
|
||||
|
||||
const { version: packageVersion } = await fs.readJson(
|
||||
cliPaths.resolveTarget('package.json'),
|
||||
);
|
||||
|
||||
return {
|
||||
cliVersion: version,
|
||||
gitVersion,
|
||||
packageVersion,
|
||||
timestamp,
|
||||
commit,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createConfig(
|
||||
paths: BundlingPaths,
|
||||
options: BundlingOptions,
|
||||
): webpack.Configuration {
|
||||
): Promise<webpack.Configuration> {
|
||||
const { checksEnabled, isDev } = options;
|
||||
|
||||
const { plugins, loaders } = transforms(options);
|
||||
@@ -81,6 +115,13 @@ export function createConfig(
|
||||
}),
|
||||
);
|
||||
|
||||
const buildInfo = await readBuildInfo();
|
||||
plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.BUILD_INFO': JSON.stringify(buildInfo),
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
mode: isDev ? 'development' : 'production',
|
||||
profile: false,
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function serveBundle(options: ServeOptions) {
|
||||
const paths = resolveBundlingPaths(options);
|
||||
const pkgPath = paths.targetPackageJson;
|
||||
const pkg = await fs.readJson(pkgPath);
|
||||
const config = createConfig(paths, {
|
||||
const config = await createConfig(paths, {
|
||||
...options,
|
||||
isDev: true,
|
||||
baseUrl: url,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
createDatabase,
|
||||
createDatabaseClient,
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
getRootLogger,
|
||||
@@ -27,11 +27,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) {
|
||||
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
const logger = getRootLogger().child({ type: 'plugin', plugin });
|
||||
const database = createDatabase(config.getConfig('backend.database'), {
|
||||
connection: {
|
||||
database: `backstage_plugin_${plugin}`,
|
||||
const database = createDatabaseClient(
|
||||
config.getConfig('backend.database'),
|
||||
{
|
||||
connection: {
|
||||
database: `backstage_plugin_${plugin}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
return { logger, database, config };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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`],
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user