@powersync/service-module-postgres-storage 0.10.8 → 0.10.9

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 (39) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/migrations/scripts/1756282360128-connection-reporting.d.ts +3 -0
  4. package/dist/@types/storage/PostgresReportStorage.d.ts +24 -0
  5. package/dist/@types/storage/PostgresStorageProvider.d.ts +1 -1
  6. package/dist/@types/storage/storage-index.d.ts +0 -1
  7. package/dist/@types/types/models/SdkReporting.d.ts +21 -0
  8. package/dist/@types/types/models/models-index.d.ts +1 -0
  9. package/dist/@types/{storage/PostgresTestStorageFactoryGenerator.d.ts → utils/test-utils.d.ts} +5 -3
  10. package/dist/@types/utils/utils-index.d.ts +1 -0
  11. package/dist/migrations/scripts/1756282360128-connection-reporting.js +107 -0
  12. package/dist/migrations/scripts/1756282360128-connection-reporting.js.map +1 -0
  13. package/dist/storage/PostgresReportStorage.js +238 -0
  14. package/dist/storage/PostgresReportStorage.js.map +1 -0
  15. package/dist/storage/PostgresStorageProvider.js +10 -1
  16. package/dist/storage/PostgresStorageProvider.js.map +1 -1
  17. package/dist/storage/storage-index.js +0 -1
  18. package/dist/storage/storage-index.js.map +1 -1
  19. package/dist/types/models/SdkReporting.js +17 -0
  20. package/dist/types/models/SdkReporting.js.map +1 -0
  21. package/dist/types/models/models-index.js +1 -0
  22. package/dist/types/models/models-index.js.map +1 -1
  23. package/dist/{storage/PostgresTestStorageFactoryGenerator.js → utils/test-utils.js} +22 -6
  24. package/dist/utils/test-utils.js.map +1 -0
  25. package/dist/utils/utils-index.js +1 -0
  26. package/dist/utils/utils-index.js.map +1 -1
  27. package/package.json +9 -9
  28. package/src/migrations/scripts/1756282360128-connection-reporting.ts +41 -0
  29. package/src/storage/PostgresReportStorage.ts +258 -0
  30. package/src/storage/PostgresStorageProvider.ts +13 -2
  31. package/src/storage/storage-index.ts +0 -1
  32. package/src/types/models/SdkReporting.ts +23 -0
  33. package/src/types/models/models-index.ts +1 -0
  34. package/src/{storage/PostgresTestStorageFactoryGenerator.ts → utils/test-utils.ts} +21 -5
  35. package/src/utils/utils-index.ts +1 -0
  36. package/test/src/__snapshots__/connection-report-storage.test.ts.snap +215 -0
  37. package/test/src/connection-report-storage.test.ts +233 -0
  38. package/test/src/util.ts +3 -6
  39. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +0 -1
