@remit/backend 0.0.93 → 0.0.95
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/package.json
CHANGED
package/src/handlers/config.ts
CHANGED
|
@@ -14,6 +14,10 @@ import type { AccountConfigItem, MailboxItem } from "@remit/data-ports";
|
|
|
14
14
|
import { ConfigNotEmptyError, NotFoundError } from "@remit/data-ports/errors";
|
|
15
15
|
import type { CanonicalMailboxRoleValue } from "@remit/data-ports/folder-role";
|
|
16
16
|
import { logger } from "@remit/logger-lambda";
|
|
17
|
+
import {
|
|
18
|
+
EMBEDDING_PROVIDER_OFF,
|
|
19
|
+
readEmbeddingProviderFromEnv,
|
|
20
|
+
} from "@remit/search-service/from-env";
|
|
17
21
|
import type { APIGatewayProxyEvent } from "aws-lambda";
|
|
18
22
|
import { env } from "expect-env";
|
|
19
23
|
import type { Context } from "openapi-backend";
|
|
@@ -138,9 +142,22 @@ const emptyConfigResponse = (
|
|
|
138
142
|
updatedAt: now,
|
|
139
143
|
},
|
|
140
144
|
accounts: [],
|
|
145
|
+
semanticSearchEnabled: semanticSearchEnabled(),
|
|
141
146
|
};
|
|
142
147
|
};
|
|
143
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Whether this instance embeds anything, read from the same
|
|
151
|
+
* `SEARCH_EMBEDDING_PROVIDER` the search-index worker and the `remit` wrapper
|
|
152
|
+
* read. It rides GET /config because the semantic surfaces need it before they
|
|
153
|
+
* have a query to send: an off instance stores no vectors, so the Organize
|
|
154
|
+
* widen and semantic filters have nothing to read, and a client that learns
|
|
155
|
+
* that only from an empty result cannot tell it from a mailbox with nothing
|
|
156
|
+
* similar in it (#1068).
|
|
157
|
+
*/
|
|
158
|
+
const semanticSearchEnabled = (): boolean =>
|
|
159
|
+
readEmbeddingProviderFromEnv() !== EMBEDDING_PROVIDER_OFF;
|
|
160
|
+
|
|
144
161
|
export const ConfigOperations: Record<
|
|
145
162
|
ConfigOperationIds,
|
|
146
163
|
OperationHandler<ConfigOperationIds>
|
|
@@ -220,6 +237,7 @@ export const ConfigOperations: Record<
|
|
|
220
237
|
|
|
221
238
|
return {
|
|
222
239
|
accountConfig: toAccountConfigResponse(accountConfig),
|
|
240
|
+
semanticSearchEnabled: semanticSearchEnabled(),
|
|
223
241
|
...(pendingImport ? { pendingImport } : {}),
|
|
224
242
|
accounts: activeAccounts.map((acc) =>
|
|
225
243
|
toAccountResponse(
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { after, afterEach, before, beforeEach, describe, it } from "node:test";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
import type { APIGatewayProxyEvent } from "aws-lambda";
|
|
13
|
+
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
|
|
14
14
|
import type { Context } from "openapi-backend";
|
|
15
|
+
import { normalizeRequest } from "../request.js";
|
|
15
16
|
import { SystemOperations } from "./system-update.js";
|
|
16
17
|
|
|
17
18
|
const tmpRoot = join(
|
|
@@ -415,3 +416,35 @@ describe("POST /system/update", () => {
|
|
|
415
416
|
assert.ok(stamped >= before - 1000 && stamped <= after + 1000);
|
|
416
417
|
});
|
|
417
418
|
});
|
|
419
|
+
|
|
420
|
+
describe("GET /system/update through the whole request pipeline", () => {
|
|
421
|
+
it("records the check for ?refresh=true off the wire", async () => {
|
|
422
|
+
// The rest of this file hands the handler a query the validator has already
|
|
423
|
+
// coerced. Here the query arrives as API Gateway delivers it — every value a
|
|
424
|
+
// string — and goes through the real built spec, which is where the press
|
|
425
|
+
// was being answered with 400 instead of reaching the handler at all.
|
|
426
|
+
writeState(okState);
|
|
427
|
+
const { api } = await import("../index.js");
|
|
428
|
+
|
|
429
|
+
const event = {
|
|
430
|
+
httpMethod: "GET",
|
|
431
|
+
path: "/system/update",
|
|
432
|
+
queryStringParameters: { refresh: "true" },
|
|
433
|
+
headers: {},
|
|
434
|
+
requestContext: { authorizer: { claims: { sub: USER } } },
|
|
435
|
+
} as unknown as APIGatewayProxyEvent;
|
|
436
|
+
|
|
437
|
+
const result = (await api.handleRequest(
|
|
438
|
+
normalizeRequest(event),
|
|
439
|
+
event,
|
|
440
|
+
{} as never,
|
|
441
|
+
)) as APIGatewayProxyResult;
|
|
442
|
+
|
|
443
|
+
assert.equal(result.statusCode, 200);
|
|
444
|
+
assert.deepEqual(
|
|
445
|
+
JSON.parse(readFileSync(join(controlDir, "check-request.json"), "utf8")),
|
|
446
|
+
{},
|
|
447
|
+
);
|
|
448
|
+
assert.deepEqual(JSON.parse(result.body), okState);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { OpenAPISpec } from "./index.js";
|
|
4
|
+
|
|
5
|
+
type Parameter = {
|
|
6
|
+
name: string;
|
|
7
|
+
in: string;
|
|
8
|
+
explode?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const queryParameters = (): Array<{
|
|
12
|
+
operationId: string;
|
|
13
|
+
param: Parameter;
|
|
14
|
+
}> => {
|
|
15
|
+
const found: Array<{ operationId: string; param: Parameter }> = [];
|
|
16
|
+
for (const item of Object.values(OpenAPISpec.paths ?? {})) {
|
|
17
|
+
for (const operation of Object.values(item ?? {})) {
|
|
18
|
+
const { operationId, parameters } = operation as {
|
|
19
|
+
operationId?: string;
|
|
20
|
+
parameters?: Parameter[];
|
|
21
|
+
};
|
|
22
|
+
if (!operationId || !parameters) continue;
|
|
23
|
+
for (const param of parameters) {
|
|
24
|
+
if (param.in === "query") found.push({ operationId, param });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return found;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe("the built OpenAPI spec", () => {
|
|
32
|
+
it("declares every query parameter exploded", () => {
|
|
33
|
+
// openapi-backend splits a non-exploded query value on commas before it
|
|
34
|
+
// validates, so `?refresh=true` arrives at the validator as `["true"]` and
|
|
35
|
+
// is refused as "must be boolean" — the handler never runs. Exploded is the
|
|
36
|
+
// wire-identical form for a scalar, so every `@query` in main.tsp carries
|
|
37
|
+
// `#{ explode: true }`.
|
|
38
|
+
const offenders = queryParameters()
|
|
39
|
+
.filter(({ param }) => param.explode === false)
|
|
40
|
+
.map(({ operationId, param }) => `${operationId}.${param.name}`);
|
|
41
|
+
|
|
42
|
+
assert.deepEqual(offenders, []);
|
|
43
|
+
});
|
|
44
|
+
});
|