142 lines
5.1 KiB
TypeScript
142 lines
5.1 KiB
TypeScript
import type { components } from './schema';
|
|
|
|
export type AuthSession = components['schemas']['AuthSession'];
|
|
export type ReportingPeriod = components['schemas']['ReportingPeriod'];
|
|
export type AnalyticsRun = components['schemas']['AnalyticsRun'];
|
|
export type SessionDetailPage = components['schemas']['SessionDetailPage'];
|
|
export type SessionDetail = components['schemas']['SessionDetail'];
|
|
export type Terminal = components['schemas']['Terminal'];
|
|
export type TerminalList = components['schemas']['TerminalList'];
|
|
export type SessionStatus = components['schemas']['SessionStatus'];
|
|
export type DetailView = components['schemas']['DetailView'];
|
|
export type FailureStage = components['schemas']['FailureStage'];
|
|
export type Problem = components['schemas']['Problem'];
|
|
|
|
const apiBase = (import.meta.env.PUBLIC_API_BASE_URL || '/api/v1').replace(/\/$/, '');
|
|
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
public status: number,
|
|
public problem?: Problem,
|
|
public retryAfter?: number,
|
|
) {
|
|
super(problem?.detail ?? 'در ارتباط با سرویس مشکلی پیش آمد.');
|
|
}
|
|
}
|
|
|
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
const response = await fetch(`${apiBase}${path}`, {
|
|
...init,
|
|
credentials: 'include',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
|
...init.headers,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const type = response.headers.get('content-type');
|
|
const problem = type?.includes('application/problem+json')
|
|
? ((await response.json()) as Problem)
|
|
: undefined;
|
|
throw new ApiError(response.status, problem, Number(response.headers.get('retry-after')) || undefined);
|
|
}
|
|
|
|
return response.status === 204 ? (undefined as T) : ((await response.json()) as T);
|
|
}
|
|
|
|
export const api = {
|
|
login(username: string, password: string) {
|
|
return request<AuthSession>('/auth/sessions', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
},
|
|
session() {
|
|
return request<AuthSession>('/auth/session');
|
|
},
|
|
logout(csrfToken: string) {
|
|
return request<void>('/auth/session', {
|
|
method: 'DELETE',
|
|
headers: { 'X-CSRF-Token': csrfToken },
|
|
});
|
|
},
|
|
periods() {
|
|
return request<components['schemas']['PeriodList']>('/analytics/periods');
|
|
},
|
|
terminals() {
|
|
return request<TerminalList>('/analytics/terminals');
|
|
},
|
|
startRun(periodId: string, csrfToken: string, terminalId?: string) {
|
|
return request<AnalyticsRun>('/analytics/runs', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-Token': csrfToken,
|
|
'Idempotency-Key': crypto.randomUUID(),
|
|
},
|
|
body: JSON.stringify({ periodId, ...(terminalId ? { terminalId } : {}) }),
|
|
});
|
|
},
|
|
run(runId: string) {
|
|
return request<AnalyticsRun>(`/analytics/runs/${encodeURIComponent(runId)}`);
|
|
},
|
|
retry(runId: string, sectionId: string, csrfToken: string) {
|
|
return request<AnalyticsRun>(
|
|
`/analytics/runs/${encodeURIComponent(runId)}/sections/${encodeURIComponent(sectionId)}/retry-attempts`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-Token': csrfToken,
|
|
'Idempotency-Key': crypto.randomUUID(),
|
|
},
|
|
},
|
|
);
|
|
},
|
|
details(
|
|
periodId: string,
|
|
view: DetailView,
|
|
options: {
|
|
cursor?: string;
|
|
failureStage?: FailureStage;
|
|
terminalId?: string;
|
|
status?: SessionStatus;
|
|
minAmountRial?: number;
|
|
maxAmountRial?: number;
|
|
limit?: number;
|
|
} = {},
|
|
) {
|
|
const query = new URLSearchParams({ periodId, view, limit: '25' });
|
|
if (options.cursor) query.set('cursor', options.cursor);
|
|
if (options.failureStage) query.set('failureStage', options.failureStage);
|
|
if (options.terminalId) query.set('terminalId', options.terminalId);
|
|
if (options.status) query.set('status', options.status);
|
|
if (options.minAmountRial !== undefined) query.set('minAmountRial', String(options.minAmountRial));
|
|
if (options.maxAmountRial !== undefined) query.set('maxAmountRial', String(options.maxAmountRial));
|
|
if (options.limit) query.set('limit', String(options.limit));
|
|
return request<SessionDetailPage>(`/analytics/session-details?${query}`);
|
|
},
|
|
sessionDetail(sessionId: string) {
|
|
return request<SessionDetail>(`/analytics/sessions/${encodeURIComponent(sessionId)}`);
|
|
},
|
|
exportUrl(
|
|
periodId: string,
|
|
view: DetailView,
|
|
options: {
|
|
failureStage?: FailureStage;
|
|
terminalId?: string;
|
|
status?: SessionStatus;
|
|
minAmountRial?: number;
|
|
maxAmountRial?: number;
|
|
} = {},
|
|
) {
|
|
const query = new URLSearchParams({ periodId, view });
|
|
if (options.failureStage) query.set('failureStage', options.failureStage);
|
|
if (options.terminalId) query.set('terminalId', options.terminalId);
|
|
if (options.status) query.set('status', options.status);
|
|
if (options.minAmountRial !== undefined) query.set('minAmountRial', String(options.minAmountRial));
|
|
if (options.maxAmountRial !== undefined) query.set('maxAmountRial', String(options.maxAmountRial));
|
|
return `${apiBase}/analytics/session-details.csv?${query}`;
|
|
},
|
|
};
|