@@ -0,0 +1,233 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
+ import { POSTGRES_REPORT_STORAGE_FACTORY } from './util.js';
3
+ import { event_types } from '@powersync/service-types';
4
+ import { register, ReportUserData } from '@powersync/service-core-tests';
5
+ import { PostgresReportStorage } from '../../src/storage/PostgresReportStorage.js';
6
+ import { DateTimeValue } from '@powersync/service-sync-rules';
7
+
8
+ const factory = await POSTGRES_REPORT_STORAGE_FACTORY();
9
+ const userData = register.REPORT_TEST_USERS;
10
+ const dates = register.REPORT_TEST_DATES;
11
+
12
+ function removeVolatileFields(sdks: event_types.ClientConnection[]): Partial<event_types.ClientConnection>[] {
13
+ return sdks.map((sdk) => {
14
+ const { id, disconnected_at, connected_at, jwt_exp, ...rest } = sdk;
15
+ return {
16
+ ...rest
17
+ };
18
+ });
19
+ }
20
+
21
+ async function loadData(userData: ReportUserData, factory: PostgresReportStorage) {
22
+ await factory.db.sql`
23
+ INSERT INTO
24
+ connection_report_events (
25
+ user_id,
26
+ client_id,
27
+ connected_at,
28
+ sdk,
29
+ user_agent,
30
+ jwt_exp,
31
+ id,
32
+ disconnected_at
33
+ )
34
+ VALUES
35
+ (
36
+ ${{ type: 'varchar', value: userData.user_one.user_id }},
37
+ ${{ type: 'varchar', value: userData.user_one.client_id }},
38
+ ${{ type: 1184, value: userData.user_one.connected_at.toISOString() }},
39
+ ${{ type: 'varchar', value: userData.user_one.sdk }},
40
+ ${{ type: 'varchar', value: userData.user_one.user_agent }},
41
+ ${{ type: 1184, value: userData.user_one.jwt_exp.toISOString() }},
42
+ ${{ type: 'varchar', value: '1' }},
43
+ NULL
44
+ ),
45
+ (
46
+ ${{ type: 'varchar', value: userData.user_two.user_id }},
47
+ ${{ type: 'varchar', value: userData.user_two.client_id }},
48
+ ${{ type: 1184, value: userData.user_two.connected_at.toISOString() }},
49
+ ${{ type: 'varchar', value: userData.user_two.sdk }},
50
+ ${{ type: 'varchar', value: userData.user_two.user_agent }},
51
+ ${{ type: 1184, value: userData.user_two.jwt_exp.toISOString() }},
52
+ ${{ type: 'varchar', value: '2' }},
53
+ NULL
54
+ ),
55
+ (
56
+ ${{ type: 'varchar', value: userData.user_four.user_id }},
57
+ ${{ type: 'varchar', value: userData.user_four.client_id }},
58
+ ${{ type: 1184, value: userData.user_four.connected_at.toISOString() }},
59
+ ${{ type: 'varchar', value: userData.user_four.sdk }},
60
+ ${{ type: 'varchar', value: userData.user_four.user_agent }},
61
+ ${{ type: 1184, value: userData.user_four.jwt_exp.toISOString() }},
62
+ ${{ type: 'varchar', value: '4' }},
63
+ NULL
64
+ ),
65
+ (
66
+ ${{ type: 'varchar', value: userData.user_old.user_id }},
67
+ ${{ type: 'varchar', value: userData.user_old.client_id }},
68
+ ${{ type: 1184, value: userData.user_old.connected_at.toISOString() }},
69
+ ${{ type: 'varchar', value: userData.user_old.sdk }},
70
+ ${{ type: 'varchar', value: userData.user_old.user_agent }},
71
+ ${{ type: 1184, value: userData.user_old.jwt_exp.toISOString() }},
72
+ ${{ type: 'varchar', value: '5' }},
73
+ NULL
74
+ ),
75
+ (
76
+ ${{ type: 'varchar', value: userData.user_three.user_id }},
77
+ ${{ type: 'varchar', value: userData.user_three.client_id }},
78
+ ${{ type: 1184, value: userData.user_three.connected_at.toISOString() }},
79
+ ${{ type: 'varchar', value: userData.user_three.sdk }},
80
+ ${{ type: 'varchar', value: userData.user_three.user_agent }},
81
+ NULL,
82
+ ${{ type: 'varchar', value: '3' }},
83
+ ${{ type: 1184, value: userData.user_three.disconnected_at.toISOString() }}
84
+ ),
85
+ (
86
+ ${{ type: 'varchar', value: userData.user_week.user_id }},
87
+ ${{ type: 'varchar', value: userData.user_week.client_id }},
88
+ ${{ type: 1184, value: userData.user_week.connected_at.toISOString() }},
89
+ ${{ type: 'varchar', value: userData.user_week.sdk }},
90
+ ${{ type: 'varchar', value: userData.user_week.user_agent }},
91
+ NULL,
92
+ ${{ type: 'varchar', value: 'week' }},
93
+ ${{ type: 1184, value: userData.user_week.disconnected_at.toISOString() }}
94
+ ),
95
+ (
96
+ ${{ type: 'varchar', value: userData.user_month.user_id }},
97
+ ${{ type: 'varchar', value: userData.user_month.client_id }},
98
+ ${{ type: 1184, value: userData.user_month.connected_at.toISOString() }},
99
+ ${{ type: 'varchar', value: userData.user_month.sdk }},
100
+ ${{ type: 'varchar', value: userData.user_month.user_agent }},
101
+ NULL,
102
+ ${{ type: 'varchar', value: 'month' }},
103
+ ${{ type: 1184, value: userData.user_month.disconnected_at.toISOString() }}
104
+ ),
105
+ (
106
+ ${{ type: 'varchar', value: userData.user_expired.user_id }},
107
+ ${{ type: 'varchar', value: userData.user_expired.client_id }},
108
+ ${{ type: 1184, value: userData.user_expired.connected_at.toISOString() }},
109
+ ${{ type: 'varchar', value: userData.user_expired.sdk }},
110
+ ${{ type: 'varchar', value: userData.user_expired.user_agent }},
111
+ ${{ type: 1184, value: userData.user_expired.jwt_exp.toISOString() }},
112
+ ${{ type: 'varchar', value: 'expired' }},
113
+ NULL
114
+ )
115
+ `.execute();
116
+ }
117
+
118
+ async function deleteData(factory: PostgresReportStorage) {
119
+ await factory.db.sql`TRUNCATE TABLE connection_report_events`.execute();
120
+ }
121
+
122
+ beforeAll(async () => {
123
+ await loadData(userData, factory);
124
+ });
125
+ afterAll(async () => {
126
+ await deleteData(factory);
127
+ });
128
+
129
+ describe('Report storage tests', async () => {
130
+ await register.registerReportTests(factory);
131
+ });
132
+
133
+ describe('Connection report storage', async () => {
134
+ it('Should update a connection event if its within a day', async () => {
135
+ const newConnectAt = new Date(
136
+ dates.now.getFullYear(),
137
+ dates.now.getMonth(),
138
+ dates.now.getDate(),
139
+ dates.now.getHours(),
140
+ dates.now.getMinutes() + 20
141
+ );
142
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
143
+ await factory.reportClientConnection({
144
+ sdk: userData.user_one.sdk,
145
+ connected_at: newConnectAt,
146
+ jwt_exp: jwtExp,
147
+ client_id: userData.user_one.client_id,
148
+ user_id: userData.user_one.user_id,
149
+ user_agent: userData.user_one.user_agent
150
+ });
151
+
152
+ const sdk = await factory.db
153
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_one.user_id }} AND client_id = ${{ type: 'varchar', value: userData.user_one.client_id }}`.rows<event_types.ClientConnection>();
154
+ expect(sdk).toHaveLength(1);
155
+ expect(new Date((sdk[0].connected_at as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
156
+ newConnectAt.toISOString()
157
+ );
158
+ expect(new Date((sdk[0].jwt_exp! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
159
+ jwtExp.toISOString()
160
+ );
161
+ expect(sdk[0].disconnected_at).toBeNull();
162
+ const cleaned = removeVolatileFields(sdk);
163
+ expect(cleaned).toMatchSnapshot();
164
+ });
165
+
166
+ it('Should update a connection event and make it disconnected', async () => {
167
+ const disconnectAt = new Date(
168
+ dates.now.getFullYear(),
169
+ dates.now.getMonth(),
170
+ dates.now.getDate(),
171
+ dates.now.getHours(),
172
+ dates.now.getMinutes() + 20
173
+ );
174
+ const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
175
+
176
+ await factory.reportClientDisconnection({
177
+ disconnected_at: disconnectAt,
178
+ jwt_exp: jwtExp,
179
+ client_id: userData.user_three.client_id,
180
+ user_id: userData.user_three.user_id,
181
+ user_agent: userData.user_three.user_agent,
182
+ connected_at: dates.yesterday
183
+ });
184
+
185
+ const sdk = await factory.db
186
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_three.user_id }}`.rows<event_types.ClientConnection>();
187
+ expect(sdk).toHaveLength(1);
188
+ console.log(sdk[0]);
189
+ expect(new Date((sdk[0].disconnected_at! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
190
+ disconnectAt.toISOString()
191
+ );
192
+ const cleaned = removeVolatileFields(sdk);
193
+ expect(cleaned).toMatchSnapshot();
194
+ });
195
+
196
+ it('Should create a connection event if its after a day', async () => {
197
+ const newConnectAt = new Date(
198
+ dates.now.getFullYear(),
199
+ dates.now.getMonth(),
200
+ dates.now.getDate() + 1,
201
+ dates.now.getHours()
202
+ );
203
+ const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
204
+
205
+ await factory.reportClientConnection({
206
+ sdk: userData.user_week.sdk,
207
+ connected_at: newConnectAt,
208
+ jwt_exp: jwtExp,
209
+ client_id: userData.user_week.client_id,
210
+ user_id: userData.user_week.user_id,
211
+ user_agent: userData.user_week.user_agent
212
+ });
213
+
214
+ const sdk = await factory.db
215
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_week.user_id }}`.rows<event_types.ClientConnection>();
216
+ expect(sdk).toHaveLength(2);
217
+ const cleaned = removeVolatileFields(sdk);
218
+ expect(cleaned).toMatchSnapshot();
219
+ });
220
+
221
+ it('Should delete rows older than specified range', async () => {
222
+ await deleteData(factory);
223
+ await loadData(userData, factory);
224
+ await factory.deleteOldConnectionData({
225
+ date: dates.weekAgo
226
+ });
227
+ const sdk = await factory.getClientConnectionReports({
228
+ start: dates.monthAgo,
229
+ end: dates.now
230
+ });
231
+ expect(sdk).toMatchSnapshot();
232
+ });
233
+ });
package/test/src/util.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
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';
3
+ import { normalizePostgresStorageConfig, PostgresMigrationAgent } from '../../src/index.js';
9
4
  import { env } from './env.js';
5
+ import { postgresTestSetup } from '../../src/utils/test-utils.js';
10
6
 
11
7
  const __filename = fileURLToPath(import.meta.url);
12
8
  const __dirname = path.dirname(__filename);
@@ -37,3 +33,4 @@ export const POSTGRES_STORAGE_SETUP = postgresTestSetup({
37
33
  });
38
34
 
39
35
  export const POSTGRES_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.factory;
36
+ export const POSTGRES_REPORT_STORAGE_FACTORY = POSTGRES_STORAGE_SETUP.reportFactory;
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostgresTestStorageFactoryGenerator.js","sourceRoot":"","sources":["../../src/storage/PostgresTestStorageFactoryGenerator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAiE,MAAM,yBAAyB,CAAC;AACnH,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,8BAA8B,EAAgC,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAWjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,cAA0C,EAAE,EAAE;IAC9E,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,YAAqB;QAC3B,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,OAAO,EAAE,SAAkB;KAC5B,CAAC;IAEF,MAAM,uBAAuB,GAAG,8BAA8B,CAAC,WAAW,CAAC,CAAC;IAE5E,MAAM,OAAO,GAAG,KAAK,EAAE,SAAyC,EAAE,EAAE;;;YAClE,MAAY,gBAAgB,kCAA8B,IAAI,SAAS,CAAC,gBAAgB,EAAE,OAAA,CAAC;YAC3F,MAAY,cAAc,kCAAG,cAAc,CAAC,cAAc;gBACxD,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC;gBAC5C,CAAC,CAAC,IAAI,sBAAsB,CAAC,WAAW,CAAC,OAAA,CAAC;YAC5C,gBAAgB,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;YAExD,MAAM,kBAAkB,GAAG,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAA+B,CAAC;YAEpG,MAAM,gBAAgB,CAAC,OAAO,CAAC;gBAC7B,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI;gBAC9C,gBAAgB,EAAE;oBAChB,eAAe,EAAE,kBAAkB;iBACpC;aACF,CAAC,CAAC;YAEH,IAAI,SAAS,IAAI,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;gBACnD,MAAM,gBAAgB,CAAC,OAAO,CAAC;oBAC7B,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;oBAC5C,gBAAgB,EAAE;wBAChB,eAAe,EAAE,kBAAkB;qBACpC;iBACF,CAAC,CAAC;YACL,CAAC;;;;;;;;;;;KACF,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,KAAK,EAAE,OAA4B,EAAE,EAAE;YAC9C,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;oBACzB,MAAM,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACnD,CAAC;gBAED,OAAO,IAAI,4BAA4B,CAAC;oBACtC,MAAM,EAAE,uBAAuB;oBAC/B,gBAAgB,EAAE,OAAO;iBAC1B,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;gBACZ,iFAAiF;gBACjF,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;gBAC5B,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QACD,OAAO;KACR,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,cAA0C,EAAE,EAAE;IAChG,OAAO,iBAAiB,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC;AACnD,CAAC,CAAC"}