@powersync/service-module-postgres-storage 0.0.0-dev-20250827072023 → 0.0.0-dev-20250828090417

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +20 -14
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/storage/PostgresStorageProvider.d.ts +1 -1
  4. package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +3 -1
  5. package/dist/@types/{utils/test-utils.d.ts → storage/PostgresTestStorageFactoryGenerator.d.ts} +3 -5
  6. package/dist/@types/storage/storage-index.d.ts +1 -0
  7. package/dist/@types/types/models/models-index.d.ts +0 -1
  8. package/dist/@types/utils/utils-index.d.ts +0 -1
  9. package/dist/migrations/scripts/1684951997326-init.js +0 -18
  10. package/dist/migrations/scripts/1684951997326-init.js.map +1 -1
  11. package/dist/storage/PostgresStorageProvider.js +1 -10
  12. package/dist/storage/PostgresStorageProvider.js.map +1 -1
  13. package/dist/storage/PostgresSyncRulesStorage.js +19 -6
  14. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
  15. package/dist/{utils/test-utils.js → storage/PostgresTestStorageFactoryGenerator.js} +6 -22
  16. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +1 -0
  17. package/dist/storage/batch/PostgresBucketBatch.js +5 -2
  18. package/dist/storage/batch/PostgresBucketBatch.js.map +1 -1
  19. package/dist/storage/storage-index.js +1 -0
  20. package/dist/storage/storage-index.js.map +1 -1
  21. package/dist/types/models/models-index.js +0 -1
  22. package/dist/types/models/models-index.js.map +1 -1
  23. package/dist/utils/db.js +0 -1
  24. package/dist/utils/db.js.map +1 -1
  25. package/dist/utils/utils-index.js +0 -1
  26. package/dist/utils/utils-index.js.map +1 -1
  27. package/package.json +12 -11
  28. package/src/migrations/scripts/1684951997326-init.ts +0 -22
  29. package/src/storage/PostgresStorageProvider.ts +2 -13
  30. package/src/storage/PostgresSyncRulesStorage.ts +26 -7
  31. package/src/{utils/test-utils.ts → storage/PostgresTestStorageFactoryGenerator.ts} +5 -21
  32. package/src/storage/batch/PostgresBucketBatch.ts +4 -2
  33. package/src/storage/storage-index.ts +1 -0
  34. package/src/types/models/models-index.ts +0 -1
  35. package/src/utils/db.ts +0 -1
  36. package/src/utils/utils-index.ts +0 -1
  37. package/test/src/__snapshots__/storage_sync.test.ts.snap +110 -0
  38. package/test/src/util.ts +6 -3
  39. package/dist/@types/storage/PostgresReportStorageFactory.d.ts +0 -24
  40. package/dist/@types/types/models/SdkReporting.d.ts +0 -21
  41. package/dist/storage/PostgresReportStorageFactory.js +0 -238
  42. package/dist/storage/PostgresReportStorageFactory.js.map +0 -1
  43. package/dist/types/models/SdkReporting.js +0 -17
  44. package/dist/types/models/SdkReporting.js.map +0 -1
  45. package/dist/utils/test-utils.js.map +0 -1
  46. package/src/storage/PostgresReportStorageFactory.ts +0 -258
  47. package/src/types/models/SdkReporting.ts +0 -23
  48. package/test/src/__snapshots__/connection-report-storage.test.ts.snap +0 -215
  49. package/test/src/connection-report-storage.test.ts +0 -233
@@ -128,28 +128,6 @@ export const up: migrations.PowerSyncMigrationFunction = async (context) => {
128
128
  CONSTRAINT unique_user_sync PRIMARY KEY (user_id, sync_rules_id)
129
129
  );
