@shipfox/api-runners 6.0.0 → 7.0.1
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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +19 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/entities/runner-instance.d.ts +1 -0
- package/dist/core/entities/runner-instance.d.ts.map +1 -1
- package/dist/core/entities/runner-instance.js.map +1 -1
- package/dist/core/errors.d.ts +6 -6
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +8 -8
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +2 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/runner-assignments.d.ts +6 -0
- package/dist/core/runner-assignments.d.ts.map +1 -0
- package/dist/core/runner-assignments.js +6 -0
- package/dist/core/runner-assignments.js.map +1 -0
- package/dist/core/runner-instances.d.ts +2 -9
- package/dist/core/runner-instances.d.ts.map +1 -1
- package/dist/core/runner-instances.js +3 -6
- package/dist/core/runner-instances.js.map +1 -1
- package/dist/db/db.d.ts +34 -218
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +0 -2
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +2 -2
- package/dist/db/index.js.map +1 -1
- package/dist/db/runner-assignments.d.ts +7 -0
- package/dist/db/runner-assignments.d.ts.map +1 -0
- package/dist/db/runner-assignments.js +55 -0
- package/dist/db/runner-assignments.js.map +1 -0
- package/dist/db/runner-instances.d.ts +2 -9
- package/dist/db/runner-instances.d.ts.map +1 -1
- package/dist/db/runner-instances.js +2 -16
- package/dist/db/runner-instances.js.map +1 -1
- package/dist/db/schema/runner-instances.d.ts +17 -0
- package/dist/db/schema/runner-instances.d.ts.map +1 -1
- package/dist/db/schema/runner-instances.js +5 -1
- package/dist/db/schema/runner-instances.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/dto/index.d.ts +0 -1
- package/dist/presentation/dto/index.d.ts.map +1 -1
- package/dist/presentation/dto/index.js +0 -1
- package/dist/presentation/dto/index.js.map +1 -1
- package/dist/presentation/routes/assign-runner-instances.d.ts +2 -0
- package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -0
- package/dist/presentation/routes/assign-runner-instances.js +40 -0
- package/dist/presentation/routes/assign-runner-instances.js.map +1 -0
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +4 -8
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/runner-enrollment.d.ts +1 -0
- package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -1
- package/dist/presentation/routes/runner-enrollment.js +27 -0
- package/dist/presentation/routes/runner-enrollment.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +1 -12
- package/drizzle/meta/0000_snapshot.json +6 -97
- package/package.json +10 -10
- package/src/config.ts +1 -1
- package/src/core/entities/runner-instance.ts +1 -0
- package/src/core/errors.ts +8 -8
- package/src/core/index.ts +2 -9
- package/src/core/runner-assignments.ts +9 -0
- package/src/core/runner-instances.ts +4 -13
- package/src/db/db.ts +0 -2
- package/src/db/index.ts +2 -3
- package/src/db/runner-assignments.test.ts +163 -0
- package/src/db/runner-assignments.ts +115 -0
- package/src/db/runner-instances.test.ts +36 -23
- package/src/db/runner-instances.ts +3 -23
- package/src/db/schema/runner-instances.ts +3 -1
- package/src/index.ts +0 -9
- package/src/presentation/dto/index.ts +0 -4
- package/src/presentation/routes/assign-runner-instances.ts +49 -0
- package/src/presentation/routes/index.ts +4 -7
- package/src/presentation/routes/runner-enrollment.test.ts +60 -0
- package/src/presentation/routes/runner-enrollment.ts +22 -0
- package/test/globalSetup.ts +0 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/capacity-assignments.d.ts +0 -7
- package/dist/core/capacity-assignments.d.ts.map +0 -1
- package/dist/core/capacity-assignments.js +0 -6
- package/dist/core/capacity-assignments.js.map +0 -1
- package/dist/core/entities/capacity-assignment.d.ts +0 -9
- package/dist/core/entities/capacity-assignment.d.ts.map +0 -1
- package/dist/core/entities/capacity-assignment.js +0 -3
- package/dist/core/entities/capacity-assignment.js.map +0 -1
- package/dist/db/capacity-assignments.d.ts +0 -7
- package/dist/db/capacity-assignments.d.ts.map +0 -1
- package/dist/db/capacity-assignments.js +0 -72
- package/dist/db/capacity-assignments.js.map +0 -1
- package/dist/db/schema/capacity-assignments.d.ts +0 -113
- package/dist/db/schema/capacity-assignments.d.ts.map +0 -1
- package/dist/db/schema/capacity-assignments.js +0 -29
- package/dist/db/schema/capacity-assignments.js.map +0 -1
- package/dist/presentation/dto/mint-registration-tokens.d.ts +0 -7
- package/dist/presentation/dto/mint-registration-tokens.d.ts.map +0 -1
- package/dist/presentation/dto/mint-registration-tokens.js +0 -19
- package/dist/presentation/dto/mint-registration-tokens.js.map +0 -1
- package/dist/presentation/routes/assign-capacity.d.ts +0 -2
- package/dist/presentation/routes/assign-capacity.d.ts.map +0 -1
- package/dist/presentation/routes/assign-capacity.js +0 -44
- package/dist/presentation/routes/assign-capacity.js.map +0 -1
- package/dist/presentation/routes/mint-registration-tokens.d.ts +0 -2
- package/dist/presentation/routes/mint-registration-tokens.d.ts.map +0 -1
- package/dist/presentation/routes/mint-registration-tokens.js +0 -74
- package/dist/presentation/routes/mint-registration-tokens.js.map +0 -1
- package/dist/presentation/routes/planned-capacity.d.ts +0 -3
- package/dist/presentation/routes/planned-capacity.d.ts.map +0 -1
- package/dist/presentation/routes/planned-capacity.js +0 -54
- package/dist/presentation/routes/planned-capacity.js.map +0 -1
- package/src/core/capacity-assignments.ts +0 -10
- package/src/core/entities/capacity-assignment.ts +0 -8
- package/src/db/capacity-assignments.test.ts +0 -159
- package/src/db/capacity-assignments.ts +0 -116
- package/src/db/schema/capacity-assignments.ts +0 -34
- package/src/presentation/dto/mint-registration-tokens.ts +0 -28
- package/src/presentation/routes/assign-capacity.ts +0 -48
- package/src/presentation/routes/mint-registration-tokens.test.ts +0 -453
- package/src/presentation/routes/mint-registration-tokens.ts +0 -80
- package/src/presentation/routes/planned-capacity.ts +0 -49
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import {eq, inArray} from 'drizzle-orm';
|
|
2
|
-
import {assignCapacityBatch} from '#db/capacity-assignments.js';
|
|
3
|
-
import {db} from '#db/db.js';
|
|
4
|
-
import {capacityAssignments} from '#db/schema/capacity-assignments.js';
|
|
5
|
-
import {reservations} from '#db/schema/reservations.js';
|
|
6
|
-
import {providerRunners} from '#db/schema/runner-instances.js';
|
|
7
|
-
|
|
8
|
-
describe('assignCapacityBatch', () => {
|
|
9
|
-
let workspaceId: string;
|
|
10
|
-
let provisionerId: string;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
workspaceId = crypto.randomUUID();
|
|
14
|
-
provisionerId = crypto.randomUUID();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('assigns compatible live capacity once and returns the same assignment on retry', async () => {
|
|
18
|
-
const reservation = await createReservation();
|
|
19
|
-
const capacity = await createCapacity();
|
|
20
|
-
|
|
21
|
-
const first = await assignCapacityBatch({
|
|
22
|
-
provisionerId,
|
|
23
|
-
reservationId: reservation.id,
|
|
24
|
-
capacityIds: [capacity.id],
|
|
25
|
-
});
|
|
26
|
-
const retry = await assignCapacityBatch({
|
|
27
|
-
provisionerId,
|
|
28
|
-
reservationId: reservation.id,
|
|
29
|
-
capacityIds: [capacity.id],
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
expect(first).toHaveLength(1);
|
|
33
|
-
expect(retry).toEqual(first);
|
|
34
|
-
expect(first[0]).toMatchObject({
|
|
35
|
-
capacityId: capacity.id,
|
|
36
|
-
reservationId: reservation.id,
|
|
37
|
-
workspaceId,
|
|
38
|
-
provisionerId,
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('does not rebind capacity or a reservation under concurrent assignment requests', async () => {
|
|
43
|
-
const reservation = await createReservation();
|
|
44
|
-
const firstCapacity = await createCapacity();
|
|
45
|
-
const secondCapacity = await createCapacity();
|
|
46
|
-
|
|
47
|
-
const result = await Promise.allSettled([
|
|
48
|
-
assignCapacityBatch({
|
|
49
|
-
provisionerId,
|
|
50
|
-
reservationId: reservation.id,
|
|
51
|
-
capacityIds: [firstCapacity.id],
|
|
52
|
-
}),
|
|
53
|
-
assignCapacityBatch({
|
|
54
|
-
provisionerId,
|
|
55
|
-
reservationId: reservation.id,
|
|
56
|
-
capacityIds: [secondCapacity.id],
|
|
57
|
-
}),
|
|
58
|
-
]);
|
|
59
|
-
|
|
60
|
-
expect(result.filter((entry) => entry.status === 'fulfilled')).toHaveLength(1);
|
|
61
|
-
const rows = await db()
|
|
62
|
-
.select()
|
|
63
|
-
.from(capacityAssignments)
|
|
64
|
-
.where(eq(capacityAssignments.reservationId, reservation.id));
|
|
65
|
-
expect(rows).toHaveLength(1);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('consumes each slot in a multi-capacity reservation without rebinding capacity', async () => {
|
|
69
|
-
const reservation = await createReservation({count: 2});
|
|
70
|
-
const firstCapacity = await createCapacity();
|
|
71
|
-
const secondCapacity = await createCapacity();
|
|
72
|
-
|
|
73
|
-
const assignments = await assignCapacityBatch({
|
|
74
|
-
provisionerId,
|
|
75
|
-
reservationId: reservation.id,
|
|
76
|
-
capacityIds: [firstCapacity.id, secondCapacity.id],
|
|
77
|
-
});
|
|
78
|
-
const retry = await assignCapacityBatch({
|
|
79
|
-
provisionerId,
|
|
80
|
-
reservationId: reservation.id,
|
|
81
|
-
capacityIds: [firstCapacity.id, secondCapacity.id],
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
expect(assignments).toHaveLength(2);
|
|
85
|
-
expect(retry).toEqual(assignments);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('rejects expired, incompatible, and foreign capacity without consuming it', async () => {
|
|
89
|
-
const expiredReservation = await createReservation({expiresAt: new Date(Date.now() - 1_000)});
|
|
90
|
-
const compatibleCapacity = await createCapacity();
|
|
91
|
-
const incompatibleReservation = await createReservation({requiredLabels: ['gpu']});
|
|
92
|
-
const foreignCapacity = await createCapacity({provisionerId: crypto.randomUUID()});
|
|
93
|
-
|
|
94
|
-
await expect(
|
|
95
|
-
assignCapacityBatch({
|
|
96
|
-
provisionerId,
|
|
97
|
-
reservationId: expiredReservation.id,
|
|
98
|
-
capacityIds: [compatibleCapacity.id],
|
|
99
|
-
}),
|
|
100
|
-
).rejects.toMatchObject({name: 'ReservationExpiredError'});
|
|
101
|
-
await expect(
|
|
102
|
-
assignCapacityBatch({
|
|
103
|
-
provisionerId,
|
|
104
|
-
reservationId: incompatibleReservation.id,
|
|
105
|
-
capacityIds: [compatibleCapacity.id],
|
|
106
|
-
}),
|
|
107
|
-
).rejects.toMatchObject({name: 'CapacityNotAssignableError'});
|
|
108
|
-
await expect(
|
|
109
|
-
assignCapacityBatch({
|
|
110
|
-
provisionerId,
|
|
111
|
-
reservationId: incompatibleReservation.id,
|
|
112
|
-
capacityIds: [foreignCapacity.id],
|
|
113
|
-
}),
|
|
114
|
-
).rejects.toMatchObject({name: 'CapacityNotAssignableError'});
|
|
115
|
-
|
|
116
|
-
const rows = await db()
|
|
117
|
-
.select()
|
|
118
|
-
.from(capacityAssignments)
|
|
119
|
-
.where(
|
|
120
|
-
inArray(capacityAssignments.reservationId, [
|
|
121
|
-
expiredReservation.id,
|
|
122
|
-
incompatibleReservation.id,
|
|
123
|
-
]),
|
|
124
|
-
);
|
|
125
|
-
expect(rows).toEqual([]);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
async function createReservation(overrides: Partial<typeof reservations.$inferInsert> = {}) {
|
|
129
|
-
const [row] = await db()
|
|
130
|
-
.insert(reservations)
|
|
131
|
-
.values({
|
|
132
|
-
workspaceId,
|
|
133
|
-
provisionerId,
|
|
134
|
-
requiredLabels: ['linux'],
|
|
135
|
-
count: 1,
|
|
136
|
-
expiresAt: new Date(Date.now() + 60_000),
|
|
137
|
-
...overrides,
|
|
138
|
-
})
|
|
139
|
-
.returning();
|
|
140
|
-
if (!row) throw new Error('Reservation insert returned no row');
|
|
141
|
-
return row;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function createCapacity(overrides: Partial<typeof providerRunners.$inferInsert> = {}) {
|
|
145
|
-
const [row] = await db()
|
|
146
|
-
.insert(providerRunners)
|
|
147
|
-
.values({
|
|
148
|
-
provisionerId,
|
|
149
|
-
providerRunnerId: crypto.randomUUID(),
|
|
150
|
-
labels: ['linux'],
|
|
151
|
-
state: 'running',
|
|
152
|
-
reportedAt: new Date(),
|
|
153
|
-
...overrides,
|
|
154
|
-
})
|
|
155
|
-
.returning();
|
|
156
|
-
if (!row) throw new Error('Capacity insert returned no row');
|
|
157
|
-
return row;
|
|
158
|
-
}
|
|
159
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import {and, eq, inArray, sql} from 'drizzle-orm';
|
|
2
|
-
import type {CapacityAssignment} from '#core/entities/capacity-assignment.js';
|
|
3
|
-
import {
|
|
4
|
-
CapacityAlreadyAssignedError,
|
|
5
|
-
CapacityNotAssignableError,
|
|
6
|
-
ReservationExpiredError,
|
|
7
|
-
ReservationNotFoundError,
|
|
8
|
-
} from '#core/errors.js';
|
|
9
|
-
import {db} from './db.js';
|
|
10
|
-
import {capacityAssignments, toCapacityAssignment} from './schema/capacity-assignments.js';
|
|
11
|
-
import {reservations} from './schema/reservations.js';
|
|
12
|
-
import {providerRunners} from './schema/runner-instances.js';
|
|
13
|
-
|
|
14
|
-
export async function assignCapacityBatch(params: {
|
|
15
|
-
provisionerId: string;
|
|
16
|
-
reservationId: string;
|
|
17
|
-
capacityIds: string[];
|
|
18
|
-
}): Promise<CapacityAssignment[]> {
|
|
19
|
-
return await db().transaction(async (tx) => {
|
|
20
|
-
const lockKey = [
|
|
21
|
-
'runners_capacity_assignments',
|
|
22
|
-
params.provisionerId,
|
|
23
|
-
params.reservationId,
|
|
24
|
-
].join(':');
|
|
25
|
-
await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${lockKey}))`);
|
|
26
|
-
for (const capacityId of [...params.capacityIds].sort()) {
|
|
27
|
-
await tx.execute(
|
|
28
|
-
sql`select pg_advisory_xact_lock(hashtext(${['runners_capacity_assignments', capacityId].join(':')}))`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const [reservation] = await tx
|
|
33
|
-
.select()
|
|
34
|
-
.from(reservations)
|
|
35
|
-
.where(
|
|
36
|
-
and(
|
|
37
|
-
eq(reservations.id, params.reservationId),
|
|
38
|
-
eq(reservations.provisionerId, params.provisionerId),
|
|
39
|
-
),
|
|
40
|
-
)
|
|
41
|
-
.limit(1);
|
|
42
|
-
if (!reservation) throw new ReservationNotFoundError(params.reservationId);
|
|
43
|
-
if (reservation.expiresAt <= new Date())
|
|
44
|
-
throw new ReservationExpiredError(params.reservationId);
|
|
45
|
-
|
|
46
|
-
const existing = await tx
|
|
47
|
-
.select()
|
|
48
|
-
.from(capacityAssignments)
|
|
49
|
-
.where(eq(capacityAssignments.reservationId, reservation.id));
|
|
50
|
-
const existingByCapacityId = new Map(
|
|
51
|
-
existing.map((assignment) => [assignment.capacityId, assignment]),
|
|
52
|
-
);
|
|
53
|
-
const newCapacityIds = params.capacityIds.filter(
|
|
54
|
-
(capacityId) => !existingByCapacityId.has(capacityId),
|
|
55
|
-
);
|
|
56
|
-
if (existing.length + newCapacityIds.length > reservation.count)
|
|
57
|
-
throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');
|
|
58
|
-
|
|
59
|
-
if (newCapacityIds.length === 0) {
|
|
60
|
-
return params.capacityIds.flatMap((capacityId) => {
|
|
61
|
-
const assignment = existingByCapacityId.get(capacityId);
|
|
62
|
-
return assignment ? [toCapacityAssignment(assignment)] : [];
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const capacities = await tx
|
|
67
|
-
.select()
|
|
68
|
-
.from(providerRunners)
|
|
69
|
-
.where(
|
|
70
|
-
and(
|
|
71
|
-
eq(providerRunners.provisionerId, params.provisionerId),
|
|
72
|
-
inArray(providerRunners.id, newCapacityIds),
|
|
73
|
-
),
|
|
74
|
-
);
|
|
75
|
-
if (capacities.length !== newCapacityIds.length)
|
|
76
|
-
throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');
|
|
77
|
-
for (const capacity of capacities) {
|
|
78
|
-
if (capacity.state !== 'starting' && capacity.state !== 'running')
|
|
79
|
-
throw new CapacityNotAssignableError(capacity.id);
|
|
80
|
-
if (
|
|
81
|
-
!capacity.providerRunnerId ||
|
|
82
|
-
capacity.labels.length === 0 ||
|
|
83
|
-
!reservation.requiredLabels.every((label) => capacity.labels.includes(label))
|
|
84
|
-
) {
|
|
85
|
-
throw new CapacityNotAssignableError(capacity.id);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const assignedCapacity = await tx
|
|
90
|
-
.select({capacityId: capacityAssignments.capacityId})
|
|
91
|
-
.from(capacityAssignments)
|
|
92
|
-
.where(inArray(capacityAssignments.capacityId, newCapacityIds));
|
|
93
|
-
if (assignedCapacity.length > 0)
|
|
94
|
-
throw new CapacityAlreadyAssignedError(assignedCapacity[0]?.capacityId ?? '');
|
|
95
|
-
|
|
96
|
-
const inserted = await tx
|
|
97
|
-
.insert(capacityAssignments)
|
|
98
|
-
.values(
|
|
99
|
-
newCapacityIds.map((capacityId) => ({
|
|
100
|
-
capacityId,
|
|
101
|
-
reservationId: reservation.id,
|
|
102
|
-
workspaceId: reservation.workspaceId,
|
|
103
|
-
provisionerId: params.provisionerId,
|
|
104
|
-
})),
|
|
105
|
-
)
|
|
106
|
-
.returning();
|
|
107
|
-
const insertedByCapacityId = new Map(
|
|
108
|
-
inserted.map((assignment) => [assignment.capacityId, assignment]),
|
|
109
|
-
);
|
|
110
|
-
return params.capacityIds.flatMap((capacityId) => {
|
|
111
|
-
const assignment =
|
|
112
|
-
existingByCapacityId.get(capacityId) ?? insertedByCapacityId.get(capacityId);
|
|
113
|
-
return assignment ? [toCapacityAssignment(assignment)] : [];
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
|
|
2
|
-
import {index, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
|
|
3
|
-
import type {CapacityAssignment} from '#core/entities/capacity-assignment.js';
|
|
4
|
-
import {pgTable} from './common.js';
|
|
5
|
-
|
|
6
|
-
export const capacityAssignments = pgTable(
|
|
7
|
-
'capacity_assignments',
|
|
8
|
-
{
|
|
9
|
-
id: uuidv7PrimaryKey(),
|
|
10
|
-
capacityId: uuid('capacity_id').notNull(),
|
|
11
|
-
reservationId: uuid('reservation_id').notNull(),
|
|
12
|
-
workspaceId: uuid('workspace_id').notNull(),
|
|
13
|
-
provisionerId: uuid('provisioner_id').notNull(),
|
|
14
|
-
createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
|
|
15
|
-
},
|
|
16
|
-
(table) => [
|
|
17
|
-
uniqueIndex('runners_capacity_assignments_capacity_unique').on(table.capacityId),
|
|
18
|
-
index('runners_capacity_assignments_reservation_idx').on(table.reservationId),
|
|
19
|
-
index('runners_capacity_assignments_workspace_idx').on(table.workspaceId),
|
|
20
|
-
],
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
export type CapacityAssignmentDb = typeof capacityAssignments.$inferSelect;
|
|
24
|
-
|
|
25
|
-
export function toCapacityAssignment(row: CapacityAssignmentDb): CapacityAssignment {
|
|
26
|
-
return {
|
|
27
|
-
id: row.id,
|
|
28
|
-
capacityId: row.capacityId,
|
|
29
|
-
reservationId: row.reservationId,
|
|
30
|
-
workspaceId: row.workspaceId,
|
|
31
|
-
provisionerId: row.provisionerId,
|
|
32
|
-
createdAt: row.createdAt,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
MintedRegistrationTokenDto,
|
|
3
|
-
MintRegistrationTokensBatchResponseDto,
|
|
4
|
-
MintRegistrationTokensRunnerInstanceDto,
|
|
5
|
-
} from '@shipfox/api-runners-dto';
|
|
6
|
-
import type {MintEphemeralRegistrationTokensBatchResult} from '#core/ephemeral-registration-tokens.js';
|
|
7
|
-
|
|
8
|
-
export function toMintRegistrationTokensRunnerInstances(
|
|
9
|
-
providerRunners: MintRegistrationTokensRunnerInstanceDto[],
|
|
10
|
-
) {
|
|
11
|
-
return providerRunners.map((runner) => ({providerRunnerId: runner.provider_runner_id}));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function toMintRegistrationTokensResponseDto(
|
|
15
|
-
result: MintEphemeralRegistrationTokensBatchResult[],
|
|
16
|
-
): MintRegistrationTokensBatchResponseDto {
|
|
17
|
-
return {tokens: result.map(toMintedRegistrationTokenDto)};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function toMintedRegistrationTokenDto(
|
|
21
|
-
result: MintEphemeralRegistrationTokensBatchResult,
|
|
22
|
-
): MintedRegistrationTokenDto {
|
|
23
|
-
return {
|
|
24
|
-
provider_runner_id: result.providerRunnerId,
|
|
25
|
-
registration_token: result.rawToken,
|
|
26
|
-
expires_at: result.token.expiresAt.toISOString(),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {requireProvisionerContext} from '@shipfox/api-auth-context';
|
|
2
|
-
import {
|
|
3
|
-
assignCapacityBatchBodySchema,
|
|
4
|
-
assignCapacityBatchResponseSchema,
|
|
5
|
-
} from '@shipfox/api-runners-dto';
|
|
6
|
-
import {ClientError, defineRoute} from '@shipfox/node-fastify';
|
|
7
|
-
import {assignCapacityBatch} from '#core/capacity-assignments.js';
|
|
8
|
-
import {
|
|
9
|
-
CapacityAlreadyAssignedError,
|
|
10
|
-
CapacityNotAssignableError,
|
|
11
|
-
ReservationExpiredError,
|
|
12
|
-
ReservationNotFoundError,
|
|
13
|
-
} from '#core/errors.js';
|
|
14
|
-
|
|
15
|
-
export const assignCapacityRoute = defineRoute({
|
|
16
|
-
method: 'POST',
|
|
17
|
-
path: '/capacity/assignments',
|
|
18
|
-
description: 'Atomically assign provisioner capacity to a live reservation',
|
|
19
|
-
schema: {body: assignCapacityBatchBodySchema, response: {200: assignCapacityBatchResponseSchema}},
|
|
20
|
-
errorHandler: (error) => {
|
|
21
|
-
if (error instanceof ReservationNotFoundError)
|
|
22
|
-
throw new ClientError('Reservation not found', 'reservation-not-found', {status: 404});
|
|
23
|
-
if (error instanceof ReservationExpiredError)
|
|
24
|
-
throw new ClientError('Reservation has expired', 'reservation-expired', {status: 409});
|
|
25
|
-
if (
|
|
26
|
-
error instanceof CapacityAlreadyAssignedError ||
|
|
27
|
-
error instanceof CapacityNotAssignableError
|
|
28
|
-
)
|
|
29
|
-
throw new ClientError('Capacity cannot be assigned', 'capacity-not-assignable', {
|
|
30
|
-
status: 409,
|
|
31
|
-
});
|
|
32
|
-
throw error;
|
|
33
|
-
},
|
|
34
|
-
handler: async (request) => {
|
|
35
|
-
const {provisionerTokenId} = requireProvisionerContext(request);
|
|
36
|
-
const assignments = await assignCapacityBatch({
|
|
37
|
-
provisionerId: provisionerTokenId,
|
|
38
|
-
reservationId: request.body.reservation_id,
|
|
39
|
-
capacityIds: request.body.capacity_ids,
|
|
40
|
-
});
|
|
41
|
-
return {
|
|
42
|
-
assignments: assignments.map((assignment) => ({
|
|
43
|
-
capacity_id: assignment.capacityId,
|
|
44
|
-
assignment_id: assignment.id,
|
|
45
|
-
})),
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
});
|