@powersync/service-module-mongodb-storage 0.0.0-dev-20250730152539 → 0.0.0-dev-20250804065653
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/CHANGELOG.md +6 -6
- package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.d.ts +7 -0
- package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.js +13 -0
- package/dist/storage/implementation/MongoTestReportStorageFactoryGenerator.js.map +1 -0
- package/package.json +7 -7
- package/src/storage/implementation/MongoTestReportStorageFactoryGenerator.ts +22 -0
- package/test/src/sdk-report-storage.test.ts +253 -0
- package/test/src/util.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @powersync/service-module-mongodb-storage
|
|
2
2
|
|
|
3
|
-
## 0.0.0-dev-
|
|
3
|
+
## 0.0.0-dev-20250804065653
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
- Updated dependencies [d56eeb9]
|
|
30
30
|
- Updated dependencies [a700ec9]
|
|
31
31
|
- Updated dependencies [d4db4e2]
|
|
32
|
-
- @powersync/service-core@0.0.0-dev-
|
|
33
|
-
- @powersync/service-types@0.0.0-dev-
|
|
34
|
-
- @powersync/lib-services-framework@0.0.0-dev-
|
|
35
|
-
- @powersync/service-sync-rules@0.0.0-dev-
|
|
36
|
-
- @powersync/lib-service-mongodb@0.0.0-dev-
|
|
32
|
+
- @powersync/service-core@0.0.0-dev-20250804065653
|
|
33
|
+
- @powersync/service-types@0.0.0-dev-20250804065653
|
|
34
|
+
- @powersync/lib-services-framework@0.0.0-dev-20250804065653
|
|
35
|
+
- @powersync/service-sync-rules@0.0.0-dev-20250804065653
|
|
36
|
+
- @powersync/lib-service-mongodb@0.0.0-dev-20250804065653
|
|
37
37
|
|
|
38
38
|
## 0.10.4
|
|
39
39
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TestStorageOptions } from '@powersync/service-core';
|
|
2
|
+
import { MongoReportStorage } from '../MongoReportStorage.js';
|
|
3
|
+
export type MongoTestStorageOptions = {
|
|
4
|
+
url: string;
|
|
5
|
+
isCI: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const MongoTestReportStorageFactoryGenerator: (factoryOptions: MongoTestStorageOptions) => (options?: TestStorageOptions) => Promise<MongoReportStorage>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { connectMongoForTests } from './util.js';
|
|
2
|
+
import { MongoReportStorage } from '../MongoReportStorage.js';
|
|
3
|
+
export const MongoTestReportStorageFactoryGenerator = (factoryOptions) => {
|
|
4
|
+
return async (options) => {
|
|
5
|
+
const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
|
|
6
|
+
await db.createSdkReportingCollection();
|
|
7
|
+
if (!options?.doNotClear) {
|
|
8
|
+
await db.clear();
|
|
9
|
+
}
|
|
10
|
+
return new MongoReportStorage(db);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=MongoTestReportStorageFactoryGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoTestReportStorageFactoryGenerator.js","sourceRoot":"","sources":["../../../src/storage/implementation/MongoTestReportStorageFactoryGenerator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAO9D,MAAM,CAAC,MAAM,sCAAsC,GAAG,CAAC,cAAuC,EAAE,EAAE;IAChG,OAAO,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC5C,MAAM,EAAE,GAAG,oBAAoB,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;QAEzE,MAAM,EAAE,CAAC,4BAA4B,EAAE,CAAC;QAExC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mongodb-storage",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.0.0-dev-
|
|
5
|
+
"version": "0.0.0-dev-20250804065653",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"license": "FSL-1.1-Apache-2.0",
|
|
8
8
|
"type": "module",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"lru-cache": "^10.2.2",
|
|
28
28
|
"ts-codec": "^1.3.0",
|
|
29
29
|
"uuid": "^11.1.0",
|
|
30
|
-
"@powersync/lib-service-mongodb": "0.0.0-dev-
|
|
31
|
-
"@powersync/
|
|
32
|
-
"@powersync/service-
|
|
30
|
+
"@powersync/lib-service-mongodb": "0.0.0-dev-20250804065653",
|
|
31
|
+
"@powersync/lib-services-framework": "0.0.0-dev-20250804065653",
|
|
32
|
+
"@powersync/service-core": "0.0.0-dev-20250804065653",
|
|
33
|
+
"@powersync/service-types": "0.0.0-dev-20250804065653",
|
|
33
34
|
"@powersync/service-jsonbig": "0.17.10",
|
|
34
|
-
"@powersync/
|
|
35
|
-
"@powersync/service-sync-rules": "0.0.0-dev-20250730152539"
|
|
35
|
+
"@powersync/service-sync-rules": "0.0.0-dev-20250804065653"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@powersync/service-core-tests": "0.0.0-dev-
|
|
38
|
+
"@powersync/service-core-tests": "0.0.0-dev-20250804065653"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TestStorageOptions } from '@powersync/service-core';
|
|
2
|
+
import { connectMongoForTests } from './util.js';
|
|
3
|
+
import { MongoReportStorage } from '../MongoReportStorage.js';
|
|
4
|
+
|
|
5
|
+
export type MongoTestStorageOptions = {
|
|
6
|
+
url: string;
|
|
7
|
+
isCI: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const MongoTestReportStorageFactoryGenerator = (factoryOptions: MongoTestStorageOptions) => {
|
|
11
|
+
return async (options?: TestStorageOptions) => {
|
|
12
|
+
const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
|
|
13
|
+
|
|
14
|
+
await db.createSdkReportingCollection();
|
|
15
|
+
|
|
16
|
+
if (!options?.doNotClear) {
|
|
17
|
+
await db.clear();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return new MongoReportStorage(db);
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
+
import { INITIALIZED_MONGO_REPORT_STORAGE_FACTORY } from './util.js';
|
|
3
|
+
import { event_types } from '@powersync/service-types';
|
|
4
|
+
|
|
5
|
+
function removeVolatileFields(sdks: event_types.SdkConnectDocument[]): Partial<event_types.SdkConnectDocument>[] {
|
|
6
|
+
return sdks.map((sdk) => {
|
|
7
|
+
const { id, disconnect_at, connect_at, jwt_exp, ...rest } = sdk;
|
|
8
|
+
return {
|
|
9
|
+
...rest
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('SDK reporting storage', async () => {
|
|
15
|
+
const factory = await INITIALIZED_MONGO_REPORT_STORAGE_FACTORY();
|
|
16
|
+
const now = new Date();
|
|
17
|
+
const nowAdd5minutes = new Date(
|
|
18
|
+
now.getFullYear(),
|
|
19
|
+
now.getMonth(),
|
|
20
|
+
now.getDate(),
|
|
21
|
+
now.getHours(),
|
|
22
|
+
now.getMinutes() + 5
|
|
23
|
+
);
|
|
24
|
+
const nowLess5minutes = new Date(
|
|
25
|
+
now.getFullYear(),
|
|
26
|
+
now.getMonth(),
|
|
27
|
+
now.getDate(),
|
|
28
|
+
now.getHours(),
|
|
29
|
+
now.getMinutes() - 5
|
|
30
|
+
);
|
|
31
|
+
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
|
32
|
+
const weekAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
|
|
33
|
+
const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
|
|
34
|
+
const user_one = {
|
|
35
|
+
user_id: 'user_one',
|
|
36
|
+
client_id: 'client_one',
|
|
37
|
+
connect_at: now,
|
|
38
|
+
sdk: 'powersync-dart/1.6.4',
|
|
39
|
+
user_agent: 'powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android',
|
|
40
|
+
jwt_exp: nowAdd5minutes
|
|
41
|
+
};
|
|
42
|
+
const user_two = {
|
|
43
|
+
user_id: 'user_two',
|
|
44
|
+
client_id: 'client_two',
|
|
45
|
+
connect_at: nowLess5minutes,
|
|
46
|
+
sdk: 'powersync-js/1.21.1',
|
|
47
|
+
user_agent: 'powersync-js/1.21.0 powersync-web Chromium/138 linux',
|
|
48
|
+
jwt_exp: nowAdd5minutes
|
|
49
|
+
};
|
|
50
|
+
const user_three = {
|
|
51
|
+
user_id: 'user_three',
|
|
52
|
+
client_id: 'client_three',
|
|
53
|
+
connect_at: yesterday,
|
|
54
|
+
sdk: 'powersync-js/1.21.2',
|
|
55
|
+
user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
|
|
56
|
+
disconnect_at: yesterday
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const user_four = {
|
|
60
|
+
user_id: 'user_four',
|
|
61
|
+
client_id: 'client_four',
|
|
62
|
+
connect_at: now,
|
|
63
|
+
sdk: 'powersync-js/1.21.4',
|
|
64
|
+
user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
|
|
65
|
+
jwt_exp: nowLess5minutes
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const user_week = {
|
|
69
|
+
user_id: 'user_week',
|
|
70
|
+
client_id: 'client_week',
|
|
71
|
+
connect_at: weekAgo,
|
|
72
|
+
sdk: 'powersync-js/1.24.5',
|
|
73
|
+
user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
|
|
74
|
+
disconnect_at: weekAgo
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const user_month = {
|
|
78
|
+
user_id: 'user_month',
|
|
79
|
+
client_id: 'client_month',
|
|
80
|
+
connect_at: monthAgo,
|
|
81
|
+
sdk: 'powersync-js/1.23.6',
|
|
82
|
+
user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
|
|
83
|
+
disconnect_at: monthAgo
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const user_expired = {
|
|
87
|
+
user_id: 'user_expired',
|
|
88
|
+
client_id: 'client_expired',
|
|
89
|
+
connect_at: monthAgo,
|
|
90
|
+
sdk: 'powersync-js/1.23.7',
|
|
91
|
+
user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
|
|
92
|
+
jwt_exp: monthAgo
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
async function loadData() {
|
|
96
|
+
await factory.db.sdk_report_events.insertMany([
|
|
97
|
+
user_one,
|
|
98
|
+
user_two,
|
|
99
|
+
user_three,
|
|
100
|
+
user_four,
|
|
101
|
+
user_week,
|
|
102
|
+
user_month,
|
|
103
|
+
user_expired
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function deleteData() {
|
|
108
|
+
return factory.db.sdk_report_events.deleteMany();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
beforeAll(async () => {
|
|
112
|
+
await loadData();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
afterAll(async () => {
|
|
116
|
+
await deleteData();
|
|
117
|
+
});
|
|
118
|
+
it('Should show connected users with start range', async () => {
|
|
119
|
+
const current = await factory.listCurrentConnections({
|
|
120
|
+
range: {
|
|
121
|
+
start_date: new Date(
|
|
122
|
+
now.getFullYear(),
|
|
123
|
+
now.getMonth(),
|
|
124
|
+
now.getDate(),
|
|
125
|
+
now.getHours(),
|
|
126
|
+
now.getMinutes() - 1
|
|
127
|
+
).toISOString()
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
expect(current).toMatchSnapshot();
|
|
131
|
+
});
|
|
132
|
+
it('Should show connected users with start range and end range', async () => {
|
|
133
|
+
const current = await factory.listCurrentConnections({
|
|
134
|
+
range: {
|
|
135
|
+
end_date: nowLess5minutes.toISOString(),
|
|
136
|
+
start_date: new Date(
|
|
137
|
+
now.getFullYear(),
|
|
138
|
+
now.getMonth(),
|
|
139
|
+
now.getDate(),
|
|
140
|
+
now.getHours(),
|
|
141
|
+
now.getMinutes() - 6
|
|
142
|
+
).toISOString()
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
expect(current).toMatchSnapshot();
|
|
146
|
+
});
|
|
147
|
+
it('Should show SDK scrape data for user over the past month', async () => {
|
|
148
|
+
const sdk = await factory.scrapeSdkData({
|
|
149
|
+
interval: 1,
|
|
150
|
+
timeframe: 'month'
|
|
151
|
+
});
|
|
152
|
+
expect(sdk).toMatchSnapshot();
|
|
153
|
+
});
|
|
154
|
+
it('Should show SDK scrape data for user over the past week', async () => {
|
|
155
|
+
const sdk = await factory.scrapeSdkData({
|
|
156
|
+
interval: 1,
|
|
157
|
+
timeframe: 'week'
|
|
158
|
+
});
|
|
159
|
+
expect(sdk).toMatchSnapshot();
|
|
160
|
+
});
|
|
161
|
+
it('Should show SDK scrape data for user over the past day', async () => {
|
|
162
|
+
const sdk = await factory.scrapeSdkData({
|
|
163
|
+
interval: 1,
|
|
164
|
+
timeframe: 'day'
|
|
165
|
+
});
|
|
166
|
+
expect(sdk).toMatchSnapshot();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('Should update a sdk event if its within a day', async () => {
|
|
170
|
+
const newConnectAt = new Date(
|
|
171
|
+
now.getFullYear(),
|
|
172
|
+
now.getMonth(),
|
|
173
|
+
now.getDate(),
|
|
174
|
+
now.getHours(),
|
|
175
|
+
now.getMinutes() + 20
|
|
176
|
+
);
|
|
177
|
+
const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
|
|
178
|
+
await factory.reportSdkConnect({
|
|
179
|
+
sdk: user_one.sdk,
|
|
180
|
+
connect_at: newConnectAt,
|
|
181
|
+
jwt_exp: jwtExp,
|
|
182
|
+
client_id: user_one.client_id,
|
|
183
|
+
user_id: user_one.user_id,
|
|
184
|
+
user_agent: user_one.user_agent
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
|
|
188
|
+
expect(sdk).toHaveLength(1);
|
|
189
|
+
expect(new Date(sdk[0].connect_at)).toEqual(newConnectAt);
|
|
190
|
+
expect(new Date(sdk[0].jwt_exp!)).toEqual(jwtExp);
|
|
191
|
+
expect(sdk[0].disconnect_at).toBeNull();
|
|
192
|
+
const cleaned = removeVolatileFields(sdk);
|
|
193
|
+
expect(cleaned).toMatchSnapshot();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('Should update a connected sdk event and make it disconnected', async () => {
|
|
197
|
+
const disconnectAt = new Date(
|
|
198
|
+
now.getFullYear(),
|
|
199
|
+
now.getMonth(),
|
|
200
|
+
now.getDate(),
|
|
201
|
+
now.getHours(),
|
|
202
|
+
now.getMinutes() + 20
|
|
203
|
+
);
|
|
204
|
+
const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
|
|
205
|
+
|
|
206
|
+
await factory.reportSdkDisconnect({
|
|
207
|
+
disconnect_at: disconnectAt,
|
|
208
|
+
jwt_exp: jwtExp,
|
|
209
|
+
client_id: user_one.client_id,
|
|
210
|
+
user_id: user_one.user_id,
|
|
211
|
+
user_agent: user_one.user_agent
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
|
|
215
|
+
expect(sdk).toHaveLength(1);
|
|
216
|
+
expect(new Date(sdk[0].disconnect_at!)).toEqual(disconnectAt);
|
|
217
|
+
const cleaned = removeVolatileFields(sdk);
|
|
218
|
+
expect(cleaned).toMatchSnapshot();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('Should create a sdk event if its after a day', async () => {
|
|
222
|
+
const newConnectAt = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, now.getHours());
|
|
223
|
+
const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
|
|
224
|
+
|
|
225
|
+
await factory.reportSdkConnect({
|
|
226
|
+
sdk: user_week.sdk,
|
|
227
|
+
connect_at: newConnectAt,
|
|
228
|
+
jwt_exp: jwtExp,
|
|
229
|
+
client_id: user_week.client_id,
|
|
230
|
+
user_id: user_week.user_id,
|
|
231
|
+
user_agent: user_week.user_agent
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const sdk = await factory.db.sdk_report_events.find({ user_id: user_one.user_id }).toArray();
|
|
235
|
+
expect(sdk).toHaveLength(2);
|
|
236
|
+
const cleaned = removeVolatileFields(sdk);
|
|
237
|
+
expect(cleaned).toMatchSnapshot();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('Should delete rows older than specified range', async () => {
|
|
241
|
+
await deleteData();
|
|
242
|
+
await loadData();
|
|
243
|
+
await factory.deleteOldSdkData({
|
|
244
|
+
interval: 1,
|
|
245
|
+
timeframe: 'week'
|
|
246
|
+
});
|
|
247
|
+
const sdk = await factory.scrapeSdkData({
|
|
248
|
+
interval: 1,
|
|
249
|
+
timeframe: 'month'
|
|
250
|
+
});
|
|
251
|
+
expect(sdk).toMatchSnapshot();
|
|
252
|
+
});
|
|
253
|
+
});
|
package/test/src/util.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { env } from './env.js';
|
|
2
2
|
|
|
3
3
|
import { MongoTestStorageFactoryGenerator } from '@module/storage/implementation/MongoTestStorageFactoryGenerator.js';
|
|
4
|
+
import { MongoTestReportStorageFactoryGenerator } from '@module/storage/implementation/MongoTestReportStorageFactoryGenerator.js';
|
|
4
5
|
|
|
5
6
|
export const INITIALIZED_MONGO_STORAGE_FACTORY = MongoTestStorageFactoryGenerator({
|
|
6
7
|
url: env.MONGO_TEST_URL,
|
|
7
8
|
isCI: env.CI
|
|
8
9
|
});
|
|
10
|
+
|
|
11
|
+
export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = MongoTestReportStorageFactoryGenerator({
|
|
12
|
+
url: env.MONGO_TEST_URL,
|
|
13
|
+
isCI: env.CI
|
|
14
|
+
});
|