130
130
  `.execute();
131
- await db.sql`
132
- CREATE TABLE connection_report_events (
133
- id TEXT PRIMARY KEY,
134
- user_agent TEXT NOT NULL,
135
- client_id TEXT NOT NULL,
136
- user_id TEXT NOT NULL,
137
- sdk TEXT NOT NULL,
138
- jwt_exp TIMESTAMP WITH TIME ZONE,
139
- connected_at TIMESTAMP WITH TIME ZONE NOT NULL,
140
- disconnected_at TIMESTAMP WITH TIME ZONE
141
- )
142
- `.execute();
143
-
144
- await db.sql`
145
- CREATE INDEX sdk_list_index ON connection_report_events (connected_at, jwt_exp, disconnected_at)
146
- `.execute();
147
-
148
- await db.sql`CREATE INDEX sdk_user_id_index ON connection_report_events (user_id)`.execute();
149
-
150
- await db.sql`CREATE INDEX sdk_client_id_index ON connection_report_events (client_id)`.execute();
151
-
152
- await db.sql`CREATE INDEX sdk_index ON connection_report_events (sdk)`.execute();
153
131
  });
154
132
  };
155
133
 
@@ -5,9 +5,8 @@ import { storage } from '@powersync/service-core';
5
5
  import { isPostgresStorageConfig, normalizePostgresStorageConfig, PostgresStorageConfig } from '../types/types.js';
6
6
  import { dropTables } from '../utils/db.js';
7
7
  import { PostgresBucketStorageFactory } from './PostgresBucketStorageFactory.js';
8
- import { PostgresReportStorageFactory } from './PostgresReportStorageFactory.js';
9
8
 
10
- export class PostgresStorageProvider implements storage.StorageProvider {
9
+ export class PostgresStorageProvider implements storage.BucketStorageProvider {
11
10
  get type() {
12
11
  return lib_postgres.POSTGRES_CONNECTION_TYPE;
13
12
  }
@@ -29,23 +28,13 @@ export class PostgresStorageProvider implements storage.StorageProvider {
29
28
  config: normalizedConfig,
30
29
  slot_name_prefix: options.resolvedConfig.slot_name_prefix
31
30
  });
32
-
33
- const reportStorageFactory = new PostgresReportStorageFactory({
34
- config: normalizedConfig
35
- });
36
-
37
31
  return {
38
- reportStorage: reportStorageFactory,
39
32
  storage: storageFactory,
40
- shutDown: async () => {
41
- await storageFactory.db[Symbol.asyncDispose]();
42
- await reportStorageFactory.db[Symbol.asyncDispose]();
43
- },
33
+ shutDown: async () => storageFactory.db[Symbol.asyncDispose](),
44
34
  tearDown: async () => {
45
35
  logger.info(`Tearing down Postgres storage: ${normalizedConfig.database}...`);
46
36
  await dropTables(storageFactory.db);
47
37
  await storageFactory.db[Symbol.asyncDispose]();
48
- await reportStorageFactory.db[Symbol.asyncDispose]();
49
38
  return true;
50
39
  }
51
40
  } satisfies storage.ActiveStorage;
@@ -1,13 +1,16 @@
1
1
  import * as lib_postgres from '@powersync/lib-service-postgres';
2
2
  import {
3
3
  BroadcastIterable,
4
+ BucketChecksum,
4
5
  CHECKPOINT_INVALIDATE_ALL,
5
6
  CheckpointChanges,
7
+ CompactOptions,
6
8
  GetCheckpointChangesOptions,
7
9
  InternalOpId,
8
10
  internalToExternalOpId,
9
11
  LastValueSink,
10
12
  maxLsn,
13
+ PartialChecksum,
11
14
  ReplicationCheckpoint,
12
15
  storage,
13
16
  utils,
@@ -109,6 +112,10 @@ export class PostgresSyncRulesStorage
109
112
  return new PostgresCompactor(this.db, this.group_id, options).compact();
110
113
  }
111
114
 
115
+ async populatePersistentChecksumCache(options: Pick<CompactOptions, 'signal' | 'maxOpId'>): Promise<void> {
116
+ // no-op - checksum cache is not implemented for Postgres yet
117
+ }
118
+
112
119
  lastWriteCheckpoint(filters: storage.SyncStorageLastWriteCheckpointFilters): Promise<bigint | null> {
113
120
  return this.writeCheckpointAPI.lastWriteCheckpoint({
114
121
  ...filters,
@@ -571,6 +578,10 @@ export class PostgresSyncRulesStorage
571
578
  return this.checksumCache.getChecksumMap(checkpoint, buckets);
572
579
  }
573
580
 
581
+ clearChecksumCache() {
582
+ this.checksumCache.clear();
583
+ }
584
+
574
585
  async terminate(options?: storage.TerminateOptions) {
575
586
  if (!options || options?.clearStorage) {
576
587
  await this.clear(options);
@@ -692,16 +703,24 @@ export class PostgresSyncRulesStorage
692
703
  b.bucket_name;
693
704
  `.rows<{ bucket: string; checksum_total: bigint; total: bigint; has_clear_op: number }>();
