Skip to content

Commit

Permalink
Adding config prop usePluginSchemas to allow plugins using the pg
Browse files Browse the repository at this point in the history
… client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db.

Signed-off-by: Greg Bomkamp <Gregory.Bomkamp@libertymutual.com>
  • Loading branch information
Greg Bomkamp authored and Greg Bomkamp committed Oct 28, 2021
1 parent dd028ea commit 7ad9a07
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-moons-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---

Adding config prop `usePluginSchemas` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db.
10 changes: 10 additions & 0 deletions packages/backend-common/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ export interface Config {
* Defaults to true if unspecified.
*/
ensureExists?: boolean;
/**
* Whether plugins should use their own schemas instead of databases. If enabled,
* each plugin will create a schema in the configured database instance
* using the `pluginId` as its schema name.
*
* NOTE: Currently only supported by the `pg` client.
*
* @default false
*/
usePluginSchemas?: boolean;
/** Plugin specific database configuration and client override */
plugin?: {
[pluginId: string]: {
Expand Down
193 changes: 192 additions & 1 deletion packages/backend-common/src/database/DatabaseManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@
*/
import { ConfigReader } from '@backstage/config';
import { omit } from 'lodash';
import { createDatabaseClient, ensureDatabaseExists } from './connection';
import {
createDatabaseClient,
ensureDatabaseExists,
ensureSchemaExists,
} from './connection';
import { DatabaseManager } from './DatabaseManager';

jest.mock('./connection', () => ({
...jest.requireActual('./connection'),
createDatabaseClient: jest.fn(),
ensureDatabaseExists: jest.fn(),
ensureSchemaExists: jest.fn(),
}));

describe('DatabaseManager', () => {
Expand Down Expand Up @@ -314,5 +319,191 @@ describe('DatabaseManager', () => {
expect.stringContaining('userdbname'),
);
});

it('plugin sets schema override for pg client', async () => {
const overrideConfig = {
backend: {
database: {
client: 'pg',
usePluginSchemas: true,
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
},
},
};
const testManager = DatabaseManager.fromConfig(
new ConfigReader(overrideConfig),
);
const pluginId = 'schemaoverride';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const [baseConfig, overrides] = mockCalls[0];

expect(baseConfig.get()).toMatchObject({
client: 'pg',
connection: config.backend.database.connection,
});

expect(overrides).toMatchObject({
searchPath: [pluginId],
});
});

it('plugin does not provide schema override for non pg client', async () => {
const testManager = DatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'sqlite3',
usePluginSchemas: true,
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
},
},
}),
);
const pluginId = 'any-plugin';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const [baseConfig, overrides] = mockCalls[0];

expect(baseConfig.get()).toMatchObject({
client: 'sqlite3',
connection: config.backend.database.connection,
});

expect(overrides).not.toHaveProperty('searchPath');
});

it('plugin does not provide schema override if usePluginSchemas is not provided', async () => {
const testManager = DatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'pg',
usePluginSchemas: false,
connection: 'some-file-path',
},
},
}),
);

const pluginId = 'any-plugin';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const [_baseConfig, overrides] = mockCalls[0];

expect(overrides).not.toHaveProperty('searchPath');
});

it('plugin does not provide schema override if usePluginSchemas is false', async () => {
const testManager = DatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'pg',
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
},
},
}),
);

const pluginId = 'schemaoverride';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const [_baseConfig, overrides] = mockCalls[0];

expect(overrides).not.toHaveProperty('searchPath');
});

it('usePluginSchemas ensures that each plugin schema exists', async () => {
const testManager = DatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'pg',
usePluginSchemas: true,
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
},
},
}),
);
const pluginId = 'testdbname';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(ensureSchemaExists).mock.calls;
const [_, schemaName] = mockCalls[0];

expect(schemaName).toEqual('testdbname');
});

it('usePluginSchemas allows connection overrides for plugins', async () => {
const testManager = DatabaseManager.fromConfig(
new ConfigReader({
backend: {
database: {
client: 'pg',
usePluginSchemas: true,
connection: {
host: 'localhost',
user: 'foo',
password: 'bar',
database: 'foodb',
},
plugin: {
testdbname: {
connection: {
database: 'database_name_overriden',
host: 'newhost',
},
},
},
},
},
}),
);
const pluginId = 'testdbname';
await testManager.forPlugin(pluginId).getClient();

const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
const [baseConfig, overrides] = mockCalls[0];

expect(baseConfig.get()).toMatchObject({
client: 'pg',
connection: {
database: 'database_name_overriden',
host: 'newhost',
user: 'foo',
password: 'bar',
},
});
expect(overrides).toHaveProperty('searchPath', ['testdbname']);
expect(overrides).toHaveProperty(
'connection.database',
'database_name_overriden',
);
});
});
});

0 comments on commit 7ad9a07

Please sign in to comment.