Skip to content

Commit 6c772b3

Browse files
authored
Harden and standardize the Message Box stack (#359)
* Harden and standardize Message Box stack * Resolve Message Box quality and coverage findings
1 parent d4c98d0 commit 6c772b3

115 files changed

Lines changed: 7597 additions & 5301 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎conformance/generated/messaging/models.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,22 @@ class MessageObject(BaseModel):
4747
None,
4848
description='Preferred plural form. Takes precedence over `recipient` when both are present.',
4949
)
50-
messageBox: str = Field(
50+
messageBox: constr(min_length=1, max_length=128) = Field(
5151
...,
5252
description='Named message box (e.g. `payment_inbox`, `notifications`).',
5353
examples=['payment_inbox'],
5454
)
55-
messageId: str | list[str] = Field(
55+
messageId: (
56+
constr(min_length=1, max_length=256)
57+
| list[constr(min_length=1, max_length=256)]
58+
) = Field(
5659
...,
5760
description='Unique identifier(s) for the message(s) — typically an HMAC. When\n`recipients` has more than one entry, `messageId` must be an array\nof the same length with one ID per recipient.\n',
5861
examples=['xyz123'],
5962
)
60-
body: str | dict[str, Any] = Field(
61-
..., description='Message payload. Strings and JSON objects are both accepted.'
63+
body: str = Field(
64+
...,
65+
description='Serialized message payload. The official client serializes object\nbodies before sending them.\n',
6266
)
6367

6468

‎conformance/generated/messaging/types.gen.d.ts‎

Lines changed: 135 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@
44
*/
55

66
export interface paths {
7+
"/health": {
8+
parameters: {
9+
query?: never;
10+
header?: never;
11+
path?: never;
12+
cookie?: never;
13+
};
14+
/** Report process liveness */
15+
get: operations["health"];
16+
put?: never;
17+
post?: never;
18+
delete?: never;
19+
options?: never;
20+
head?: never;
21+
patch?: never;
22+
trace?: never;
23+
};
24+
"/ready": {
25+
parameters: {
26+
query?: never;
27+
header?: never;
28+
path?: never;
29+
cookie?: never;
30+
};
31+
/** Report database readiness */
32+
get: operations["readiness"];
33+
put?: never;
34+
post?: never;
35+
delete?: never;
36+
options?: never;
37+
head?: never;
38+
patch?: never;
39+
trace?: never;
40+
};
741
"/sendMessage": {
842
parameters: {
943
query?: never;
@@ -55,9 +89,10 @@ export interface paths {
5589
put?: never;
5690
/**
5791
* Retrieve messages from a named message box
58-
* @description Returns all stored messages belonging to the authenticated identity in the
59-
* specified message box. Returns an empty array if the box does not exist.
60-
* Message bodies are always returned as strings (JSON objects are stringified).
92+
* @description Returns one deterministic, bounded page of stored messages belonging
93+
* to the authenticated identity in the specified message box. Returns an
94+
* empty array if the box does not exist. Message bodies are always
95+
* returned as strings (JSON objects are stringified).
6196
*/
6297
post: operations["listMessages"];
6398
delete?: never;
@@ -99,10 +134,10 @@ export interface paths {
99134
put?: never;
100135
/**
101136
* Register a device for FCM push notifications
102-
* @description Stores or updates an FCM token for the authenticated identity. If the
103-
* token already exists it is updated (identity key, device ID, platform,
104-
* last_used timestamp). The FCM token is unique-indexed; upsert semantics
105-
* are applied via ON CONFLICT MERGE.
137+
* @description Stores or refreshes an FCM token for the authenticated identity. A
138+
* token already owned by another identity cannot be reassigned through
139+
* this route. Re-registering a token for the same identity updates its
140+
* device metadata and last-used timestamp.
106141
*/
107142
post: operations["registerDevice"];
108143
delete?: never;
@@ -120,7 +155,7 @@ export interface paths {
120155
};
121156
/**
122157
* List registered devices for the authenticated identity
123-
* @description Returns all device registrations for the authenticated user, sorted by
158+
* @description Returns one bounded page of device registrations for the authenticated user, sorted by
124159
* `updated_at` descending. FCM tokens are truncated for security (last 10
125160
* characters prefixed with "...").
126161
*/
@@ -170,8 +205,7 @@ export interface paths {
170205
* Get the message permission for a sender/box combination
171206
* @description Retrieves the permission record for a specific sender + message box pair
172207
* owned by the authenticated user. If `sender` is omitted, returns the
173-
* box-wide default. Returns `permission: undefined` (null in JSON) when no
174-
* record is set.
208+
* box-wide default. Returns `permission: null` when no record is set.
175209
*/
176210
get: operations["getPermission"];
177211
put?: never;
@@ -190,12 +224,12 @@ export interface paths {
190224
cookie?: never;
191225
};
192226
/**
193-
* List all message permissions for the authenticated user
194-
* @description Returns all permission records owned by the authenticated identity with
195-
* optional filtering and pagination. Results are ordered by
196-
* `(message_box ASC, sender ASC NULLS FIRST, created_at DESC/ASC)`.
197-
* Box-wide defaults (sender = null) appear before sender-specific entries
198-
* within each message box.
227+
* List message permissions for the authenticated user
228+
* @description Returns one bounded page of permission records owned by the
229+
* authenticated identity, with optional filtering. Results are ordered by
230+
* `(message_box ASC, normalized sender scope ASC, created_at DESC/ASC)`.
231+
* Box-wide defaults (sender = null) use the empty normalized scope and
232+
* therefore appear before sender-specific entries within each message box.
199233
*/
200234
get: operations["listPermissions"];
201235
put?: never;
@@ -325,10 +359,11 @@ export interface components {
325359
* @example xyz123
326360
*/
327361
messageId: string | string[];
328-
/** @description Message payload. Strings and JSON objects are both accepted. */
329-
body: string | {
330-
[key: string]: unknown;
331-
};
362+
/**
363+
* @description Serialized message payload. The official client serializes object
364+
* bodies before sending them.
365+
*/
366+
body: string;
332367
};
333368
/** @description A single output inside a Payment describing how to route satoshis. */
334369
PaymentOutput: {
@@ -429,6 +464,63 @@ export interface components {
429464
}
430465
export type $defs = Record<string, never>;
431466
export interface operations {
467+
health: {
468+
parameters: {
469+
query?: never;
470+
header?: never;
471+
path?: never;
472+
cookie?: never;
473+
};
474+
requestBody?: never;
475+
responses: {
476+
/** @description Process is alive. */
477+
200: {
478+
headers: {
479+
/** @description Always `no-store`. */
480+
"Cache-Control"?: string;
481+
[name: string]: unknown;
482+
};
483+
content: {
484+
"application/json": {
485+
/** @enum {string} */
486+
status: "ok";
487+
};
488+
};
489+
};
490+
};
491+
};
492+
readiness: {
493+
parameters: {
494+
query?: never;
495+
header?: never;
496+
path?: never;
497+
cookie?: never;
498+
};
499+
requestBody?: never;
500+
responses: {
501+
/** @description Required dependencies are ready. */
502+
200: {
503+
headers: {
504+
[name: string]: unknown;
505+
};
506+
content: {
507+
"application/json": {
508+
/** @enum {string} */
509+
status: "ready";
510+
};
511+
};
512+
};
513+
/** @description A required dependency is unavailable. */
514+
503: {
515+
headers: {
516+
[name: string]: unknown;
517+
};
518+
content: {
519+
"application/json": components["schemas"]["ErrorResponse"];
520+
};
521+
};
522+
};
523+
};
432524
sendMessage: {
433525
parameters: {
434526
query?: never;
@@ -537,6 +629,10 @@ export interface operations {
537629
* @example payment_inbox
538630
*/
539631
messageBox: string;
632+
/** @default 1000 */
633+
limit?: number;
634+
/** @default 0 */
635+
offset?: number;
540636
};
541637
};
542638
};
@@ -551,6 +647,9 @@ export interface operations {
551647
/** @enum {string} */
552648
status: "success";
553649
messages: components["schemas"]["StoredMessage"][];
650+
limit: number;
651+
offset: number;
652+
hasMore: boolean;
554653
};
555654
};
556655
};
@@ -712,6 +811,15 @@ export interface operations {
712811
"application/json": components["schemas"]["ErrorResponse"];
713812
};
714813
};
814+
/** @description Device token is already registered to another identity. */
815+
409: {
816+
headers: {
817+
[name: string]: unknown;
818+
};
819+
content: {
820+
"application/json": components["schemas"]["ErrorResponse"];
821+
};
822+
};
715823
/** @description Internal server error (`ERR_DATABASE_ERROR`, `ERR_INTERNAL`). */
716824
500: {
717825
headers: {
@@ -725,7 +833,10 @@ export interface operations {
725833
};
726834
listDevices: {
727835
parameters: {
728-
query?: never;
836+
query?: {
837+
limit?: number;
838+
offset?: number;
839+
};
729840
header?: never;
730841
path?: never;
731842
cookie?: never;
@@ -741,6 +852,8 @@ export interface operations {
741852
"application/json": {
742853
/** @enum {string} */
743854
status: "success";
855+
limit: number;
856+
offset: number;
744857
devices: components["schemas"]["RegisteredDevice"][];
745858
};
746859
};
@@ -856,7 +969,7 @@ export interface operations {
856969
/** @enum {string} */
857970
status: "success";
858971
description: string;
859-
/** @description The permission record, or null/undefined if not set. */
972+
/** @description The permission record, or null if not set. */
860973
permission?: components["schemas"]["PermissionRecord"] | null;
861974
};
862975
};

0 commit comments

Comments
 (0)