supabase-test 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -410
- package/connect.d.ts +11 -19
- package/connect.js +62 -73
- package/esm/connect.js +62 -72
- package/esm/index.js +4 -6
- package/index.d.ts +3 -6
- package/index.js +6 -6
- package/package.json +9 -21
- package/admin.d.ts +0 -26
- package/admin.js +0 -182
- package/dist/README.md +0 -557
- package/dist/package.json +0 -72
- package/esm/admin.js +0 -178
- package/esm/manager.js +0 -136
- package/esm/roles.js +0 -32
- package/esm/seed/adapters.js +0 -23
- package/esm/seed/csv.js +0 -44
- package/esm/seed/index.js +0 -16
- package/esm/seed/json.js +0 -18
- package/esm/seed/launchql.js +0 -19
- package/esm/seed/sqitch.js +0 -17
- package/esm/seed/types.js +0 -1
- package/esm/stream.js +0 -43
- package/esm/test-client.js +0 -150
- package/manager.d.ts +0 -25
- package/manager.js +0 -140
- package/roles.d.ts +0 -17
- package/roles.js +0 -38
- package/seed/adapters.d.ts +0 -4
- package/seed/adapters.js +0 -28
- package/seed/csv.d.ts +0 -9
- package/seed/csv.js +0 -49
- package/seed/index.d.ts +0 -16
- package/seed/index.js +0 -33
- package/seed/json.d.ts +0 -6
- package/seed/json.js +0 -21
- package/seed/launchql.d.ts +0 -2
- package/seed/launchql.js +0 -22
- package/seed/sqitch.d.ts +0 -2
- package/seed/sqitch.js +0 -20
- package/seed/types.d.ts +0 -13
- package/seed/types.js +0 -2
- package/stream.d.ts +0 -2
- package/stream.js +0 -46
- package/test-client.d.ts +0 -49
- package/test-client.js +0 -154
package/connect.js
CHANGED
|
@@ -1,83 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getConnections =
|
|
4
|
-
const env_1 = require("@launchql/env");
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
|
-
const pg_cache_1 = require("pg-cache");
|
|
3
|
+
exports.getConnections = void 0;
|
|
7
4
|
const pg_env_1 = require("pg-env");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
const pgsql_test_1 = require("pgsql-test");
|
|
6
|
+
/**
|
|
7
|
+
* Supabase default connection options
|
|
8
|
+
*/
|
|
9
|
+
const SUPABASE_DEFAULTS = {
|
|
10
|
+
roles: {
|
|
11
|
+
anonymous: 'anon',
|
|
12
|
+
authenticated: 'authenticated',
|
|
13
|
+
administrator: 'service_role',
|
|
14
|
+
default: 'anon',
|
|
15
|
+
}
|
|
19
16
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
pg: config,
|
|
29
|
-
db: connect
|
|
30
|
-
};
|
|
17
|
+
/**
|
|
18
|
+
* Supabase default PostgreSQL config
|
|
19
|
+
*/
|
|
20
|
+
const SUPABASE_PG_DEFAULTS = {
|
|
21
|
+
port: 54322,
|
|
22
|
+
user: 'supabase_admin',
|
|
23
|
+
password: 'postgres',
|
|
31
24
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await (0, pg_cache_1.teardownPgPools)();
|
|
55
|
-
await manager.closeAll();
|
|
25
|
+
/**
|
|
26
|
+
* Get connections with Supabase defaults applied.
|
|
27
|
+
* Environment variables take precedence over Supabase defaults.
|
|
28
|
+
* User-provided options take precedence over both.
|
|
29
|
+
*
|
|
30
|
+
* Note: Uses PGUSER/PGPASSWORD for both pg config and db.connection
|
|
31
|
+
* (DB_CONNECTION_ROLE can still be used to override the default role)
|
|
32
|
+
*/
|
|
33
|
+
const getConnections = async (cn = {}, seedAdapters) => {
|
|
34
|
+
// Get environment variables - these should take precedence over our defaults
|
|
35
|
+
const pgEnvVars = (0, pg_env_1.getPgEnvVars)();
|
|
36
|
+
// Build pg config: env vars > Supabase defaults, then user overrides will override both
|
|
37
|
+
const pgConfig = {};
|
|
38
|
+
pgConfig.port = pgEnvVars.port ?? SUPABASE_PG_DEFAULTS.port;
|
|
39
|
+
pgConfig.user = pgEnvVars.user ?? SUPABASE_PG_DEFAULTS.user;
|
|
40
|
+
pgConfig.password = pgEnvVars.password ?? SUPABASE_PG_DEFAULTS.password;
|
|
41
|
+
// Build connection config: use same user/password as pg config (from env vars or Supabase defaults)
|
|
42
|
+
// Default role is 'anon' (Supabase default), but DB_CONNECTION_ROLE can override it
|
|
43
|
+
const connectionConfig = {
|
|
44
|
+
user: pgConfig.user,
|
|
45
|
+
password: pgConfig.password,
|
|
46
|
+
role: process.env.DB_CONNECTION_ROLE ?? SUPABASE_DEFAULTS.roles.default,
|
|
56
47
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
48
|
+
// Build roles config: Supabase defaults, then user overrides will override
|
|
49
|
+
const rolesConfig = {
|
|
50
|
+
...SUPABASE_DEFAULTS.roles,
|
|
51
|
+
};
|
|
52
|
+
// Build the merged options, respecting precedence: env vars > Supabase defaults > user overrides
|
|
53
|
+
const mergedOpts = {
|
|
54
|
+
pg: {
|
|
55
|
+
...pgConfig,
|
|
56
|
+
...cn.pg, // User overrides take precedence
|
|
57
|
+
},
|
|
58
|
+
db: {
|
|
59
|
+
connection: {
|
|
60
|
+
...connectionConfig,
|
|
61
|
+
...cn.db?.connection, // User overrides take precedence
|
|
62
|
+
},
|
|
63
|
+
roles: {
|
|
64
|
+
...rolesConfig,
|
|
65
|
+
...cn.db?.roles, // User overrides take precedence
|
|
66
|
+
},
|
|
67
|
+
...cn.db, // Other user overrides
|
|
69
68
|
}
|
|
70
|
-
}
|
|
71
|
-
const dbConfig = {
|
|
72
|
-
...config,
|
|
73
|
-
user: connOpts.connection.user,
|
|
74
|
-
password: connOpts.connection.password
|
|
75
69
|
};
|
|
76
|
-
|
|
77
|
-
auth: connOpts.auth,
|
|
78
|
-
roles: connOpts.roles
|
|
79
|
-
});
|
|
80
|
-
db.setContext({ role: (0, roles_1.getDefaultRole)(connOpts) });
|
|
81
|
-
return { pg, db, teardown, manager, admin };
|
|
70
|
+
return (0, pgsql_test_1.getConnections)(mergedOpts, seedAdapters);
|
|
82
71
|
};
|
|
83
72
|
exports.getConnections = getConnections;
|
package/esm/connect.js
CHANGED
|
@@ -1,78 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
const admin = new DbAdmin(opts, false, connOpts);
|
|
15
|
-
return admin;
|
|
1
|
+
import { getPgEnvVars } from 'pg-env';
|
|
2
|
+
import { getConnections as getPgConnections } from 'pgsql-test';
|
|
3
|
+
/**
|
|
4
|
+
* Supabase default connection options
|
|
5
|
+
*/
|
|
6
|
+
const SUPABASE_DEFAULTS = {
|
|
7
|
+
roles: {
|
|
8
|
+
anonymous: 'anon',
|
|
9
|
+
authenticated: 'authenticated',
|
|
10
|
+
administrator: 'service_role',
|
|
11
|
+
default: 'anon',
|
|
12
|
+
}
|
|
16
13
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
pg: config,
|
|
25
|
-
db: connect
|
|
26
|
-
};
|
|
14
|
+
/**
|
|
15
|
+
* Supabase default PostgreSQL config
|
|
16
|
+
*/
|
|
17
|
+
const SUPABASE_PG_DEFAULTS = {
|
|
18
|
+
port: 54322,
|
|
19
|
+
user: 'supabase_admin',
|
|
20
|
+
password: 'postgres',
|
|
27
21
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await teardownPgPools();
|
|
51
|
-
await manager.closeAll();
|
|
22
|
+
/**
|
|
23
|
+
* Get connections with Supabase defaults applied.
|
|
24
|
+
* Environment variables take precedence over Supabase defaults.
|
|
25
|
+
* User-provided options take precedence over both.
|
|
26
|
+
*
|
|
27
|
+
* Note: Uses PGUSER/PGPASSWORD for both pg config and db.connection
|
|
28
|
+
* (DB_CONNECTION_ROLE can still be used to override the default role)
|
|
29
|
+
*/
|
|
30
|
+
export const getConnections = async (cn = {}, seedAdapters) => {
|
|
31
|
+
// Get environment variables - these should take precedence over our defaults
|
|
32
|
+
const pgEnvVars = getPgEnvVars();
|
|
33
|
+
// Build pg config: env vars > Supabase defaults, then user overrides will override both
|
|
34
|
+
const pgConfig = {};
|
|
35
|
+
pgConfig.port = pgEnvVars.port ?? SUPABASE_PG_DEFAULTS.port;
|
|
36
|
+
pgConfig.user = pgEnvVars.user ?? SUPABASE_PG_DEFAULTS.user;
|
|
37
|
+
pgConfig.password = pgEnvVars.password ?? SUPABASE_PG_DEFAULTS.password;
|
|
38
|
+
// Build connection config: use same user/password as pg config (from env vars or Supabase defaults)
|
|
39
|
+
// Default role is 'anon' (Supabase default), but DB_CONNECTION_ROLE can override it
|
|
40
|
+
const connectionConfig = {
|
|
41
|
+
user: pgConfig.user,
|
|
42
|
+
password: pgConfig.password,
|
|
43
|
+
role: process.env.DB_CONNECTION_ROLE ?? SUPABASE_DEFAULTS.roles.default,
|
|
52
44
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
// Build roles config: Supabase defaults, then user overrides will override
|
|
46
|
+
const rolesConfig = {
|
|
47
|
+
...SUPABASE_DEFAULTS.roles,
|
|
48
|
+
};
|
|
49
|
+
// Build the merged options, respecting precedence: env vars > Supabase defaults > user overrides
|
|
50
|
+
const mergedOpts = {
|
|
51
|
+
pg: {
|
|
52
|
+
...pgConfig,
|
|
53
|
+
...cn.pg, // User overrides take precedence
|
|
54
|
+
},
|
|
55
|
+
db: {
|
|
56
|
+
connection: {
|
|
57
|
+
...connectionConfig,
|
|
58
|
+
...cn.db?.connection, // User overrides take precedence
|
|
59
|
+
},
|
|
60
|
+
roles: {
|
|
61
|
+
...rolesConfig,
|
|
62
|
+
...cn.db?.roles, // User overrides take precedence
|
|
63
|
+
},
|
|
64
|
+
...cn.db, // Other user overrides
|
|
65
65
|
}
|
|
66
|
-
}
|
|
67
|
-
const dbConfig = {
|
|
68
|
-
...config,
|
|
69
|
-
user: connOpts.connection.user,
|
|
70
|
-
password: connOpts.connection.password
|
|
71
66
|
};
|
|
72
|
-
|
|
73
|
-
auth: connOpts.auth,
|
|
74
|
-
roles: connOpts.roles
|
|
75
|
-
});
|
|
76
|
-
db.setContext({ role: getDefaultRole(connOpts) });
|
|
77
|
-
return { pg, db, teardown, manager, admin };
|
|
67
|
+
return getPgConnections(mergedOpts, seedAdapters);
|
|
78
68
|
};
|
package/esm/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from '
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export * from './seed';
|
|
6
|
-
export * from './test-client';
|
|
1
|
+
// Re-export everything from pgsql-test
|
|
2
|
+
export * from 'pgsql-test';
|
|
3
|
+
// Export Supabase-specific getConnections with defaults baked in
|
|
4
|
+
export { getConnections } from './connect';
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export * from '
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './roles';
|
|
5
|
-
export * from './seed';
|
|
6
|
-
export * from './test-client';
|
|
1
|
+
export * from 'pgsql-test';
|
|
2
|
+
export { getConnections } from './connect';
|
|
3
|
+
export type { GetConnectionOpts, GetConnectionResult } from './connect';
|
package/index.js
CHANGED
|
@@ -14,9 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
__exportStar(require("
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
exports.getConnections = void 0;
|
|
18
|
+
// Re-export everything from pgsql-test
|
|
19
|
+
__exportStar(require("pgsql-test"), exports);
|
|
20
|
+
// Export Supabase-specific getConnections with defaults baked in
|
|
21
|
+
var connect_1 = require("./connect");
|
|
22
|
+
Object.defineProperty(exports, "getConnections", { enumerable: true, get: function () { return connect_1.getConnections; } });
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supabase-test",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"author": "
|
|
5
|
-
"description": "supabase-test offers isolated, role-aware, and rollback-friendly PostgreSQL environments for integration tests
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"author": "Interweb <developers@interweb.io>",
|
|
5
|
+
"description": "supabase-test offers isolated, role-aware, and rollback-friendly PostgreSQL environments for integration tests with Supabase defaults baked in",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"module": "esm/index.js",
|
|
8
8
|
"types": "index.d.ts",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"url": "https://github.com/launchql/launchql/issues"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
|
+
"supabase",
|
|
23
24
|
"postgres",
|
|
24
25
|
"postgresql",
|
|
25
26
|
"testing",
|
|
@@ -32,11 +33,6 @@
|
|
|
32
33
|
"test-runner",
|
|
33
34
|
"jest",
|
|
34
35
|
"mocha",
|
|
35
|
-
"sqitch",
|
|
36
|
-
"launchql",
|
|
37
|
-
"graphile",
|
|
38
|
-
"typeorm",
|
|
39
|
-
"knex",
|
|
40
36
|
"seed",
|
|
41
37
|
"fixtures",
|
|
42
38
|
"transactions",
|
|
@@ -55,18 +51,10 @@
|
|
|
55
51
|
"test": "jest",
|
|
56
52
|
"test:watch": "jest --watch"
|
|
57
53
|
},
|
|
58
|
-
"devDependencies": {
|
|
59
|
-
"@types/pg": "^8.15.2",
|
|
60
|
-
"@types/pg-copy-streams": "^1.2.5"
|
|
61
|
-
},
|
|
62
54
|
"dependencies": {
|
|
63
|
-
"@launchql/
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"pg-cache": "^1.3.1",
|
|
69
|
-
"pg-copy-streams": "^6.0.6",
|
|
70
|
-
"pg-env": "^1.1.0"
|
|
71
|
-
}
|
|
55
|
+
"@launchql/types": "^2.6.1",
|
|
56
|
+
"pg-env": "^1.1.0",
|
|
57
|
+
"pgsql-test": "^2.11.6"
|
|
58
|
+
},
|
|
59
|
+
"gitHead": "0b26f726ac2ca19886af7553a06e95ce826d515b"
|
|
72
60
|
}
|
package/admin.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { PgTestConnectionOptions } from '@launchql/types';
|
|
2
|
-
import { PgConfig } from 'pg-env';
|
|
3
|
-
import { SeedAdapter } from './seed/types';
|
|
4
|
-
export declare class DbAdmin {
|
|
5
|
-
private config;
|
|
6
|
-
private verbose;
|
|
7
|
-
private roleConfig?;
|
|
8
|
-
constructor(config: PgConfig, verbose?: boolean, roleConfig?: PgTestConnectionOptions);
|
|
9
|
-
private getEnv;
|
|
10
|
-
private run;
|
|
11
|
-
private safeDropDb;
|
|
12
|
-
drop(dbName?: string): void;
|
|
13
|
-
dropTemplate(dbName: string): void;
|
|
14
|
-
create(dbName?: string): void;
|
|
15
|
-
createFromTemplate(template: string, dbName?: string): void;
|
|
16
|
-
installExtensions(extensions: string[] | string, dbName?: string): void;
|
|
17
|
-
connectionString(dbName?: string): string;
|
|
18
|
-
createTemplateFromBase(base: string, template: string): void;
|
|
19
|
-
cleanupTemplate(template: string): void;
|
|
20
|
-
grantRole(role: string, user: string, dbName?: string): Promise<void>;
|
|
21
|
-
grantConnect(role: string, dbName?: string): Promise<void>;
|
|
22
|
-
createUserRole(user: string, password: string, dbName: string): Promise<void>;
|
|
23
|
-
loadSql(file: string, dbName: string): void;
|
|
24
|
-
streamSql(sql: string, dbName: string): Promise<void>;
|
|
25
|
-
createSeededTemplate(templateName: string, adapter: SeedAdapter): Promise<void>;
|
|
26
|
-
}
|
package/admin.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbAdmin = void 0;
|
|
4
|
-
const logger_1 = require("@launchql/logger");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const pg_env_1 = require("pg-env");
|
|
8
|
-
const roles_1 = require("./roles");
|
|
9
|
-
const stream_1 = require("./stream");
|
|
10
|
-
const log = new logger_1.Logger('db-admin');
|
|
11
|
-
class DbAdmin {
|
|
12
|
-
config;
|
|
13
|
-
verbose;
|
|
14
|
-
roleConfig;
|
|
15
|
-
constructor(config, verbose = false, roleConfig) {
|
|
16
|
-
this.config = config;
|
|
17
|
-
this.verbose = verbose;
|
|
18
|
-
this.roleConfig = roleConfig;
|
|
19
|
-
this.config = (0, pg_env_1.getPgEnvOptions)(config);
|
|
20
|
-
}
|
|
21
|
-
getEnv() {
|
|
22
|
-
return {
|
|
23
|
-
PGHOST: this.config.host,
|
|
24
|
-
PGPORT: String(this.config.port),
|
|
25
|
-
PGUSER: this.config.user,
|
|
26
|
-
PGPASSWORD: this.config.password
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
run(command) {
|
|
30
|
-
try {
|
|
31
|
-
(0, child_process_1.execSync)(command, {
|
|
32
|
-
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
33
|
-
env: {
|
|
34
|
-
...process.env,
|
|
35
|
-
...this.getEnv()
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
if (this.verbose)
|
|
39
|
-
log.success(`Executed: ${command}`);
|
|
40
|
-
}
|
|
41
|
-
catch (err) {
|
|
42
|
-
log.error(`Command failed: ${command}`);
|
|
43
|
-
if (this.verbose)
|
|
44
|
-
log.error(err.message);
|
|
45
|
-
throw err;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
safeDropDb(name) {
|
|
49
|
-
try {
|
|
50
|
-
this.run(`dropdb "${name}"`);
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
if (!err.message.includes('does not exist')) {
|
|
54
|
-
log.warn(`Could not drop database ${name}: ${err.message}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
drop(dbName) {
|
|
59
|
-
this.safeDropDb(dbName ?? this.config.database);
|
|
60
|
-
}
|
|
61
|
-
dropTemplate(dbName) {
|
|
62
|
-
this.run(`psql -c "UPDATE pg_database SET datistemplate='false' WHERE datname='${dbName}';"`);
|
|
63
|
-
this.drop(dbName);
|
|
64
|
-
}
|
|
65
|
-
create(dbName) {
|
|
66
|
-
const db = dbName ?? this.config.database;
|
|
67
|
-
this.run(`createdb -U ${this.config.user} -h ${this.config.host} -p ${this.config.port} "${db}"`);
|
|
68
|
-
}
|
|
69
|
-
createFromTemplate(template, dbName) {
|
|
70
|
-
const db = dbName ?? this.config.database;
|
|
71
|
-
this.run(`createdb -U ${this.config.user} -h ${this.config.host} -p ${this.config.port} -e "${db}" -T "${template}"`);
|
|
72
|
-
}
|
|
73
|
-
installExtensions(extensions, dbName) {
|
|
74
|
-
const db = dbName ?? this.config.database;
|
|
75
|
-
const extList = typeof extensions === 'string' ? extensions.split(',') : extensions;
|
|
76
|
-
for (const extension of extList) {
|
|
77
|
-
this.run(`psql --dbname "${db}" -c 'CREATE EXTENSION IF NOT EXISTS "${extension}" CASCADE;'`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
connectionString(dbName) {
|
|
81
|
-
const { user, password, host, port } = this.config;
|
|
82
|
-
const db = dbName ?? this.config.database;
|
|
83
|
-
return `postgres://${user}:${password}@${host}:${port}/${db}`;
|
|
84
|
-
}
|
|
85
|
-
createTemplateFromBase(base, template) {
|
|
86
|
-
this.run(`createdb -T "${base}" "${template}"`);
|
|
87
|
-
this.run(`psql -c "UPDATE pg_database SET datistemplate = true WHERE datname = '${template}';"`);
|
|
88
|
-
}
|
|
89
|
-
cleanupTemplate(template) {
|
|
90
|
-
try {
|
|
91
|
-
this.run(`psql -c "UPDATE pg_database SET datistemplate = false WHERE datname = '${template}'"`);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
log.warn(`Skipping failed UPDATE of datistemplate for ${template}`);
|
|
95
|
-
}
|
|
96
|
-
this.safeDropDb(template);
|
|
97
|
-
}
|
|
98
|
-
async grantRole(role, user, dbName) {
|
|
99
|
-
const db = dbName ?? this.config.database;
|
|
100
|
-
const sql = `GRANT ${role} TO ${user};`;
|
|
101
|
-
await this.streamSql(sql, db);
|
|
102
|
-
}
|
|
103
|
-
async grantConnect(role, dbName) {
|
|
104
|
-
const db = dbName ?? this.config.database;
|
|
105
|
-
const sql = `GRANT CONNECT ON DATABASE "${db}" TO ${role};`;
|
|
106
|
-
await this.streamSql(sql, db);
|
|
107
|
-
}
|
|
108
|
-
// TODO: make adminRole a configurable option
|
|
109
|
-
// ONLY granting admin role for testing purposes, normally the db connection for apps won't have admin role
|
|
110
|
-
// DO NOT USE THIS FOR PRODUCTION
|
|
111
|
-
async createUserRole(user, password, dbName) {
|
|
112
|
-
const anonRole = (0, roles_1.getRoleName)('anonymous', this.roleConfig);
|
|
113
|
-
const authRole = (0, roles_1.getRoleName)('authenticated', this.roleConfig);
|
|
114
|
-
const adminRole = (0, roles_1.getRoleName)('administrator', this.roleConfig);
|
|
115
|
-
const sql = `
|
|
116
|
-
DO $$
|
|
117
|
-
BEGIN
|
|
118
|
-
-- Create role if it doesn't exist
|
|
119
|
-
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${user}') THEN
|
|
120
|
-
CREATE ROLE ${user} LOGIN PASSWORD '${password}';
|
|
121
|
-
END IF;
|
|
122
|
-
|
|
123
|
-
-- Grant anonymous role if not already granted
|
|
124
|
-
IF NOT EXISTS (
|
|
125
|
-
SELECT 1 FROM pg_auth_members am
|
|
126
|
-
JOIN pg_roles r1 ON am.roleid = r1.oid
|
|
127
|
-
JOIN pg_roles r2 ON am.member = r2.oid
|
|
128
|
-
WHERE r1.rolname = '${anonRole}' AND r2.rolname = '${user}'
|
|
129
|
-
) THEN
|
|
130
|
-
GRANT ${anonRole} TO ${user};
|
|
131
|
-
END IF;
|
|
132
|
-
|
|
133
|
-
-- Grant authenticated role if not already granted
|
|
134
|
-
IF NOT EXISTS (
|
|
135
|
-
SELECT 1 FROM pg_auth_members am
|
|
136
|
-
JOIN pg_roles r1 ON am.roleid = r1.oid
|
|
137
|
-
JOIN pg_roles r2 ON am.member = r2.oid
|
|
138
|
-
WHERE r1.rolname = '${authRole}' AND r2.rolname = '${user}'
|
|
139
|
-
) THEN
|
|
140
|
-
GRANT ${authRole} TO ${user};
|
|
141
|
-
END IF;
|
|
142
|
-
|
|
143
|
-
-- Grant administrator role if not already granted
|
|
144
|
-
IF NOT EXISTS (
|
|
145
|
-
SELECT 1 FROM pg_auth_members am
|
|
146
|
-
JOIN pg_roles r1 ON am.roleid = r1.oid
|
|
147
|
-
JOIN pg_roles r2 ON am.member = r2.oid
|
|
148
|
-
WHERE r1.rolname = '${adminRole}' AND r2.rolname = '${user}'
|
|
149
|
-
) THEN
|
|
150
|
-
GRANT ${adminRole} TO ${user};
|
|
151
|
-
END IF;
|
|
152
|
-
END $$;
|
|
153
|
-
`.trim();
|
|
154
|
-
await this.streamSql(sql, dbName);
|
|
155
|
-
}
|
|
156
|
-
loadSql(file, dbName) {
|
|
157
|
-
if (!(0, fs_1.existsSync)(file)) {
|
|
158
|
-
throw new Error(`Missing SQL file: ${file}`);
|
|
159
|
-
}
|
|
160
|
-
this.run(`psql -f ${file} ${dbName}`);
|
|
161
|
-
}
|
|
162
|
-
async streamSql(sql, dbName) {
|
|
163
|
-
await (0, stream_1.streamSql)({
|
|
164
|
-
...this.config,
|
|
165
|
-
database: dbName
|
|
166
|
-
}, sql);
|
|
167
|
-
}
|
|
168
|
-
async createSeededTemplate(templateName, adapter) {
|
|
169
|
-
const seedDb = this.config.database;
|
|
170
|
-
this.create(seedDb);
|
|
171
|
-
await adapter.seed({
|
|
172
|
-
admin: this,
|
|
173
|
-
config: this.config,
|
|
174
|
-
pg: null, // placeholder for PgTestClient
|
|
175
|
-
connect: null // placeholder for connection factory
|
|
176
|
-
});
|
|
177
|
-
this.cleanupTemplate(templateName);
|
|
178
|
-
this.createTemplateFromBase(seedDb, templateName);
|
|
179
|
-
this.drop(seedDb);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
exports.DbAdmin = DbAdmin;
|