@playwright-orchestrator/pg 1.0.5 → 1.1.0

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.
@@ -1,4 +1,7 @@
1
1
  export interface CreateArgs {
2
2
  connectionString: string;
3
3
  tableNamePrefix: string;
4
+ sslCa?: string | Buffer;
5
+ sslCert?: string | Buffer;
6
+ sslKey?: string | Buffer;
4
7
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Command } from '@commander-js/extra-typings';
2
2
  import { CreateArgs } from './create-args';
3
3
  import { PostgreSQLAdapter } from './postgresql-adapter';
4
- export { PostgreSQLAdapter as PostreSQLAdapter };
5
- export declare function factory(args: CreateArgs): PostgreSQLAdapter;
4
+ export declare function factory(args: CreateArgs): Promise<PostgreSQLAdapter>;
6
5
  export declare function createOptions(command: Command): void;
7
6
  export declare const description = "PostgreSQL storage adapter";
package/dist/index.js CHANGED
@@ -1,16 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.description = exports.PostreSQLAdapter = void 0;
3
+ exports.description = void 0;
4
4
  exports.factory = factory;
5
5
  exports.createOptions = createOptions;
6
+ const extra_typings_1 = require("@commander-js/extra-typings");
6
7
  const postgresql_adapter_1 = require("./postgresql-adapter");
