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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 3x 3x 3x 3x 3x 1x | import {type IMeta, handler} from '@feasibleone/blong';
import {newUuid, uuidBuf} from './gatewayUuid.ts';
type KnexQb = any;
/**
* Create/merge subscriptions linking an application to a bundle.
*
* Wire: `gateway.subscription.merge` — besides the `gateway_subscription` row,
* creates the `application hasRole bundleRole` core.triple edge and refreshes
* `access_pathRefresh`, so the application's effective actions (and hence its
* token authorization) follow from the subscribed bundle. The graph edge +
* refresh go through the shared `core.triple.merge` helper (P3).
*/
export default handler(
({
handler: {
'db/accessRoleEnsure': accessRoleEnsure,
'db/coreResourceEnsure': coreResourceEnsure,
'db/coreTripleMerge': coreTripleMerge,
},
}) =>
async function gatewaySubscriptionMerge(
params: {
subscription?: Record<
string,
{
/** Application clientId. */
application?: string;
/** Bundle name. */
bundle?: string;
status?: string;
startsAt?: string;
endsAt?: string;
}
>;
},
$meta: IMeta,
): Promise<{success: boolean}> {
const qb: KnexQb = this.config?.context?.queryBuilder;
if (!qb) throw new Error('Database not available');
if (!params.subscription) return {success: true};
const triples: Array<{subjectId: string; predicateName: string; objectId: string}> = [];
for (const [, subDef] of Object.entries(params.subscription)) {
// Resolve the application resource (by clientId).
const {resourceId: applicationId} = await coreResourceEnsure<{resourceId: string}>(
{
name: subDef.application ?? '',
typeAlias: 'gateway.application',
table: 'gateway_application',
extraColumns: {
ownerUserId: null,
applicationType: 'oauth2_client',
description: 'Demo API consumer',
isActive: 1,
},
keyName: 'applicationId',
},
$meta,
);
// The bundle's role IS the bundle resource (bundleId === roleId).
// A role the seed names must exist: the bit is allocated when it
// is new (it used to be hardcoded to 0 and silently skipped).
const {role: ensuredRole} = await accessRoleEnsure<{role: {roleId: string}}>(
{
role: {
roleName: subDef.bundle ?? '',
description: `${subDef.bundle} bundle role`,
},
},
$meta,
);
const roleId = ensuredRole.roleId;
// Subscription row — upsert on (applicationId, bundleId) so the
// seed is idempotent across server restarts.
await qb('gateway_subscription')
.insert({
subscriptionId: uuidBuf(newUuid()),
applicationId: uuidBuf(applicationId),
bundleId: uuidBuf(roleId),
status: subDef.status ?? 'active',
startsAt: new Date(subDef.startsAt ?? '2000-01-01'),
endsAt: subDef.endsAt ? new Date(subDef.endsAt) : null,
createdAt: new Date(),
})
.onConflict(['applicationId', 'bundleId'])
.merge();
// Authorization edge: application hasRole bundle role
triples.push({
subjectId: applicationId,
predicateName: 'hasRole',
objectId: roleId,
});
}
await coreTripleMerge({triples, refreshPath: true}, $meta);
return {success: true};
},
);
|