@vulog/aima-booking 1.2.33 → 1.2.35

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/dist/index.mjs CHANGED
@@ -148,6 +148,11 @@ const getSubscriptionBookingRequestById = async (client, id) => {
148
148
  ...data
149
149
  }));
150
150
  };
151
+ const getScheduledBookingById = async (client, bookingId) => {
152
+ const result = z.string().trim().min(1).uuid().safeParse(bookingId);
153
+ if (!result.success) throw new TypeError("Invalid bookingId", { cause: result.error.issues });
154
+ return client.get(`/boapi/proxy/user/scheduledBooking/by-service/fleets/${client.clientOptions.fleetId}/bookingrequests/${bookingId}`).then(({ data }) => data);
155
+ };
151
156
  //#endregion
152
157
  //#region src/getAvailableVehicles.ts
153
158
  /** ISO date-time without milliseconds (e.g. 2025-02-10T12:00:00Z or 2025-02-10T12:00:00+01:00). */
@@ -328,7 +333,7 @@ const deallocateVehicle = async (client, bookingRequestId, vehicleId) => {
328
333
  };
329
334
  //#endregion
330
335
  //#region src/updateScheduleBooking.ts
331
- const schema$2 = z$1.object({
336
+ const schema$7 = z$1.object({
332
337
  id: z$1.string(),
333
338
  startDate: z$1.string().refine((date) => !Number.isNaN(Date.parse(date)), { message: "Invalid date" }).optional(),
334
339
  latitude: z$1.number().min(-90).max(90).optional(),
@@ -350,7 +355,7 @@ const schema$2 = z$1.object({
350
355
  plannedReturnDate: z$1.string().refine((date) => !Number.isNaN(Date.parse(date)), { message: "Invalid date" }).optional()
351
356
  });
352
357
  const updateScheduleBooking = async (client, bookingRequestId, updateData) => {
353
- const result = schema$2.safeParse({
358
+ const result = schema$7.safeParse({
354
359
  id: bookingRequestId,
355
360
  ...updateData
356
361
  });
@@ -386,9 +391,9 @@ const triggerBRPayment = async (client, bookingRequestId, body) => {
386
391
  };
387
392
  //#endregion
388
393
  //#region src/getBookingRequestsByUserId.ts
389
- const schema$1 = z.object({ userId: z.string().trim().min(1).uuid() });
394
+ const schema$6 = z.object({ userId: z.string().trim().min(1).uuid() });
390
395
  const getBookingRequestsByUserId = async (client, userId) => {
391
- const result = schema$1.safeParse({ userId });
396
+ const result = schema$6.safeParse({ userId });
392
397
  if (!result.success) throw new TypeError("Invalid userId", { cause: result.error.issues });
393
398
  return client.get(`/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/bookingrequests/users/${userId}`).then(({ data }) => data).catch((error) => {
394
399
  if (error.formattedError?.status === 404) return [];
@@ -413,15 +418,80 @@ const releaseBRPayment = async (client, bookingRequestId, pspReference) => {
413
418
  };
414
419
  //#endregion
415
420
  //#region src/cancelBookingRequest.ts
416
- const schema = z$1.object({ id: z$1.string().uuid() });
421
+ const schema$5 = z$1.object({ id: z$1.string().uuid() });
417
422
  const cancelBookingRequest = async (client, id) => {
418
- const result = schema.safeParse({ id });
423
+ const result = schema$5.safeParse({ id });
419
424
  if (!result.success) throw new TypeError("Invalid args", { cause: result.error.issues });
420
425
  return client.post(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/bookingrequests/cancel/${id}`, {}).then(({ data }) => data).catch((error) => {
421
426
  throw new TypeError("Failed to cancel booking request", { cause: error });
422
427
  });
423
428
  };
424
429
  //#endregion
430
+ //#region src/getDigitalContracts.ts
431
+ const schema$4 = z.object({ bookingId: z.string().trim().min(1) });
432
+ const getDigitalContracts = async (client, bookingId, status = "SIGNED") => {
433
+ const result = schema$4.safeParse({ bookingId });
434
+ if (!result.success) throw new TypeError("Invalid args", { cause: result.error.issues });
435
+ return client.get(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/bookingrequests/${result.data.bookingId}/digitalContracts?status=${encodeURIComponent(status)}`).then(({ data }) => data);
436
+ };
437
+ //#endregion
438
+ //#region src/getAdditionalDrivers.ts
439
+ const schema$3 = z.object({ parentId: z.string().trim().min(1) });
440
+ const getAdditionalDrivers = async (client, parentId) => {
441
+ const result = schema$3.safeParse({ parentId });
442
+ if (!result.success) throw new TypeError("Invalid parentId", { cause: result.error.issues });
443
+ return client.get(`/boapi/proxy/user/fleets/${client.clientOptions.fleetId}/bookingrequests/${parentId}/additionalDrivers`).then(({ data }) => data);
444
+ };
445
+ //#endregion
446
+ //#region src/getSlaves.ts
447
+ const schema$2 = z.object({
448
+ parentId: z.string().trim().min(1),
449
+ status: z.enum(["ONGOING, COMPLETED", "UPCOMING"])
450
+ });
451
+ const getSlaves = async (client, parentId, status) => {
452
+ const result = schema$2.safeParse({
453
+ parentId,
454
+ status
455
+ });
456
+ if (!result.success) throw new TypeError("Invalid bookingId", { cause: result.error.issues });
457
+ return client.get(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/bookingRequests/${parentId}/bookingRequestsSlave?bookingStatus=${status}`).then(({ data }) => data);
458
+ };
459
+ //#endregion
460
+ //#region src/getSubscriptionsByUserId.ts
461
+ const schema$1 = z.object({
462
+ userId: z.string().trim().min(1).uuid(),
463
+ active: z.boolean().optional().default(true)
464
+ });
465
+ const getSubscriptionsByUserId = async (client, userId, active = true) => {
466
+ const result = schema$1.safeParse({
467
+ userId,
468
+ active
469
+ });
470
+ if (!result.success) throw new TypeError("Invalid userId", { cause: result.error.issues });
471
+ return client.get(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/users/${userId}/${active ? "active" : "past"}`).then(({ data }) => data);
472
+ };
473
+ //#endregion
474
+ //#region src/submitCancellationRequest.ts
475
+ const schema = z.object({
476
+ bookingRequestId: z.string().uuid(),
477
+ userId: z.string().uuid()
478
+ });
479
+ const submitCancellationRequest = async (client, bookingRequestId, userId) => {
480
+ const result = schema.safeParse({
481
+ bookingRequestId,
482
+ userId
483
+ });
484
+ if (!result.success) throw new TypeError("Invalid args", { cause: result.error.issues });
485
+ return client.post(`/boapi/proxy/user/fleets/${client.clientOptions.fleetId}/bookingrequests/${bookingRequestId}/actions`, {
486
+ action: "EARLIER_CANCELLATION_REQUEST",
487
+ bookingStatusBO: "ONGOING",
488
+ status: "PENDING",
489
+ userId
490
+ }).then(({ data }) => data).catch((error) => {
491
+ throw new TypeError("Failed to submit cancellation request", { cause: error });
492
+ });
493
+ };
494
+ //#endregion
425
495
  //#region src/createSubscription.ts
426
496
  const createSubscriptionBodySchema = z.object({
427
497
  userId: z.string().trim().min(1).uuid(),
@@ -451,4 +521,4 @@ const createSubscription = async (client, body) => {
451
521
  return client.post(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/bookingrequests`, result.data).then(({ data }) => data);
452
522
  };
453
523
  //#endregion
454
- export { allocateVehicle, cancelBookingRequest, createSubscription, deallocateVehicle, getAvailableVehicles, getBookingRequestById, getBookingRequestByTrip, getBookingRequests, getBookingRequestsByUserId, getSATBookingRequests, getScheduleBookingRequests, getStationById, getStations, getSubscriptionBookingRequestById, getSubscriptionBookingRequests, releaseBRPayment, triggerBRPayment, updateScheduleBooking };
524
+ export { allocateVehicle, cancelBookingRequest, createSubscription, deallocateVehicle, getAdditionalDrivers, getAvailableVehicles, getBookingRequestById, getBookingRequestByTrip, getBookingRequests, getBookingRequestsByUserId, getDigitalContracts, getSATBookingRequests, getScheduleBookingRequests, getScheduledBookingById, getSlaves, getStationById, getStations, getSubscriptionBookingRequestById, getSubscriptionBookingRequests, getSubscriptionsByUserId, releaseBRPayment, submitCancellationRequest, triggerBRPayment, updateScheduleBooking };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vulog/aima-booking",
3
3
  "type": "module",
4
- "version": "1.2.33",
4
+ "version": "1.2.35",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.cts",
@@ -32,8 +32,8 @@
32
32
  "author": "Vulog",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "@vulog/aima-client": "1.2.33",
36
- "@vulog/aima-core": "1.2.33"
35
+ "@vulog/aima-client": "1.2.35",
36
+ "@vulog/aima-core": "1.2.35"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "es-toolkit": "^1.39.9",
@@ -0,0 +1,61 @@
1
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
+ import { Client } from '@vulog/aima-client';
3
+ import { randomUUID } from 'crypto';
4
+ import { getAdditionalDrivers } from './getAdditionalDrivers';
5
+
6
+ describe('getAdditionalDrivers', () => {
7
+ const getMock = vi.fn();
8
+ const client = {
9
+ get: getMock,
10
+ clientOptions: {
11
+ fleetId: 'FLEET_ID',
12
+ },
13
+ } as unknown as Client;
14
+
15
+ beforeEach(() => {
16
+ getMock.mockReset();
17
+ });
18
+
19
+ test('throws Invalid parentId when parentId is empty', async () => {
20
+ await expect(getAdditionalDrivers(client, '')).rejects.toThrow(TypeError);
21
+ await expect(getAdditionalDrivers(client, '')).rejects.toMatchObject({
22
+ message: 'Invalid parentId',
23
+ });
24
+ expect(getMock).not.toHaveBeenCalled();
25
+ });
26
+
27
+ test('calls GET with correct URL and returns additional drivers', async () => {
28
+ const parentId = randomUUID();
29
+ const drivers = [
30
+ {
31
+ id: randomUUID(),
32
+ fleetId: 'FLEET_ID',
33
+ bookingRequestId: parentId,
34
+ profileId: randomUUID(),
35
+ updateDate: '2025-01-15T10:00:00Z',
36
+ invitationEmail: 'driver@example.com',
37
+ profileStatus: 'APPROVED',
38
+ userId: randomUUID(),
39
+ },
40
+ ];
41
+ getMock.mockResolvedValueOnce({ data: drivers });
42
+
43
+ const result = await getAdditionalDrivers(client, parentId);
44
+
45
+ expect(getMock).toHaveBeenCalledTimes(1);
46
+ expect(getMock).toHaveBeenCalledWith(
47
+ `/boapi/proxy/user/fleets/FLEET_ID/bookingrequests/${parentId}/additionalDrivers`
48
+ );
49
+ expect(result).toEqual(drivers);
50
+ expect(result).toHaveLength(1);
51
+ });
52
+
53
+ test('returns empty array when no additional drivers', async () => {
54
+ const parentId = randomUUID();
55
+ getMock.mockResolvedValueOnce({ data: [] });
56
+
57
+ const result = await getAdditionalDrivers(client, parentId);
58
+
59
+ expect(result).toEqual([]);
60
+ });
61
+ });
@@ -0,0 +1,23 @@
1
+ import { Client } from '@vulog/aima-client';
2
+ import { z } from 'zod';
3
+
4
+ import { type AdditionalDriver } from './types';
5
+
6
+ const schema = z.object({
7
+ parentId: z.string().trim().min(1),
8
+ });
9
+
10
+ export const getAdditionalDrivers = async (client: Client, parentId: string): Promise<AdditionalDriver[]> => {
11
+ const result = schema.safeParse({ parentId });
12
+ if (!result.success) {
13
+ throw new TypeError('Invalid parentId', {
14
+ cause: result.error.issues,
15
+ });
16
+ }
17
+
18
+ return client
19
+ .get<
20
+ AdditionalDriver[]
21
+ >(`/boapi/proxy/user/fleets/${client.clientOptions.fleetId}/bookingrequests/${parentId}/additionalDrivers`)
22
+ .then(({ data }) => data);
23
+ };
@@ -0,0 +1,53 @@
1
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
+ import { Client } from '@vulog/aima-client';
3
+ import { getAvailableVehicles } from './getAvailableVehicles';
4
+
5
+ describe('getAvailableVehicles', () => {
6
+ const getMock = vi.fn();
7
+ const client = {
8
+ get: getMock,
9
+ clientOptions: {
10
+ fleetId: 'FLEET_ID',
11
+ },
12
+ } as unknown as Client;
13
+
14
+ beforeEach(() => {
15
+ getMock.mockReset();
16
+ });
17
+
18
+ test('throws Invalid args when stationId is empty', async () => {
19
+ await expect(
20
+ getAvailableVehicles(client, '', '2025-02-10T12:00:00Z')
21
+ ).rejects.toThrow(TypeError);
22
+ await expect(
23
+ getAvailableVehicles(client, '', '2025-02-10T12:00:00Z')
24
+ ).rejects.toMatchObject({ message: 'Invalid args' });
25
+ expect(getMock).not.toHaveBeenCalled();
26
+ });
27
+
28
+ test('throws Invalid args when startDate is invalid datetime', async () => {
29
+ await expect(
30
+ getAvailableVehicles(client, 'station-123', 'not-a-date')
31
+ ).rejects.toThrow(TypeError);
32
+ expect(getMock).not.toHaveBeenCalled();
33
+ });
34
+
35
+ test('calls GET with correct URL and returns available vehicles response', async () => {
36
+ const stationId = 'ded03b86-fa3f-4d79-80fa-191b2ec13b5e';
37
+ const startDate = '2025-02-10T12:00:00Z';
38
+ const response = {
39
+ stationId,
40
+ vehicles: [{ vehicleId: 'vehicle-1' }, { vehicleId: 'vehicle-2' }],
41
+ };
42
+ getMock.mockResolvedValueOnce({ data: response });
43
+
44
+ const result = await getAvailableVehicles(client, stationId, startDate);
45
+
46
+ expect(getMock).toHaveBeenCalledTimes(1);
47
+ expect(getMock).toHaveBeenCalledWith(
48
+ `/boapi/proxy/user/scheduledBooking/fleets/FLEET_ID/subscription/stations/${stationId}/vehicles/available?startDate=${startDate}`
49
+ );
50
+ expect(result).toEqual(response);
51
+ expect(result.vehicles).toHaveLength(2);
52
+ });
53
+ });
@@ -1,5 +1,10 @@
1
1
  import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { getBookingRequestById, getBookingRequestByTrip, getSubscriptionBookingRequestById } from './getBookingRequest';
2
+ import {
3
+ getBookingRequestById,
4
+ getBookingRequestByTrip,
5
+ getSubscriptionBookingRequestById,
6
+ getScheduledBookingById,
7
+ } from './getBookingRequest';
3
8
  import { Client } from '@vulog/aima-client';
4
9
  import { randomUUID } from 'crypto';
5
10
  import { PaymentReceipts } from './types';
@@ -436,3 +441,69 @@ describe('getsubscriptionBookingRequestByID', () => {
436
441
  expect(result.station).toEqual(data.stationId);
437
442
  });
438
443
  });
444
+
445
+ describe('getScheduledBookingById', () => {
446
+ const getMock = vi.fn();
447
+ const client = {
448
+ get: getMock,
449
+ clientOptions: {
450
+ fleetId: 'FLEET_ID',
451
+ },
452
+ } as unknown as Client;
453
+
454
+ beforeEach(() => {
455
+ getMock.mockReset();
456
+ });
457
+
458
+ test('throws Invalid bookingId when bookingId is not a valid UUID', async () => {
459
+ await expect(getScheduledBookingById(client, 'not-a-uuid')).rejects.toThrow(TypeError);
460
+ await expect(getScheduledBookingById(client, 'not-a-uuid')).rejects.toMatchObject({
461
+ message: 'Invalid bookingId',
462
+ });
463
+ expect(getMock).not.toHaveBeenCalled();
464
+ });
465
+
466
+ test('throws Invalid bookingId when bookingId is empty', async () => {
467
+ await expect(getScheduledBookingById(client, '')).rejects.toThrow(TypeError);
468
+ expect(getMock).not.toHaveBeenCalled();
469
+ });
470
+
471
+ test('calls GET with by-service URL and returns SATBookingRequest', async () => {
472
+ const bookingId = randomUUID();
473
+ const data = {
474
+ id: bookingId,
475
+ startDate: '2026-03-03T10:00:00Z',
476
+ endDate: '2026-06-03T09:00:00Z',
477
+ returnedDate: null,
478
+ profileId: '76175b0c-7e39-41ad-a338-914df3a5fdf6',
479
+ vehicleId: null,
480
+ modelId: null,
481
+ journeyId: 'BEBD00056A9A16103D418CC36D940E26',
482
+ station: 'ded03b86-fa3f-4d79-80fa-191b2ec13b5e',
483
+ status: 'CONFIRMED',
484
+ creationDate: '2026-03-03T09:29:45Z',
485
+ fleetId: 'FLEET_ID',
486
+ userId: '20fb98a3-b60d-491a-9359-62e55f51fcb9',
487
+ serviceId: 'f49080b0-66bc-45e9-8e84-0dfb57bb8f92',
488
+ productIds: [],
489
+ pricingDetails: [],
490
+ completed: true,
491
+ rollingContract: true,
492
+ planDurationInMonths: 3,
493
+ planName: '3 mois',
494
+ contractType: 'SVS',
495
+ bookingReferenceId: 'q93UCF',
496
+ };
497
+ getMock.mockResolvedValueOnce({ data });
498
+
499
+ const result = await getScheduledBookingById(client, bookingId);
500
+
501
+ expect(getMock).toHaveBeenCalledTimes(1);
502
+ expect(getMock).toHaveBeenCalledWith(
503
+ `/boapi/proxy/user/scheduledBooking/by-service/fleets/FLEET_ID/bookingrequests/${bookingId}`
504
+ );
505
+ expect(result).toEqual(data);
506
+ expect(result.id).toBe(bookingId);
507
+ expect(result.status).toBe('CONFIRMED');
508
+ });
509
+ });
@@ -1,7 +1,7 @@
1
1
  import { Client } from '@vulog/aima-client';
2
2
  import { z } from 'zod';
3
3
 
4
- import { BookingRequest } from './types';
4
+ import { BookingRequest, SATBookingRequest } from './types';
5
5
 
6
6
  export const getBookingRequestById = async (client: Client, id: string): Promise<BookingRequest> => {
7
7
  const result = z.string().trim().min(1).uuid().safeParse(id);
@@ -46,3 +46,17 @@ export const getSubscriptionBookingRequestById = async (client: Client, id: stri
46
46
  )
47
47
  .then(({ data: { stationId, ...data } }) => ({ station: stationId, ...data }) as BookingRequest);
48
48
  };
49
+
50
+ export const getScheduledBookingById = async (client: Client, bookingId: string): Promise<SATBookingRequest> => {
51
+ const result = z.string().trim().min(1).uuid().safeParse(bookingId);
52
+ if (!result.success) {
53
+ throw new TypeError('Invalid bookingId', {
54
+ cause: result.error.issues,
55
+ });
56
+ }
57
+ return client
58
+ .get<SATBookingRequest>(
59
+ `/boapi/proxy/user/scheduledBooking/by-service/fleets/${client.clientOptions.fleetId}/bookingrequests/${bookingId}`
60
+ )
61
+ .then(({ data }) => data);
62
+ };
@@ -0,0 +1,72 @@
1
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
+ import { Client } from '@vulog/aima-client';
3
+ import { getDigitalContracts } from './getDigitalContracts';
4
+ import type { Contract } from './types';
5
+
6
+ describe('getDigitalContracts', () => {
7
+ const getMock = vi.fn();
8
+ const client = {
9
+ get: getMock,
10
+ clientOptions: {
11
+ fleetId: 'FLEET_ID',
12
+ },
13
+ } as unknown as Client;
14
+
15
+ const mockContracts: Contract[] = [
16
+ {
17
+ id: 'contract-1',
18
+ status: 'SIGNED',
19
+ provider: 'DOCUSIGN',
20
+ creationDate: '2026-03-03T10:00:00Z',
21
+ profileId: '76175b0c-7e39-41ad-a338-914df3a5fdf6',
22
+ externalContractId: 'ext-1',
23
+ type: 'RENTAL',
24
+ },
25
+ ];
26
+
27
+ beforeEach(() => {
28
+ getMock.mockReset();
29
+ });
30
+
31
+ test('throws Invalid args when bookingId is empty', async () => {
32
+ await expect(getDigitalContracts(client, '')).rejects.toThrow(TypeError);
33
+ await expect(getDigitalContracts(client, '')).rejects.toMatchObject({
34
+ message: 'Invalid args',
35
+ });
36
+ expect(getMock).not.toHaveBeenCalled();
37
+ });
38
+
39
+ test('calls GET with default status SIGNED and returns contracts', async () => {
40
+ const bookingId = '66bcbd77-37ef-47bd-b9af-877e5d213b8f';
41
+ getMock.mockResolvedValueOnce({ data: mockContracts });
42
+
43
+ const result = await getDigitalContracts(client, bookingId);
44
+
45
+ expect(getMock).toHaveBeenCalledTimes(1);
46
+ expect(getMock).toHaveBeenCalledWith(
47
+ `/boapi/proxy/user/scheduledBooking/fleets/FLEET_ID/subscription/bookingrequests/${bookingId}/digitalContracts?status=SIGNED`
48
+ );
49
+ expect(result).toEqual(mockContracts);
50
+ expect(result[0].status).toBe('SIGNED');
51
+ });
52
+
53
+ test('calls GET with custom status when provided', async () => {
54
+ const bookingId = '66bcbd77-37ef-47bd-b9af-877e5d213b8f';
55
+ getMock.mockResolvedValueOnce({ data: [] });
56
+
57
+ await getDigitalContracts(client, bookingId, 'PENDING');
58
+
59
+ expect(getMock).toHaveBeenCalledWith(
60
+ `/boapi/proxy/user/scheduledBooking/fleets/FLEET_ID/subscription/bookingrequests/${bookingId}/digitalContracts?status=PENDING`
61
+ );
62
+ });
63
+
64
+ test('returns empty array when no contracts', async () => {
65
+ const bookingId = 'some-booking-id';
66
+ getMock.mockResolvedValueOnce({ data: [] });
67
+
68
+ const result = await getDigitalContracts(client, bookingId);
69
+
70
+ expect(result).toEqual([]);
71
+ });
72
+ });
@@ -0,0 +1,26 @@
1
+ import { Client } from '@vulog/aima-client';
2
+ import { z } from 'zod';
3
+
4
+ import { Contract } from './types';
5
+
6
+ const schema = z.object({
7
+ bookingId: z.string().trim().min(1),
8
+ });
9
+
10
+ export const getDigitalContracts = async (
11
+ client: Client,
12
+ bookingId: string,
13
+ status = 'SIGNED'
14
+ ): Promise<Contract[]> => {
15
+ const result = schema.safeParse({ bookingId });
16
+ if (!result.success) {
17
+ throw new TypeError('Invalid args', {
18
+ cause: result.error.issues,
19
+ });
20
+ }
21
+ return client
22
+ .get<
23
+ Contract[]
24
+ >(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/bookingrequests/${result.data.bookingId}/digitalContracts?status=${encodeURIComponent(status)}`)
25
+ .then(({ data }) => data);
26
+ };
@@ -0,0 +1,60 @@
1
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
+ import { Client } from '@vulog/aima-client';
3
+ import { randomUUID } from 'crypto';
4
+ import { getSlaves } from './getSlaves';
5
+ import type { BookingRequestSlave } from './types';
6
+
7
+ describe('getSlaves', () => {
8
+ const getMock = vi.fn();
9
+ const client = {
10
+ get: getMock,
11
+ clientOptions: {
12
+ fleetId: 'FLEET_ID',
13
+ },
14
+ } as unknown as Client;
15
+
16
+ beforeEach(() => {
17
+ getMock.mockReset();
18
+ });
19
+
20
+ test('throws Invalid bookingId when parentId is empty', async () => {
21
+ await expect(getSlaves(client, '', 'UPCOMING')).rejects.toThrow(TypeError);
22
+ await expect(getSlaves(client, '', 'UPCOMING')).rejects.toMatchObject({
23
+ message: 'Invalid bookingId',
24
+ });
25
+ expect(getMock).not.toHaveBeenCalled();
26
+ });
27
+
28
+ test('throws when status is not in enum', async () => {
29
+ const parentId = randomUUID();
30
+ await expect(getSlaves(client, parentId, 'INVALID_STATUS')).rejects.toThrow(TypeError);
31
+ expect(getMock).not.toHaveBeenCalled();
32
+ });
33
+
34
+ test('calls GET with correct URL for UPCOMING and returns slaves', async () => {
35
+ const parentId = randomUUID();
36
+ const slaves: BookingRequestSlave[] = [
37
+ {
38
+ id: randomUUID(),
39
+ vehicleId: randomUUID(),
40
+ modelId: 2597,
41
+ startDate: '2026-03-03T10:00:00Z',
42
+ endDate: '2026-03-06T09:00:00Z',
43
+ tripId: 'BEBD00056A9A16103D418CC36D940E26',
44
+ parentId,
45
+ traveledDistance: 120,
46
+ energyLevelEnd: 80,
47
+ },
48
+ ];
49
+ getMock.mockResolvedValueOnce({ data: slaves });
50
+
51
+ const result = await getSlaves(client, parentId, 'UPCOMING');
52
+
53
+ expect(getMock).toHaveBeenCalledTimes(1);
54
+ expect(getMock).toHaveBeenCalledWith(
55
+ `/boapi/proxy/user/scheduledBooking/fleets/FLEET_ID/subscription/bookingRequests/${parentId}/bookingRequestsSlave?bookingStatus=UPCOMING`
56
+ );
57
+ expect(result).toEqual(slaves);
58
+ expect(result).toHaveLength(1);
59
+ });
60
+ });
@@ -0,0 +1,24 @@
1
+ import { Client } from '@vulog/aima-client';
2
+ import { z } from 'zod';
3
+
4
+ import { BookingRequestSlave } from './types';
5
+
6
+ const schema = z.object({
7
+ parentId: z.string().trim().min(1),
8
+ status: z.enum(['ONGOING, COMPLETED', 'UPCOMING']),
9
+ });
10
+
11
+ export const getSlaves = async (client: Client, parentId: string, status: string): Promise<BookingRequestSlave[]> => {
12
+ const result = schema.safeParse({ parentId, status });
13
+ if (!result.success) {
14
+ throw new TypeError('Invalid bookingId', {
15
+ cause: result.error.issues,
16
+ });
17
+ }
18
+
19
+ return client
20
+ .get<
21
+ BookingRequestSlave[]
22
+ >(`/boapi/proxy/user/scheduledBooking/fleets/${client.clientOptions.fleetId}/subscription/bookingRequests/${parentId}/bookingRequestsSlave?bookingStatus=${status}`)
23
+ .then(({ data }) => data);
24
+ };