7
- Object.defineProperty(exports, "PostreSQLAdapter", { enumerable: true, get: function () { return postgresql_adapter_1.PostgreSQLAdapter; } });
8
- function factory(args) {
8
+ const promises_1 = require("node:fs/promises");
9
+ async function factory(args) {
10
+ const { sslCa, sslCert, sslKey } = args;
11
+ if (sslCa) {
12
+ args.sslCa = await (0, promises_1.readFile)(sslCa);
13
+ }
14
+ if (sslCert && sslKey) {
15
+ args.sslCert = await (0, promises_1.readFile)(sslCert);
16
+ args.sslKey = await (0, promises_1.readFile)(sslKey);
17
+ }
9
18
  return new postgresql_adapter_1.PostgreSQLAdapter(args);
10
19
  }
11
20
  function createOptions(command) {
12
21
  command
13
- .option('--table-name-prefix <string>', 'Tables name prefix', 'playwright_orchestrator')
14
- .requiredOption('--connection-string <string>', 'Connection string');
22
+ .addOption(new extra_typings_1.Option('--table-name-prefix <string>', 'Tables name prefix')
23
+ .default('playwright_orchestrator')
24
+ .env('TABLE_NAME_PREFIX'))
25
+ .addOption(new extra_typings_1.Option('--ssl-ca <string>', 'SSL CA file').env('SSL_CA'))
26
+ .addOption(new extra_typings_1.Option('--ssl-cert <string>', 'SSL certificate file').env('SSL_CERT'))
27
+ .addOption(new extra_typings_1.Option('--ssl-key <string>', 'SSL key file').env('SSL_KEY'))
28
+ .addOption(new extra_typings_1.Option('--connection-string <string>', 'Connection string')
29
+ .makeOptionMandatory()
30
+ .env('CONNECTION_STRING'));
15
31
  }
16
32
  exports.description = 'PostgreSQL storage adapter';
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwright-orchestrator/pg",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "keywords": [],
5
5
  "author": "Rostyslav Kudrevatykh",
6
6
  "license": "Apache-2.0",
@@ -4,7 +4,7 @@ export declare class PostgreSQLAdapter extends Adapter {
4
4
  private readonly configTable;
5
5
  private readonly testsTable;
6
6
  private readonly pool;
7
- constructor({ connectionString, tableNamePrefix }: CreateArgs);
7
+ constructor({ connectionString, tableNamePrefix, sslCa, sslCert, sslKey }: CreateArgs);
8
8
  getNextTest(runId: string, config: TestRunConfig): Promise<TestItem | undefined>;
9
9
  finishTest(runId: string, test: TestItem): Promise<void>;
10
10
  failTest(runId: string, test: TestItem): Promise<void>;
@@ -4,9 +4,18 @@ exports.PostgreSQLAdapter = void 0;
4
4
  const core_1 = require("@playwright-orchestrator/core");
5
5
  const pg_1 = require("pg");
6
6
  class PostgreSQLAdapter extends core_1.Adapter {
7
- constructor({ connectionString, tableNamePrefix }) {
7
+ constructor({ connectionString, tableNamePrefix, sslCa, sslCert, sslKey }) {
8
8
  super();
9
- this.pool = new pg_1.Pool({ connectionString });
9
+ const config = { connectionString };
10
+ config.ssl = sslCa || sslCert || sslKey ? {} : undefined;
11
+ if (sslCa) {
12
+ config.ssl.ca = sslCa;
13
+ }
14
+ if (sslCert && sslKey) {
15
+ config.ssl.cert = sslCert;
16
+ config.ssl.key = sslKey;
17
+ }
18
+ this.pool = new pg_1.Pool(config);
10
19
  this.configTable = (0, pg_1.escapeIdentifier)(`${tableNamePrefix}_test_runs`);
11
20
  this.testsTable = (0, pg_1.escapeIdentifier)(`${tableNamePrefix}_tests`);
12
21
  }
@@ -28,7 +37,7 @@ class PostgreSQLAdapter extends core_1.Adapter {
28
37
  FROM next_test
29
38
  WHERE t.run_id = $1 AND t.order_num = next_test.order_num
30
39
  RETURNING *`,
31
- values: [runId, core_1.TestStatus.Ready, core_1.TestStatus.Running],
40
+ values: [runId, core_1.TestStatus.Ready, core_1.TestStatus.Ongoing],
32
41
  });
33
42
  await client.query('COMMIT');
34
43
  if (result.rowCount === 0)
@@ -103,35 +112,59 @@ class PostgreSQLAdapter extends core_1.Adapter {
103
112
  updated TIMESTAMP NOT NULL DEFAULT NOW(),
104
113
  PRIMARY KEY (run_id, order_num),
105
114
  FOREIGN KEY (run_id) REFERENCES ${this.configTable}(id)
106
- );`);
115
+ );
116
+ CREATE INDEX IF NOT EXISTS status_idx ON ${this.testsTable}(status);`);
107
117
  }
108
118
  async startShard(runId) {
109
- // Avoid updating updated field as shard can be started multiple times in uncertain order and time.
110
- const result = await this.pool.query({
111
- name: 'update-start-config',
112
- text: `UPDATE ${this.configTable}
113
- SET status =
114
- CASE
115
- WHEN status IN ($1, $2) THEN $2
116
- ELSE $3
117
- END
118
- WHERE id = $4
119
- RETURNING *`,
120
- values: [core_1.RunStatus.Finished, core_1.RunStatus.Rerun, core_1.RunStatus.Run, runId],
121
- });
122
- if (result.rowCount === 0) {
123
- throw new Error(`Run ${runId} not found`);
119
+ const client = await this.pool.connect();
120
+ try {
121
+ await client.query('BEGIN');
122
+ let result = await client.query({
123
+ text: `
124
+ SELECT *
125
+ FROM ${this.configTable}
126
+ WHERE id = $1
127
+ FOR UPDATE`,
128
+ values: [runId],
129
+ });
130
+ if (result.rowCount === 0) {
131
+ throw new Error(`Run ${runId} not found`);
132
+ }
133
+ const { updated: updatedBefore, status: statusBefore } = result.rows[0];
134
+ if (statusBefore === core_1.RunStatus.Created || statusBefore === core_1.RunStatus.Finished) {
135
+ await client.query({
136
+ text: `
137
+ UPDATE ${this.testsTable}
138
+ SET updated = NOW(), status = $3
139
+ WHERE run_id = $1 AND status = $2 AND updated <= $4;`,
140
+ values: [runId, core_1.TestStatus.Failed, core_1.TestStatus.Ready, updatedBefore],
141
+ });
142
+ // using str interpolation for case statement to avoid casting ints to strings
143
+ result = await client.query({
144
+ text: `
145
+ UPDATE ${this.configTable}
146
+ SET status = (CASE
147
+ WHEN status = $2 THEN ${core_1.RunStatus.Run}
148
+ ELSE ${core_1.RunStatus.RepeatRun}
149
+ END),
150
+ updated = NOW()
151
+ WHERE id = $1
152
+ RETURNING *;`,
153
+ values: [runId, core_1.RunStatus.Created],
154
+ });
155
+ }
156
+ await client.query('COMMIT');
157
+ const { updated, status, config } = result.rows[0];
158
+ const mappedConfig = { ...config, updated: updated.getTime(), status };
159
+ return mappedConfig;
160
+ }
161
+ catch (e) {
162
+ await client.query('ROLLBACK');
163
+ throw e;
164
+ }
165
+ finally {
166
+ client.release();
124
167
  }
125
- const { updated, status, config } = result.rows[0];
126
- const mappedConfig = { ...config, updated: updated.getTime(), status };
127
- await this.pool.query({
128
- name: 'update-tests-status',
129
- text: `UPDATE ${this.testsTable}
130
- SET status = $1
131
- WHERE run_id = $2 AND status = $3 AND updated < $4`,
132
- values: [core_1.TestStatus.Ready, runId, core_1.TestStatus.Failed, updated],
133
- });
134
- return mappedConfig;
135
168
  }
136
169
  async finishShard(runId) {
137
170
  // set 'updated' field to current time as test run exhausted all tests
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwright-orchestrator/pg",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "keywords": [],
5
5
  "author": "Rostyslav Kudrevatykh",
6
6
  "license": "Apache-2.0",