694
705
 
695
- return new Map<string, storage.PartialChecksum>(
706
+ return new Map<string, storage.PartialOrFullChecksum>(
696
707
  results.map((doc) => {
708
+ const checksum = Number(BigInt(doc.checksum_total) & 0xffffffffn) & 0xffffffff;
709
+
697
710
  return [
698
711
  doc.bucket,
699
- {
700
- bucket: doc.bucket,
701
- partialCount: Number(doc.total),
702
- partialChecksum: Number(BigInt(doc.checksum_total) & 0xffffffffn) & 0xffffffff,
703
- isFullChecksum: doc.has_clear_op == 1
704
- } satisfies storage.PartialChecksum
712
+ doc.has_clear_op == 1
713
+ ? ({
714
+ // full checksum
715
+ bucket: doc.bucket,
716
+ count: Number(doc.total),
717
+ checksum
718
+ } satisfies BucketChecksum)
719
+ : ({
720
+ bucket: doc.bucket,
721
+ partialCount: Number(doc.total),
722
+ partialChecksum: checksum
723
+ } satisfies PartialChecksum)
705
724
  ];
706
725
  })
707
726
  );
@@ -1,8 +1,7 @@
1
1
  import { framework, PowerSyncMigrationManager, ServiceContext, TestStorageOptions } from '@powersync/service-core';
2
2
  import { PostgresMigrationAgent } from '../migrations/PostgresMigrationAgent.js';
3
3
  import { normalizePostgresStorageConfig, PostgresStorageConfigDecoded } from '../types/types.js';
4
- import { PostgresReportStorageFactory } from '../storage/PostgresReportStorageFactory.js';
5
- import { PostgresBucketStorageFactory } from '../storage/PostgresBucketStorageFactory.js';
4
+ import { PostgresBucketStorageFactory } from './PostgresBucketStorageFactory.js';
6
5
 
7
6
  export type PostgresTestStorageOptions = {
8
7
  url: string;
@@ -13,7 +12,7 @@ export type PostgresTestStorageOptions = {
13
12
  migrationAgent?: (config: PostgresStorageConfigDecoded) => PostgresMigrationAgent;
14
13
  };
15
14
 
16
- export function postgresTestSetup(factoryOptions: PostgresTestStorageOptions) {
15
+ export const postgresTestSetup = (factoryOptions: PostgresTestStorageOptions) => {
17
16
  const BASE_CONFIG = {
18
17
  type: 'postgresql' as const,
19
18
  uri: factoryOptions.url,
@@ -49,21 +48,6 @@ export function postgresTestSetup(factoryOptions: PostgresTestStorageOptions) {
49
48
  };
50
49
 
51
50
  return {
52
- reportFactory: async (options?: TestStorageOptions) => {
53
- try {
54
- if (!options?.doNotClear) {
55
- await migrate(framework.migrations.Direction.Up);
56
- }
57
-
58
- return new PostgresReportStorageFactory({
59
- config: TEST_CONNECTION_OPTIONS
60
- });
61
- } catch (ex) {
62
- // Vitest does not display these errors nicely when using the `await using` syntx
63
- console.error(ex, ex.cause);
64
- throw ex;
65
- }
66
- },
67
51
  factory: async (options?: TestStorageOptions) => {
68
52
  try {
69
53
  if (!options?.doNotClear) {
@@ -82,8 +66,8 @@ export function postgresTestSetup(factoryOptions: PostgresTestStorageOptions) {
82
66
  },
83
67
  migrate
84
68
  };
85
- }
69
+ };
86
70
 
87
- export function postgresTestStorageFactoryGenerator(factoryOptions: PostgresTestStorageOptions) {
71
+ export const PostgresTestStorageFactoryGenerator = (factoryOptions: PostgresTestStorageOptions) => {
88
72
  return postgresTestSetup(factoryOptions).factory;
89
- }
73
+ };
@@ -687,7 +687,8 @@ export class PostgresBucketBatch
687
687
  // We store bytea colums for source keys
688
688
  const beforeId = operation.beforeId;
689
689
  const afterId = operation.afterId;
690
- let after = record.after;
690
+ let sourceAfter = record.after;
691
+ let after = sourceAfter && this.sync_rules.applyRowContext(sourceAfter);
691
692
  const sourceTable = record.sourceTable;
692
693
 
693
694
  let existingBuckets: CurrentBucket[] = [];
@@ -825,7 +826,8 @@ export class PostgresBucketBatch
825
826
  if (sourceTable.syncData) {
826
827
  const { results: evaluated, errors: syncErrors } = this.sync_rules.evaluateRowWithErrors({
827
828
  record: after,
828
- sourceTable
829
+ sourceTable,
830
+ bucketIdTransformer: sync_rules.SqlSyncRules.versionedBucketIdTransformer(`${this.group_id}`)
829
831
  });
830
832
 
831
833
  for (const error of syncErrors) {
@@ -2,3 +2,4 @@ export * from './PostgresBucketStorageFactory.js';
2
2
  export * from './PostgresCompactor.js';
3
3
  export * from './PostgresStorageProvider.js';
4
4
  export * from './PostgresSyncRulesStorage.js';
5
+ export * from './PostgresTestStorageFactoryGenerator.js';
@@ -8,4 +8,3 @@ export * from './Migration.js';
8
8
  export * from './SourceTable.js';
9
9
  export * from './SyncRules.js';
10
10
  export * from './WriteCheckpoint.js';
11
- export * from './SdkReporting.js';
package/src/utils/db.ts CHANGED
@@ -23,6 +23,5 @@ export const dropTables = async (client: lib_postgres.DatabaseClient) => {
23
23
  await db.sql`DROP TABLE IF EXISTS custom_write_checkpoints`.execute();
24
24
  await db.sql`DROP SEQUENCE IF EXISTS op_id_sequence`.execute();
25
25
  await db.sql`DROP SEQUENCE IF EXISTS sync_rules_id_sequence`.execute();
26
- await db.sql`DROP TABLE IF EXISTS connection_report_events`.execute();
27
26
  });
28
27
  };
@@ -2,4 +2,3 @@ export * from './bson.js';
2
2
  export * from './bucket-data.js';
3
3
  export * from './db.js';
4
4
  export * from './ts-codec.js';
5
- export * as test_utils from './test-utils.js';
@@ -104,6 +104,116 @@ exports[`sync - postgres > compacting data - invalidate checkpoint 2`] = `
104
104
  ]
105
105
  `;
106
106
 
107
+ exports[`sync - postgres > encodes sync rules id in buckes for streams 1`] = `
108
+ [
109
+ {
110
+ "checkpoint": {
111
+ "buckets": [
112
+ {
113
+ "bucket": "1#test|0[]",
114
+ "checksum": 920318466,
115
+ "count": 1,
116
+ "priority": 3,
117
+ "subscriptions": [
118
+ {
119
+ "default": 0,
120
+ },
121
+ ],
122
+ },
123
+ ],
124
+ "last_op_id": "1",
125
+ "streams": [
126
+ {
127
+ "errors": [],
128
+ "is_default": true,
129
+ "name": "test",
130
+ },
131
+ ],
132
+ "write_checkpoint": undefined,
133
+ },
134
+ },
135
+ {
136
+ "data": {
137
+ "after": "0",
138
+ "bucket": "1#test|0[]",
139
+ "data": [
140
+ {
141
+ "checksum": 920318466,
142
+ "data": "{"id":"t1","description":"Test 1"}",
143
+ "object_id": "t1",
144
+ "object_type": "test",
145
+ "op": "PUT",
146
+ "op_id": "1",
147
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
148
+ },
149
+ ],
150
+ "has_more": false,
151
+ "next_after": "1",
152
+ },
153
+ },
154
+ {
155
+ "checkpoint_complete": {
156
+ "last_op_id": "1",
157
+ },
158
+ },
159
+ ]
160
+ `;
161
+
162
+ exports[`sync - postgres > encodes sync rules id in buckes for streams 2`] = `
163
+ [
164
+ {
165
+ "checkpoint": {
166
+ "buckets": [
167
+ {
168
+ "bucket": "2#test|0[]",
169
+ "checksum": 920318466,
170
+ "count": 1,
171
+ "priority": 3,
172
+ "subscriptions": [
173
+ {
174
+ "default": 0,
175
+ },
176
+ ],
177
+ },
178
+ ],
179
+ "last_op_id": "2",
180
+ "streams": [
181
+ {
182
+ "errors": [],
183
+ "is_default": true,
184
+ "name": "test",
185
+ },
186
+ ],
187
+ "write_checkpoint": undefined,
188
+ },
189
+ },
190
+ {
191
+ "data": {
192
+ "after": "0",
193
+ "bucket": "2#test|0[]",
194
+ "data": [
195
+ {
196
+ "checksum": 920318466,
197
+ "data": "{"id":"t1","description":"Test 1"}",
198
+ "object_id": "t1",
199
+ "object_type": "test",
200
+ "op": "PUT",
201
+ "op_id": "2",
202
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
203
+ },
204
+ ],
205
+ "has_more": false,
206
+ "next_after": "2",
207
+ },
208
+ },
209
+ {
210
+ "checkpoint_complete": {
211
+ "last_op_id": "2",
212
+ },
213
+ },
214
+ ]
215
+ `;
216
+
107
217
  exports[`sync - postgres > expired token 1`] = `
108
218
  [
109
219
  {
package/test/src/util.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
- import { normalizePostgresStorageConfig, PostgresMigrationAgent } from '../../src/index.js';
3
+ import { normalizePostgresStorageConfig } from '../../src//types/types.js';
4
+ import { PostgresMigrationAgent } from '../../src/migrations/PostgresMigrationAgent.js';
5
+ import {
6
+ postgresTestSetup,
7
+ PostgresTestStorageFactoryGenerator
8
+ } from '../../src/storage/PostgresTestStorageFactoryGenerator.js';
4
9
  import { env } from './env.js';
5
- import { postgresTestSetup } from '../../src/utils/test-utils.js';
6
10
 
7
11
  const __filename = fileURLToPath(import.meta.url);
8
12
  const __dirname = path.dirname(__filename);
@@ -33,4 +37,3 @@ export const POSTGRES_STORAGE_SETUP = postgresTestSetup({
33
37
  });
34
38
 
35
39
  export const POSTGRES_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.factory;
36
- export const POSTGRES_REPORT_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.reportFactory;
@@ -1,24 +0,0 @@
1
- import { storage } from '@powersync/service-core';
2
- import * as pg_wire from '@powersync/service-jpgwire';
3
- import { event_types } from '@powersync/service-types';
4
- import * as lib_postgres from '@powersync/lib-service-postgres';
5
- import { NormalizedPostgresStorageConfig } from '../types/types.js';
6
- export type PostgresReportStorageOptions = {
7
- config: NormalizedPostgresStorageConfig;
8
- };
9
- export declare class PostgresReportStorageFactory implements storage.ReportStorage {
10
- protected options: PostgresReportStorageOptions;
11
- readonly db: lib_postgres.DatabaseClient;
12
- constructor(options: PostgresReportStorageOptions);
13
- private parseJsDate;
14
- private mapListCurrentConnectionsResponse;
15
- private listConnectionsQuery;
16
- private updateTableFilter;
17
- reportClientConnection(data: event_types.ClientConnectionBucketData): Promise<void>;
18
- reportClientDisconnection(data: event_types.ClientDisconnectionEventData): Promise<void>;
19
- getConnectedClients(): Promise<event_types.ClientConnectionReportResponse>;
20
- getClientConnectionReports(data: event_types.ClientConnectionReportRequest): Promise<event_types.ClientConnectionReportResponse>;
21
- deleteOldConnectionData(data: event_types.DeleteOldConnectionData): Promise<void>;
22
- [Symbol.asyncDispose](): Promise<void>;
23
- prepareStatements(connection: pg_wire.PgConnection): Promise<void>;
24
- }
@@ -1,21 +0,0 @@
1
- import * as t from 'ts-codec';
2
- export declare const Sdks: t.ObjectCodec<{
3
- sdk: t.IdentityCodec<t.CodecType.String>;
4
- clients: t.IdentityCodec<t.CodecType.Number>;
5
- users: t.IdentityCodec<t.CodecType.Number>;
6
- }>;
7
- export type Sdks = t.Encoded<typeof Sdks>;
8
- export declare const SdkReporting: t.ObjectCodec<{
9
- users: t.Codec<bigint, string | number, string, t.CodecProps>;
10
- sdks: t.Union<t.Codec<{
11
- data: {
12
- sdk: string;
13
- clients: number;
14
- users: number;
15
- }[];
16
- } | undefined, {
17
- data: string;
18
- } | undefined, string, t.CodecProps>, t.Codec<null, null, t.CodecType.Null, t.CodecProps>>;
19
- }>;
20
- export type SdkReporting = t.Encoded<typeof SdkReporting>;
21
- export type SdkReportingDecoded = t.Decoded<typeof SdkReporting>;
@@ -1,238 +0,0 @@
1
- import { v4 } from 'uuid';
2
- import * as lib_postgres from '@powersync/lib-service-postgres';
3
- import { SdkReporting } from '../types/models/SdkReporting.js';
4
- import { toInteger } from 'ix/util/tointeger.js';
5
- import { logger } from '@powersync/lib-services-framework';
6
- import { getStorageApplicationName } from '../utils/application-name.js';
7
- import { STORAGE_SCHEMA_NAME } from '../utils/db.js';
8
- export class PostgresReportStorageFactory {
9
- options;
10
- db;
11
- constructor(options) {
12
- this.options = options;
13
- this.db = new lib_postgres.DatabaseClient({
14
- config: options.config,
15
- schema: STORAGE_SCHEMA_NAME,
16
- applicationName: getStorageApplicationName()
17
- });
18
- this.db.registerListener({
19
- connectionCreated: async (connection) => this.prepareStatements(connection)
20
- });
21
- }
22
- parseJsDate(date) {
23
- const year = date.getFullYear();
24
- const month = date.getMonth();
25
- const today = date.getDate();
26
- const day = date.getDay();
27
- return {
28
- year,
29
- month,
30
- today,
31
- day,
32
- parsedDate: date
33
- };
34
- }
35
- mapListCurrentConnectionsResponse(result) {
36
- if (!result) {
37
- return {
38
- users: 0,
39
- sdks: []
40
- };
41
- }
42
- return {
43
- users: Number(result.users),
44
- sdks: result.sdks?.data || []
45
- };
46
- }
47
- async listConnectionsQuery() {
48
- return await this.db.sql `
49
- WITH
50
- filtered AS (
51
- SELECT
52
- *
53
- FROM
54
- connection_report_events
55
- WHERE
56
- disconnected_at IS NULL
57
- AND jwt_exp > NOW() AT TIME ZONE 'UTC'
58
- ),
59
- unique_users AS (
60
- SELECT
61
- COUNT(DISTINCT user_id) AS count
62
- FROM
63
- filtered
64
- ),
65
- sdk_versions_array AS (
66
- SELECT
67
- sdk,
68
- COUNT(DISTINCT client_id) AS clients,
69
- COUNT(DISTINCT user_id) AS users
70
- FROM
71
- filtered
72
- GROUP BY
73
- sdk
74
- )
75
- SELECT
76
- (
77
- SELECT
78
- COALESCE(count, 0)
79
- FROM
80
- unique_users
81
- ) AS users,
82
- (
83
- SELECT
84
- JSON_AGG(ROW_TO_JSON(s))
85
- FROM
86
- sdk_versions_array s
87
- ) AS sdks;
88
- `
89
- .decoded(SdkReporting)
90
- .first();
91
- }
92
- updateTableFilter() {
93
- const { year, month, today } = this.parseJsDate(new Date());
94
- const nextDay = today + 1;
95
- return {
96
- gte: new Date(year, month, today).toISOString(),
97
- lt: new Date(year, month, nextDay).toISOString()
98
- };
99
- }
100
- async reportClientConnection(data) {
101
- const { sdk, connected_at, user_id, user_agent, jwt_exp, client_id } = data;
102
- const connectIsoString = connected_at.toISOString();
103
- const jwtExpIsoString = jwt_exp.toISOString();
104
- const { gte, lt } = this.updateTableFilter();
105
- const uuid = v4();
106
- const result = await this.db.sql `
107
- UPDATE connection_report_events
108
- SET
109
- connected_at = ${{ type: 1184, value: connectIsoString }},
110
- sdk = ${{ type: 'varchar', value: sdk }},
111
- user_agent = ${{ type: 'varchar', value: user_agent }},
112
- jwt_exp = ${{ type: 1184, value: jwtExpIsoString }},
113
- disconnected_at = NULL
114
- WHERE
115
- user_id = ${{ type: 'varchar', value: user_id }}
116
- AND client_id = ${{ type: 'varchar', value: client_id }}
117
- AND connected_at >= ${{ type: 1184, value: gte }}
118
- AND connected_at < ${{ type: 1184, value: lt }};
119
- `.execute();
120
- if (result.results[1].status === 'UPDATE 0') {
121
- await this.db.sql `
122
- INSERT INTO
123
- connection_report_events (
124
- user_id,
125
- client_id,
126
- connected_at,
127
- sdk,
128
- user_agent,
129
- jwt_exp,
130
- id
131
- )
132
- VALUES
133
- (
134
- ${{ type: 'varchar', value: user_id }},
135
- ${{ type: 'varchar', value: client_id }},
136
- ${{ type: 1184, value: connectIsoString }},
137
- ${{ type: 'varchar', value: sdk }},
138
- ${{ type: 'varchar', value: user_agent }},
139
- ${{ type: 1184, value: jwtExpIsoString }},
140
- ${{ type: 'varchar', value: uuid }}
141
- )
142
- `.execute();
143
- }
144
- }
145
- async reportClientDisconnection(data) {
146
- const { user_id, client_id, disconnected_at, connected_at } = data;
147
- const disconnectIsoString = disconnected_at.toISOString();
148
- const connectIsoString = connected_at.toISOString();
149
- await this.db.sql `
150
- UPDATE connection_report_events
151
- SET
152
- disconnected_at = ${{ type: 1184, value: disconnectIsoString }},
153
- jwt_exp = NULL
154
- WHERE
155
- user_id = ${{ type: 'varchar', value: user_id }}
156
- AND client_id = ${{ type: 'varchar', value: client_id }}
157
- AND connected_at = ${{ type: 1184, value: connectIsoString }}
158
- `.execute();
159
- }
160
- async getConnectedClients() {
161
- const result = await this.listConnectionsQuery();
162
- return this.mapListCurrentConnectionsResponse(result);
163
- }
164
- async getClientConnectionReports(data) {
165
- const { start, end } = data;
166
- const result = await this.db.sql `
167
- WITH
168
- filtered AS (
169
- SELECT
170
- *
171
- FROM
172
- connection_report_events
173
- WHERE
174
- connected_at >= ${{ type: 1184, value: start.toISOString() }}
175
- AND connected_at <= ${{ type: 1184, value: end.toISOString() }}
176
- ),
177
- unique_users AS (
178
- SELECT
179
- COUNT(DISTINCT user_id) AS count
180
- FROM
181
- filtered
182
- ),
183
- sdk_versions_array AS (
184
- SELECT
185
- sdk,
186
- COUNT(DISTINCT client_id) AS clients,
187
- COUNT(DISTINCT user_id) AS users
188
- FROM
189
- filtered
190
- GROUP BY
191
- sdk
192
- )
193
- SELECT
194
- (
195
- SELECT
196
- COALESCE(count, 0)
197
- FROM
198
- unique_users
199
- ) AS users,
200
- (
201
- SELECT
202
- JSON_AGG(ROW_TO_JSON(s))
203
- FROM
204
- sdk_versions_array s
205
- ) AS sdks;
206
- `
207
- .decoded(SdkReporting)
208
- .first();
209
- return this.mapListCurrentConnectionsResponse(result);
210
- }
211
- async deleteOldConnectionData(data) {
212
- const { date } = data;
213
- const result = await this.db.sql `
214
- DELETE FROM connection_report_events
215
- WHERE
216
- connected_at < ${{ type: 1184, value: date.toISOString() }}
217
- AND (
218
- disconnected_at IS NOT NULL
219
- OR (
220
- jwt_exp < NOW() AT TIME ZONE 'UTC'
221
- AND disconnected_at IS NULL
222
- )
223
- );
224
- `.execute();
225
- const deletedRows = toInteger(result.results[1].status.split(' ')[1] || '0');
226
- if (deletedRows > 0) {
227
- logger.info(`TTL from ${date.toISOString()}: ${deletedRows} PostgresSQL rows have been removed from connection_report_events.`);
228
- }
229
- }
230
- async [Symbol.asyncDispose]() {
231
- await this.db[Symbol.asyncDispose]();
232
- }
233
- async prepareStatements(connection) {
234
- // It should be possible to prepare statements for some common operations here.
235
- // This has not been implemented yet.
236
- }
237
- }
238
- //# sourceMappingURL=PostgresReportStorageFactory.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostgresReportStorageFactory.js","sourceRoot":"","sources":["../../src/storage/PostgresReportStorageFactory.ts"],"names":[],"mappings":"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"}