Skip to content

Commit 8a11dc7

Browse files
committed
CollectorInvite.patientUsername: read from accessInfo (topology-agnostic)
The previous getter parsed `apiEndpoint` URL path for the username, which only works on dnsLess=true platforms (path-based). On dnsLess=false hosts (`{username}.host/`), the path is empty and the getter returned null — breaking any caller relying on it (notably doctor-dashboard EmbedInvite, which fell through to a "Only pendings can be shared" error when the patient was on a dnsLess=false platform). New behaviour: - patientUsername returns `accessInfo.user.username` once accessInfo is loaded; throws HDSLibError if checkAndGetAccessInfo() has not been awaited first; still returns null when status !== 'active'. - AppManagingAccount.getContacts() now batches Promise.allSettled of checkAndGetAccessInfo() across active invites before calling toContactSource() so the cache is warm. Cost: +1 /access-info round-trip per active invite the consumer needs a username for. Acceptable tradeoff vs. silently broken DNS-less.
1 parent 6833d13 commit 8a11dc7

2 files changed

Lines changed: 15 additions & 10 deletions

File tree

ts/appTemplates/AppManagingAccount.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ export class AppManagingAccount extends Application {
9797
for (const result of results) {
9898
if (result.status === 'fulfilled') {
9999
const { collector, invites } = result.value;
100+
// Active invites need accessInfo loaded before patientUsername / toContactSource can be read.
101+
await Promise.allSettled(
102+
invites
103+
.filter((inv: any) => inv.status === 'active')
104+
.map((inv: any) => inv.checkAndGetAccessInfo())
105+
);
100106
for (const invite of invites) {
101107
sources.push(invite.toContactSource());
102108
allInvitePairs.push({ collector, invite });

ts/appTemplates/CollectorInvite.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,18 @@ export class CollectorInvite {
188188
return this.eventData.content.name;
189189
}
190190

191-
/** Extract patient username from apiEndpoint (only available for active invites) */
191+
/**
192+
* Patient username — read from `accessInfo.user.username`.
193+
* Topology-agnostic (works for both `dnsLess=true` and `dnsLess=false`).
194+
* Requires `checkAndGetAccessInfo()` to have been awaited first; throws otherwise.
195+
* Returns `null` when the invite is not `active`.
196+
*/
192197
get patientUsername (): string | null {
193198
if (this.status !== 'active') return null;
194-
try {
195-
const endpoint = this.eventData.content.apiEndpoint;
196-
if (!endpoint) return null;
197-
// apiEndpoint format: https://token@host/username/
198-
const url = new URL(endpoint.replace(/\/\/[^@]+@/, '//'));
199-
const path = url.pathname.replace(/^\/|\/$/g, '');
200-
return path || null;
201-
} catch {
202-
return null;
199+
if (this.#accessInfo == null) {
200+
throw new HDSLibError('patientUsername requires checkAndGetAccessInfo() to be called first');
203201
}
202+
return this.#accessInfo.user?.username ?? null;
204203
}
205204

206205
/** Convert to ContactSource for Contact grouping (doctor side) */

0 commit comments

Comments
 (0)