@carbonorm/carbonnode 6.0.14 → 6.0.18
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/dist/executors/SqlExecutor.d.ts +17 -0
- package/dist/index.cjs.js +450 -248
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +450 -249
- package/dist/index.esm.js.map +1 -1
- package/dist/types/ormInterfaces.d.ts +1 -0
- package/dist/utils/cacheManager.d.ts +3 -1
- package/dist/utils/logLevel.d.ts +3 -3
- package/dist/utils/logSql.d.ts +10 -1
- package/package.json +2 -2
- package/scripts/assets/handlebars/C6.ts.handlebars +1 -1
- package/src/__tests__/fixtures/sqlResponses/sqlAllowList.json +1 -1
- package/src/__tests__/httpExecutor.cacheEviction.test.ts +70 -0
- package/src/__tests__/logSql.test.ts +54 -2
- package/src/__tests__/sakila-db/C6.js +1 -1
- package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
- package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
- package/src/__tests__/sakila-db/C6.sqlAllowList.json +59 -70
- package/src/__tests__/sakila-db/C6.ts +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.join.json +10 -10
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
- package/src/__tests__/sqlAllowList.test.ts +100 -0
- package/src/__tests__/sqlBuilders.test.ts +3 -4
- package/src/__tests__/sqlExecutor.cacheEviction.test.ts +79 -0
- package/src/executors/HttpExecutor.ts +20 -4
- package/src/executors/SqlExecutor.ts +131 -12
- package/src/orm/queries/DeleteQueryBuilder.ts +0 -4
- package/src/orm/queries/PostQueryBuilder.ts +0 -4
- package/src/orm/queries/SelectQueryBuilder.ts +0 -4
- package/src/orm/queries/UpdateQueryBuilder.ts +0 -4
- package/src/types/ormInterfaces.ts +4 -1
- package/src/utils/cacheManager.ts +26 -9
- package/src/utils/logLevel.ts +3 -4
- package/src/utils/logSql.ts +51 -6
- package/src/utils/sqlAllowList.ts +111 -9
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"rest": [
|
|
3
3
|
{
|
|
4
|
-
"rental_id":
|
|
5
|
-
"rental_date": "2005-05-
|
|
6
|
-
"inventory_id":
|
|
7
|
-
"customer_id":
|
|
8
|
-
"return_date": "2005-
|
|
9
|
-
"staff_id":
|
|
4
|
+
"rental_id": 1,
|
|
5
|
+
"rental_date": "2005-05-24T22:53:30.000Z",
|
|
6
|
+
"inventory_id": 367,
|
|
7
|
+
"customer_id": 130,
|
|
8
|
+
"return_date": "2005-05-26T22:04:30.000Z",
|
|
9
|
+
"staff_id": 1,
|
|
10
10
|
"last_update": "2006-02-15T04:57:20.000Z",
|
|
11
11
|
"store_id": 1,
|
|
12
|
-
"first_name": "
|
|
13
|
-
"last_name": "
|
|
14
|
-
"email": "
|
|
15
|
-
"address_id":
|
|
12
|
+
"first_name": "CHARLOTTE",
|
|
13
|
+
"last_name": "HUNTER",
|
|
14
|
+
"email": "CHARLOTTE.HUNTER@sakilacustomer.org",
|
|
15
|
+
"address_id": 134,
|
|
16
16
|
"active": 1,
|
|
17
17
|
"create_date": "2006-02-14T22:04:36.000Z"
|
|
18
18
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
"sql": {
|
|
6
6
|
"sql": "INSERT INTO `rental` (\n `rental_date`, `inventory_id`, `customer_id`, `return_date`, `staff_id`, `last_update`\n ) VALUES\n (?, ?, ?, ?, ?, ?)",
|
|
7
7
|
"values": [
|
|
8
|
-
"2026-02-
|
|
8
|
+
"2026-02-12 22:07:32",
|
|
9
9
|
1,
|
|
10
10
|
1,
|
|
11
|
-
"2026-02-
|
|
11
|
+
"2026-02-12 22:07:32",
|
|
12
12
|
1,
|
|
13
|
-
"2026-02-
|
|
13
|
+
"2026-02-12 22:07:32"
|
|
14
14
|
]
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"rest": [
|
|
3
3
|
{
|
|
4
4
|
"rental_id": 16050,
|
|
5
|
-
"rental_date": "2026-02-
|
|
5
|
+
"rental_date": "2026-02-12T22:07:32.000Z",
|
|
6
6
|
"inventory_id": 1,
|
|
7
7
|
"customer_id": 1,
|
|
8
|
-
"return_date": "2026-02-
|
|
8
|
+
"return_date": "2026-02-12T22:07:32.000Z",
|
|
9
9
|
"staff_id": 1,
|
|
10
|
-
"last_update": "2026-02-
|
|
10
|
+
"last_update": "2026-02-12T22:07:32.000Z"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"sql": {
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"rest": [
|
|
3
3
|
{
|
|
4
4
|
"rental_id": 16050,
|
|
5
|
-
"rental_date": "2026-02-
|
|
5
|
+
"rental_date": "2026-02-12T22:07:32.000Z",
|
|
6
6
|
"inventory_id": 1,
|
|
7
7
|
"customer_id": 1,
|
|
8
|
-
"return_date": "2026-02-
|
|
8
|
+
"return_date": "2026-02-12T22:07:32.000Z",
|
|
9
9
|
"staff_id": 1,
|
|
10
|
-
"last_update": "2026-02-
|
|
10
|
+
"last_update": "2026-02-12T22:07:32.000Z"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"sql": {
|
|
@@ -119,10 +119,110 @@ describe("SQL allowlist", () => {
|
|
|
119
119
|
await expect(
|
|
120
120
|
Actor.Get({
|
|
121
121
|
[C6.PAGINATION]: {[C6.LIMIT]: 1},
|
|
122
|
+
cacheResults: false,
|
|
122
123
|
} as any)
|
|
123
124
|
).rejects.toThrow("SQL statement is not permitted");
|
|
124
125
|
} finally {
|
|
125
126
|
restoreGlobals(originalGlobals);
|
|
126
127
|
}
|
|
127
128
|
});
|
|
129
|
+
|
|
130
|
+
it("normalizes multi-row VALUES with variable row counts", () => {
|
|
131
|
+
const oneRow = `
|
|
132
|
+
INSERT INTO \`valuation_report_comparables\` (\`report_id\`, \`unit_id\`, \`subject_unit_id\`)
|
|
133
|
+
VALUES (?, ?, ?)
|
|
134
|
+
ON DUPLICATE KEY UPDATE \`subject_unit_id\` = VALUES(\`subject_unit_id\`)
|
|
135
|
+
`;
|
|
136
|
+
const manyRows = `
|
|
137
|
+
INSERT INTO \`valuation_report_comparables\` (\`report_id\`, \`unit_id\`, \`subject_unit_id\`)
|
|
138
|
+
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?)
|
|
139
|
+
ON DUPLICATE KEY UPDATE \`subject_unit_id\` = VALUES(\`subject_unit_id\`)
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
expect(normalizeSql(oneRow)).toContain("VALUES (? ×3) ×*");
|
|
143
|
+
expect(normalizeSql(manyRows)).toContain("VALUES (? ×3) ×*");
|
|
144
|
+
expect(normalizeSql(oneRow)).toBe(normalizeSql(manyRows));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("normalizes IN bind list cardinality", () => {
|
|
148
|
+
const smallIn = "SELECT * FROM `geometries` WHERE ( geometries.geometry_id IN (?, ?, ?) ) LIMIT 100";
|
|
149
|
+
const largeIn = "SELECT * FROM `geometries` WHERE ( geometries.geometry_id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) LIMIT 250";
|
|
150
|
+
|
|
151
|
+
const normalizedSmall = normalizeSql(smallIn);
|
|
152
|
+
const normalizedLarge = normalizeSql(largeIn);
|
|
153
|
+
|
|
154
|
+
expect(normalizedSmall).toContain("IN (? ×*)");
|
|
155
|
+
expect(normalizedLarge).toContain("IN (? ×*)");
|
|
156
|
+
expect(normalizedSmall).toContain("LIMIT ?");
|
|
157
|
+
expect(normalizedLarge).toContain("LIMIT ?");
|
|
158
|
+
expect(normalizedSmall).toBe(normalizedLarge);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("normalizes LIMIT and OFFSET numeric literals", () => {
|
|
162
|
+
expect(normalizeSql("SELECT * FROM `actor` LIMIT 100")).toBe(
|
|
163
|
+
normalizeSql("SELECT * FROM `actor` LIMIT 25"),
|
|
164
|
+
);
|
|
165
|
+
expect(normalizeSql("SELECT * FROM `actor` LIMIT 10, 50")).toBe(
|
|
166
|
+
"SELECT * FROM `actor` LIMIT ?, ?",
|
|
167
|
+
);
|
|
168
|
+
expect(normalizeSql("SELECT * FROM `actor` LIMIT 50 OFFSET 100")).toBe(
|
|
169
|
+
"SELECT * FROM `actor` LIMIT ? OFFSET ?",
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("normalizes variable ST_GEOMFROMTEXT POINT and POLYGON literals", () => {
|
|
174
|
+
const q1 = `
|
|
175
|
+
SELECT * FROM \`property_units\`
|
|
176
|
+
WHERE MBRCONTAINS(
|
|
177
|
+
ST_GEOMFROMTEXT('POLYGON((39.1 -105.1, 39.2 -105.1, 39.2 -105.0, 39.1 -105.0, 39.1 -105.1))', 4326),
|
|
178
|
+
property_units.location
|
|
179
|
+
)
|
|
180
|
+
ORDER BY ST_DISTANCE_SPHERE(
|
|
181
|
+
property_units.location,
|
|
182
|
+
ST_GEOMFROMTEXT('POINT(39.15 -105.05)', 4326)
|
|
183
|
+
)
|
|
184
|
+
LIMIT 100
|
|
185
|
+
`;
|
|
186
|
+
const q2 = `
|
|
187
|
+
SELECT * FROM \`property_units\`
|
|
188
|
+
WHERE MBRCONTAINS(
|
|
189
|
+
ST_GEOMFROMTEXT('POLYGON((39.3 -105.3, 39.7 -105.3, 39.7 -104.8, 39.3 -104.8, 39.3 -105.3))', 4326),
|
|
190
|
+
property_units.location
|
|
191
|
+
)
|
|
192
|
+
ORDER BY ST_DISTANCE_SPHERE(
|
|
193
|
+
property_units.location,
|
|
194
|
+
ST_GEOMFROMTEXT('POINT(39.5321821 -105.0035613)', 4326)
|
|
195
|
+
)
|
|
196
|
+
LIMIT 250
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
const normalized1 = normalizeSql(q1);
|
|
200
|
+
const normalized2 = normalizeSql(q2);
|
|
201
|
+
|
|
202
|
+
expect(normalized1).toContain("ST_GEOMFROMTEXT('POLYGON((?))', ?)");
|
|
203
|
+
expect(normalized1).toContain("ST_GEOMFROMTEXT('POINT(? ?)', ?)");
|
|
204
|
+
expect(normalized1).toContain("ST_DISTANCE_SPHERE(");
|
|
205
|
+
expect(normalized1).toContain("LIMIT ?");
|
|
206
|
+
expect(normalized1).toBe(normalized2);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("normalizes geo function casing and FORCE INDEX spacing", () => {
|
|
210
|
+
const a =
|
|
211
|
+
"SELECT * FROM `property_units` FORCE INDEX (`idx_county_id`,`idx_property_units_location`) WHERE ST_Distance_Sphere(property_units.location, ST_GeomFromText('POINT(39.5 -105.0)', 4326)) <= 50 LIMIT 100";
|
|
212
|
+
const b =
|
|
213
|
+
"SELECT * FROM `property_units` FORCE INDEX (`idx_county_id`, `idx_property_units_location`) WHERE st_distance_sphere(property_units.location, st_geomfromtext('POINT(39.7 -104.9)', 4326)) <= 25 LIMIT 25";
|
|
214
|
+
|
|
215
|
+
const normalizedA = normalizeSql(a);
|
|
216
|
+
const normalizedB = normalizeSql(b);
|
|
217
|
+
|
|
218
|
+
expect(normalizedA).toContain("FORCE INDEX (`idx_county_id`, `idx_property_units_location`)");
|
|
219
|
+
expect(normalizedA).toContain("ST_DISTANCE_SPHERE");
|
|
220
|
+
expect(normalizedA).toContain("ST_GEOMFROMTEXT('POINT(? ?)', ?)");
|
|
221
|
+
expect(normalizedA).toContain("LIMIT ?");
|
|
222
|
+
expect(normalizedB).toContain("FORCE INDEX (`idx_county_id`, `idx_property_units_location`)");
|
|
223
|
+
expect(normalizedB).toContain("ST_DISTANCE_SPHERE");
|
|
224
|
+
expect(normalizedB).toContain("ST_GEOMFROMTEXT('POINT(? ?)', ?)");
|
|
225
|
+
expect(normalizedB).toContain("LIMIT ?");
|
|
226
|
+
});
|
|
227
|
+
|
|
128
228
|
});
|
|
@@ -5,7 +5,6 @@ import { PostQueryBuilder } from '../orm/queries/PostQueryBuilder';
|
|
|
5
5
|
import { UpdateQueryBuilder } from '../orm/queries/UpdateQueryBuilder';
|
|
6
6
|
import { DeleteQueryBuilder } from '../orm/queries/DeleteQueryBuilder';
|
|
7
7
|
import { buildTestConfig, buildBinaryTestConfig, buildBinaryTestConfigFqn } from './fixtures/c6.fixture';
|
|
8
|
-
import { version } from '../../package.json';
|
|
9
8
|
|
|
10
9
|
describe('SQL Builders', () => {
|
|
11
10
|
it('builds SELECT with JOIN, WHERE, GROUP BY, HAVING and default LIMIT', () => {
|
|
@@ -44,11 +43,11 @@ describe('SQL Builders', () => {
|
|
|
44
43
|
expect(params).toEqual(['%A%', 10, 1]);
|
|
45
44
|
});
|
|
46
45
|
|
|
47
|
-
it('logs SELECT
|
|
46
|
+
it('logs SELECT aggregate expressions at DEBUG level', () => {
|
|
48
47
|
const config = buildTestConfig();
|
|
49
48
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
50
49
|
const qb = new SelectQueryBuilder(config as any, {
|
|
51
|
-
SELECT: ['actor.
|
|
50
|
+
SELECT: [[C6C.COUNT, 'actor.actor_id', C6C.AS, 'cnt']],
|
|
52
51
|
} as any, false);
|
|
53
52
|
|
|
54
53
|
qb.build('actor');
|
|
@@ -59,7 +58,7 @@ describe('SQL Builders', () => {
|
|
|
59
58
|
const selectLine = logLines.find((line) => line.includes('[SELECT]'));
|
|
60
59
|
|
|
61
60
|
expect(selectLine).toBeDefined();
|
|
62
|
-
expect(selectLine).toContain(
|
|
61
|
+
expect(selectLine).toContain('COUNT(actor.actor_id) AS cnt');
|
|
63
62
|
logSpy.mockRestore();
|
|
64
63
|
});
|
|
65
64
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { restOrm } from "../api/restOrm";
|
|
3
|
+
import { apiRequestCache, clearCache } from "../utils/cacheManager";
|
|
4
|
+
import { buildTestConfig } from "./fixtures/c6.fixture";
|
|
5
|
+
|
|
6
|
+
describe("SqlExecutor cache eviction", () => {
|
|
7
|
+
const rows = [
|
|
8
|
+
{
|
|
9
|
+
actor_id: 1,
|
|
10
|
+
first_name: "ALICE",
|
|
11
|
+
last_name: "ONE",
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const buildOrm = () => {
|
|
16
|
+
const conn: any = {
|
|
17
|
+
beginTransaction: vi.fn(async () => undefined),
|
|
18
|
+
query: vi.fn(async () => [rows, []]),
|
|
19
|
+
commit: vi.fn(async () => undefined),
|
|
20
|
+
rollback: vi.fn(async () => undefined),
|
|
21
|
+
release: vi.fn(),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const baseConfig = buildTestConfig() as any;
|
|
25
|
+
|
|
26
|
+
const actorSql = restOrm<any>(() => ({
|
|
27
|
+
...baseConfig,
|
|
28
|
+
mysqlPool: {
|
|
29
|
+
getConnection: vi.fn(async () => conn),
|
|
30
|
+
},
|
|
31
|
+
verbose: false,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
actorSql,
|
|
36
|
+
conn,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
clearCache({ ignoreWarning: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("adds evictFromCache for cached GET responses", async () => {
|
|
45
|
+
const { actorSql, conn } = buildOrm();
|
|
46
|
+
|
|
47
|
+
const response = await actorSql.Get({ actor_id: 1, cacheResults: true } as any);
|
|
48
|
+
|
|
49
|
+
expect(conn.query).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(typeof response.evictFromCache).toBe("function");
|
|
51
|
+
expect(apiRequestCache.size).toBe(1);
|
|
52
|
+
|
|
53
|
+
expect(response.evictFromCache?.()).toBe(true);
|
|
54
|
+
expect(apiRequestCache.size).toBe(0);
|
|
55
|
+
expect(response.evictFromCache?.()).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("does not add evictFromCache when cacheResults is false", async () => {
|
|
59
|
+
const { actorSql, conn } = buildOrm();
|
|
60
|
+
|
|
61
|
+
const response = await actorSql.Get({ actor_id: 1, cacheResults: false } as any);
|
|
62
|
+
|
|
63
|
+
expect(conn.query).toHaveBeenCalledTimes(1);
|
|
64
|
+
expect(response.evictFromCache).toBeUndefined();
|
|
65
|
+
expect(apiRequestCache.size).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("keeps evictFromCache on cache hits", async () => {
|
|
69
|
+
const { actorSql, conn } = buildOrm();
|
|
70
|
+
|
|
71
|
+
await actorSql.Get({ actor_id: 1, cacheResults: true } as any);
|
|
72
|
+
const cached = await actorSql.Get({ actor_id: 1, cacheResults: true } as any);
|
|
73
|
+
|
|
74
|
+
expect(conn.query).toHaveBeenCalledTimes(1);
|
|
75
|
+
expect(typeof cached.evictFromCache).toBe("function");
|
|
76
|
+
expect(cached.evictFromCache?.()).toBe(true);
|
|
77
|
+
expect(apiRequestCache.size).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
PUT, RequestQueryBody
|
|
14
14
|
} from "../types/ormInterfaces";
|
|
15
15
|
import {removeInvalidKeys, removePrefixIfExists, TestRestfulResponse} from "../utils/apiHelpers";
|
|
16
|
-
import {checkCache, setCache, userCustomClearCache} from "../utils/cacheManager";
|
|
16
|
+
import {checkCache, evictCacheEntry, setCache, userCustomClearCache} from "../utils/cacheManager";
|
|
17
17
|
import {sortAndSerializeQueryObject} from "../utils/sortAndSerializeQueryObject";
|
|
18
18
|
import {notifyToast} from "../utils/toastRuntime";
|
|
19
19
|
import {Executor} from "./Executor";
|
|
@@ -254,17 +254,29 @@ export class HttpExecutor<
|
|
|
254
254
|
G['RequestTableOverrides']
|
|
255
255
|
>;
|
|
256
256
|
|
|
257
|
+
const evictFromCache =
|
|
258
|
+
requestMethod === GET && cacheResults
|
|
259
|
+
? () => evictCacheEntry(requestMethod, tableName, cacheRequestData)
|
|
260
|
+
: undefined;
|
|
261
|
+
|
|
257
262
|
// literally impossible for query to be undefined or null here but the editor is too busy licking windows to understand that
|
|
258
263
|
let querySerialized: string = sortAndSerializeQueryObject(tables, cacheRequestData ?? {});
|
|
259
264
|
|
|
260
265
|
let cachedRequest: Promise<{ data: ResponseDataType }> | false = false;
|
|
261
266
|
|
|
262
267
|
if (cacheResults) {
|
|
263
|
-
cachedRequest = checkCache<ResponseDataType>(requestMethod, tableName, cacheRequestData);
|
|
268
|
+
cachedRequest = checkCache<ResponseDataType>(requestMethod, tableName, cacheRequestData, logContext);
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
if (cachedRequest) {
|
|
267
|
-
|
|
272
|
+
const cachedData = (await cachedRequest).data;
|
|
273
|
+
if (evictFromCache
|
|
274
|
+
&& cachedData
|
|
275
|
+
&& typeof cachedData === "object"
|
|
276
|
+
&& Array.isArray((cachedData as C6RestResponse<'GET', G['RestTableInterface']>).rest)) {
|
|
277
|
+
(cachedData as C6RestResponse<'GET', G['RestTableInterface']>).evictFromCache = evictFromCache;
|
|
278
|
+
}
|
|
279
|
+
return cachedData;
|
|
268
280
|
}
|
|
269
281
|
|
|
270
282
|
if (cacheResults) {
|
|
@@ -544,7 +556,7 @@ export class HttpExecutor<
|
|
|
544
556
|
callback();
|
|
545
557
|
}
|
|
546
558
|
|
|
547
|
-
if (
|
|
559
|
+
if (requestMethod === GET && this.isRestResponse(response)) {
|
|
548
560
|
|
|
549
561
|
const responseData =
|
|
550
562
|
response.data as DetermineResponseDataType<'GET', G['RestTableInterface']>;
|
|
@@ -562,6 +574,10 @@ export class HttpExecutor<
|
|
|
562
574
|
responseData.next = undefined; // short page => done
|
|
563
575
|
}
|
|
564
576
|
|
|
577
|
+
if (cachingConfirmed && evictFromCache) {
|
|
578
|
+
responseData.evictFromCache = evictFromCache;
|
|
579
|
+
}
|
|
580
|
+
|
|
565
581
|
if (cachingConfirmed) {
|
|
566
582
|
setCache<ResponseDataType>(requestMethod, tableName, cacheRequestData, {
|
|
567
583
|
requestArgumentsSerialized: querySerialized,
|