All files / realm/blong-party/adapter/dbTest partyTestProfileMerge.ts

100% Statements 86/86
20% Branches 2/10
100% Functions 1/1
100% Lines 86/86

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 871x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
import {type IMeta, handler} from '@feasibleone/blong';
 
// The knex query builder is intentionally untyped here (matches the access realm's db handlers).
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type KnexQb = any;
 
/**
 * dbTest seed — link an access user to a `party.person` via the
 * `user --hasProfile--> person` graph edge, so the self-service profile page
 * has personal details (name → avatar initials) in the blong-party suite.
 *
 * Wire: `partyTestProfileMerge` — dispatched from
 * `meta/dbTest/partyTestProfileMerge.yaml` (a `.dbTest.asset`).
 * Idempotent: if the user already has a `hasProfile` edge, the seed is a no-op.
 *
 * Lives in `adapter/dbTest/` because it is a TEST-ONLY seed: the db adapter
 * imports `.dbTest` handler groups only under `dev` (never in production),
 * so this handler is never registered for production seeding.
 */
export default handler(
    ({handler: {'db/coreResourceEnsure': coreResourceEnsure}}) =>
        async function partyTestProfileMerge(
            params: {
                userName: string;
                firstName: string;
                middleName?: string;
                lastName: string;
                birthDate?: string;
                gender?: string;
                nationality?: string;
                occupation?: string;
            },
            $meta: IMeta,
        ): Promise<{success: boolean; linked?: boolean}> {
            const qb: KnexQb = this.config?.context?.queryBuilder;
            if (!qb) throw new Error('Database not available');
 
            // 1. Resolve the access user by resourceName.
            const user = (await qb
                .select('r.resourceId')
                .from('core_resource as r')
                .join('core_type as t', 't.typeId', 'r.typeId')
                .where('r.resourceName', params.userName)
                .where('t.typeAlias', 'access.user')
                .first()) as {resourceId: Buffer} | undefined;
            if (!user) return {success: false, linked: false};
 
            const userIdBuf = Buffer.from(user.resourceId);
 
            // 2. Already linked? (idempotent across restarts).
            const existing = (await qb('core_triple')
                .where('subjectId', userIdBuf)
                .where('predicateName', 'hasProfile')
                .first()) as {objectId: Buffer} | undefined;
            if (existing) return {success: true, linked: true};
 
            // 3. Ensure a party.person resource + row.
            const {resourceId: personId} = await coreResourceEnsure<{resourceId: string}>({
                name: `${params.firstName} ${params.lastName}`.trim(),
                typeAlias: 'party.person',
                table: 'party_person',
                extraColumns: {
                    firstName: params.firstName,
                    middleName: params.middleName ?? null,
                    lastName: params.lastName,
                    birthDate: params.birthDate ?? null,
                    gender: params.gender ?? null,
                    nationality: params.nationality ?? null,
                    occupation: params.occupation ?? null,
                },
                keyName: 'personId',
            }, $meta);
 
            // 4. Link user → hasProfile → person.
            await qb('core_triple')
                .insert({
                    subjectId: userIdBuf,
                    predicateName: 'hasProfile',
                    objectId: Buffer.from(personId.replace(/-/g, ''), 'hex'),
                })
                .onConflict()
                .ignore();
 
            return {success: true, linked: true};
        },
);