202 lines
6.7 KiB
TypeScript
202 lines
6.7 KiB
TypeScript
import {
|
|
reservationTaskAudits,
|
|
reservationTaskDetails,
|
|
} from '@/fixtures/reservation'
|
|
import { getJson, sendJson } from '@/services/httpClient'
|
|
import type {
|
|
ReservationOperaOperationResult,
|
|
ReservationOperaSimulationRequest,
|
|
ReservationOrderDetailResult,
|
|
ReservationOrderListFilters,
|
|
ReservationOrderListResult,
|
|
ReservationTaskAuditListResult,
|
|
ReservationTaskDetailResult,
|
|
ReservationTaskListFilters,
|
|
ReservationTaskListResult,
|
|
ReservationTaskPayloadMutationRequest,
|
|
ReservationTaskPayloadMutationResult,
|
|
SourceMessageConversationResult,
|
|
} from '@/types/reservation'
|
|
|
|
const useReservationFixtures = import.meta.env.VITE_RESERVATION_USE_FIXTURES === 'true'
|
|
|
|
export class EndpointPendingError extends Error {
|
|
constructor(message: string) {
|
|
super(message)
|
|
this.name = 'EndpointPendingError'
|
|
}
|
|
}
|
|
|
|
export async function fetchReservationOrders(
|
|
filters: ReservationOrderListFilters = {},
|
|
): Promise<ReservationOrderListResult> {
|
|
void filters
|
|
throw new EndpointPendingError('GET /api/reservation/orders is pending backend implementation.')
|
|
}
|
|
|
|
export async function fetchReservationTaskList(
|
|
filters: ReservationTaskListFilters = {},
|
|
): Promise<ReservationTaskListResult> {
|
|
return getJson<ReservationTaskListResult>(withQuery('/api/reservation/tasks', filters))
|
|
}
|
|
|
|
export async function fetchReservationOrderDetail(orderId: string): Promise<ReservationOrderDetailResult> {
|
|
return getJson<ReservationOrderDetailResult>(
|
|
withQuery(`/api/reservation/orders/${encodeURIComponent(orderId)}`, {
|
|
include_tasks: true,
|
|
include_source_summary: true,
|
|
}),
|
|
)
|
|
}
|
|
|
|
export async function fetchSourceMessageConversation(
|
|
sourceMessageId: string,
|
|
): Promise<SourceMessageConversationResult> {
|
|
throw new EndpointPendingError(
|
|
`GET /api/source-messages/${sourceMessageId}/conversation is pending backend implementation.`,
|
|
)
|
|
}
|
|
|
|
export async function fetchReservationTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
|
|
if (useReservationFixtures) {
|
|
return fixtureTaskDetail(taskId)
|
|
}
|
|
return getJson<ReservationTaskDetailResult>(`/api/reservation/tasks/${taskId}`)
|
|
}
|
|
|
|
export async function saveReservationTaskDraft(
|
|
taskId: string,
|
|
request: ReservationTaskPayloadMutationRequest,
|
|
): Promise<ReservationTaskPayloadMutationResult> {
|
|
if (useReservationFixtures) {
|
|
return fixtureTaskMutation(taskId, request, false)
|
|
}
|
|
return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/draft`, 'PUT', request)
|
|
}
|
|
|
|
export async function confirmReservationTask(
|
|
taskId: string,
|
|
request: ReservationTaskPayloadMutationRequest,
|
|
): Promise<ReservationTaskPayloadMutationResult> {
|
|
if (useReservationFixtures) {
|
|
return fixtureTaskMutation(taskId, request, true)
|
|
}
|
|
return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/confirm`, 'POST', request)
|
|
}
|
|
|
|
export async function executeReservationOperaOperation(
|
|
taskId: string,
|
|
operationId: string,
|
|
request: ReservationOperaSimulationRequest,
|
|
): Promise<ReservationOperaOperationResult> {
|
|
if (useReservationFixtures) {
|
|
return fixtureOperaOperation(taskId, operationId, request, 'execute')
|
|
}
|
|
return sendJson<ReservationOperaOperationResult>(
|
|
`/api/reservation/tasks/${taskId}/opera-operations/${operationId}/execute`,
|
|
'POST',
|
|
request,
|
|
)
|
|
}
|
|
|
|
export async function retryReservationOperaOperation(
|
|
taskId: string,
|
|
operationId: string,
|
|
request: ReservationOperaSimulationRequest,
|
|
): Promise<ReservationOperaOperationResult> {
|
|
if (useReservationFixtures) {
|
|
return fixtureOperaOperation(taskId, operationId, request, 'retry')
|
|
}
|
|
return sendJson<ReservationOperaOperationResult>(
|
|
`/api/reservation/tasks/${taskId}/opera-operations/${operationId}/retry`,
|
|
'POST',
|
|
request,
|
|
)
|
|
}
|
|
|
|
export async function fetchReservationTaskAudits(taskId: string): Promise<ReservationTaskAuditListResult> {
|
|
if (useReservationFixtures) {
|
|
return Promise.resolve(
|
|
reservationTaskAudits.find((audit) => audit.task_id === taskId) ?? {
|
|
task_id: taskId,
|
|
items: [],
|
|
},
|
|
)
|
|
}
|
|
return getJson<ReservationTaskAuditListResult>(`/api/reservation/tasks/${taskId}/audits`)
|
|
}
|
|
|
|
function withQuery(path: string, params: object): string {
|
|
const searchParams = new URLSearchParams()
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (
|
|
value === undefined ||
|
|
value === null ||
|
|
value === '' ||
|
|
!['string', 'number', 'boolean'].includes(typeof value)
|
|
) {
|
|
return
|
|
}
|
|
searchParams.set(key, String(value))
|
|
})
|
|
const query = searchParams.toString()
|
|
return query ? `${path}?${query}` : path
|
|
}
|
|
|
|
function fixtureTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
|
|
const detail = reservationTaskDetails.find((item) => item.task_id === taskId)
|
|
if (!detail) {
|
|
throw new Error(`Reservation task ${taskId} was not found in frontend fixture data.`)
|
|
}
|
|
return Promise.resolve(structuredClone(detail))
|
|
}
|
|
|
|
function fixtureTaskMutation(
|
|
taskId: string,
|
|
request: ReservationTaskPayloadMutationRequest,
|
|
confirmed: boolean,
|
|
): Promise<ReservationTaskPayloadMutationResult> {
|
|
return fixtureTaskDetail(taskId).then((detail) => ({
|
|
task_id: detail.task_id,
|
|
order_id: detail.order_id,
|
|
task_status: confirmed ? 'READY' : detail.task_status,
|
|
draft_payload: request.field_values,
|
|
confirmed_payload: confirmed ? request.field_values : detail.confirmed_payload,
|
|
opera_operations: confirmed ? detail.opera_operations : [],
|
|
}))
|
|
}
|
|
|
|
async function fixtureOperaOperation(
|
|
taskId: string,
|
|
operationId: string,
|
|
request: ReservationOperaSimulationRequest,
|
|
action: 'execute' | 'retry',
|
|
): Promise<ReservationOperaOperationResult> {
|
|
const detail = await fixtureTaskDetail(taskId)
|
|
const operation = detail.opera_operations.find((item) => item.operation_id === operationId)
|
|
if (!operation) {
|
|
throw new Error(`Reservation OPERA operation ${operationId} was not found in frontend fixture data.`)
|
|
}
|
|
|
|
const success = request.simulate_success !== false
|
|
const attemptNumber = operation.attempt_count + 1
|
|
return {
|
|
...operation,
|
|
operation_status: success ? 'SUCCEEDED' : 'FAILED',
|
|
attempt_count: attemptNumber,
|
|
last_error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
|
|
attempts: [
|
|
...operation.attempts,
|
|
{
|
|
attempt_id: `${operationId}-${action}-${attemptNumber}`,
|
|
attempt_number: attemptNumber,
|
|
attempt_status: success ? 'SUCCEEDED' : 'FAILED',
|
|
response_payload: success ? { simulated: true } : null,
|
|
error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
|
|
started_at: new Date().toISOString(),
|
|
finished_at: new Date().toISOString(),
|
|
},
|
|
],
|
|
}
|
|
}
|