feat(sdk-ts): add authentication fetch utils

This commit is contained in:
Ahmed Bouhuolia
2026-03-05 19:50:38 +02:00
parent 99ae7d7099
commit 4c059d610e
28 changed files with 670 additions and 108 deletions

View File

@@ -35933,6 +35933,31 @@
]
}
},
"/api/contacts/{id}": {
"get": {
"operationId": "ContactsController_getContact",
"summary": "Get contact by ID (customer or vendor)",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"description": "Contact ID",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "Contact details (under \"customer\" key for form/duplicate use)"
}
},
"tags": [
"Contacts"
]
}
},
"/api/contacts/auto-complete": {
"get": {
"operationId": "ContactsController_getAutoComplete",

View File

@@ -1,6 +1,6 @@
import type { ApiFetcher } from './fetch-utils';
import { paths } from './schema';
import { OpForPath, OpRequestBody, OpResponseBody } from './utils';
import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils';
export const BANK_RULES_ROUTES = {
RULES: '/api/banking/rules',
@@ -19,6 +19,7 @@ export const BANK_RULES_ROUTES = {
RECOGNIZED_LIST: '/api/banking/recognized',
PENDING: '/api/banking/pending',
UNCATEGORIZED_AUTOFILL: '/api/banking/uncategorized/autofill',
CATEGORIZE_BULK: '/api/banking/categorize/bulk',
} as const satisfies Record<string, keyof paths>;
export type BankRulesListResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.RULES, 'get'>>;
@@ -27,9 +28,36 @@ export type CreateBankRuleBody = OpRequestBody<OpForPath<typeof BANK_RULES_ROUTE
export type EditBankRuleBody = OpRequestBody<OpForPath<typeof BANK_RULES_ROUTES.RULE_BY_ID, 'put'>>;
export type CreateBankRuleResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.RULES, 'post'>>;
/** Path params for pause/resume bank account (id = bankAccountId). */
/** Path params for pause/resume/disconnect/refresh bank account (id = bankAccountId). */
export type PauseBankAccountParams = OpForPath<typeof BANK_RULES_ROUTES.ACCOUNTS_PAUSE, 'post'> extends { parameters: { path: infer P } } ? P : never;
export type ResumeBankAccountParams = OpForPath<typeof BANK_RULES_ROUTES.ACCOUNTS_RESUME, 'post'> extends { parameters: { path: infer P } } ? P : never;
export type DisconnectBankAccountParams = OpForPath<typeof BANK_RULES_ROUTES.ACCOUNTS_DISCONNECT, 'post'> extends { parameters: { path: infer P } } ? P : never;
export type RefreshBankAccountParams = OpForPath<typeof BANK_RULES_ROUTES.ACCOUNTS_REFRESH, 'post'> extends { parameters: { path: infer P } } ? P : never;
export type UnmatchMatchedTransactionParams = OpForPath<typeof BANK_RULES_ROUTES.MATCHING_UNMATCH, 'patch'> extends { parameters: { path: infer P } } ? P : never;
/** Response for GET /api/banking/matching/matched (from server GetMatchedTransactionsResponseDto). */
export type MatchedTransactionsResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.MATCHING_MATCHED, 'get'>>;
/** Query params for GET /api/banking/matching/matched. */
export type GetMatchedTransactionsQuery = OpQueryParams<OpForPath<typeof BANK_RULES_ROUTES.MATCHING_MATCHED, 'get'>>;
/** Body for POST /api/banking/matching/match (use referenceType, referenceId - camelCase). */
export type MatchTransactionBody = OpRequestBody<OpForPath<typeof BANK_RULES_ROUTES.MATCHING_MATCH, 'post'>>;
/** Body for PUT/DELETE /api/banking/exclude/bulk (from server ExcludeBankTransactionsBulkDto). */
export type ExcludeBankTransactionsBulkBody = OpRequestBody<OpForPath<typeof BANK_RULES_ROUTES.EXCLUDE_BULK, 'put'>>;
/** Query params for GET /api/banking/exclude. */
export type GetExcludedBankTransactionsQuery = OpQueryParams<OpForPath<typeof BANK_RULES_ROUTES.EXCLUDED_LIST, 'get'>>;
/** Query params for GET /api/banking/pending. */
export type GetPendingTransactionsQuery = OpQueryParams<OpForPath<typeof BANK_RULES_ROUTES.PENDING, 'get'>>;
/** Response for GET /api/banking/uncategorized/autofill (from server GetAutofillCategorizeTransactionResponseDto). */
export type AutofillCategorizeTransactionResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.UNCATEGORIZED_AUTOFILL, 'get'>>;
/** Response for GET /api/banking/recognized (single). */
export type RecognizedTransactionResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.RECOGNIZED, 'get'>>;
export async function fetchBankRules(fetcher: ApiFetcher): Promise<BankRulesListResponse> {
const get = fetcher.path(BANK_RULES_ROUTES.RULES).method('get').create();
@@ -115,28 +143,24 @@ export async function resumeBankAccount(
export async function fetchMatchedTransactions(
fetcher: ApiFetcher,
uncategorizedTransactionIds: number[]
): Promise<unknown> {
uncategorizedTransactionIds: number[],
query?: GetMatchedTransactionsQuery
): Promise<MatchedTransactionsResponse> {
const get = fetcher
.path(BANK_RULES_ROUTES.MATCHING_MATCHED)
.method('get')
.create();
const ids = uncategorizedTransactionIds.map(String);
const { data } = await get({ uncategorizedTransactionIds: ids });
const { data } = await get({ uncategorizedTransactionIds: ids, ...query });
return data;
}
export type MatchTransactionBody = {
uncategorizedTransactions: number[];
matchedTransactions: Array<{ reference_type: string; reference_id: number }>;
};
export async function matchTransaction(
fetcher: ApiFetcher,
body: MatchTransactionBody
): Promise<void> {
const post = fetcher.path(BANK_RULES_ROUTES.MATCHING_MATCH).method('post').create();
await (post as (body: unknown) => Promise<unknown>)(body);
await post(body);
}
export async function unmatchMatchedTransaction(
@@ -168,24 +192,24 @@ export async function unexcludeBankTransaction(
export async function excludeBankTransactionsBulk(
fetcher: ApiFetcher,
ids: Array<number | string>
body: ExcludeBankTransactionsBulkBody
): Promise<void> {
const put = fetcher.path(BANK_RULES_ROUTES.EXCLUDE_BULK).method('put').create();
await (put as (body?: { ids?: unknown[] }) => Promise<unknown>)({ ids });
await put(body);
}
export async function unexcludeBankTransactionsBulk(
fetcher: ApiFetcher,
ids: Array<number | string>
body: ExcludeBankTransactionsBulkBody
): Promise<void> {
const del = fetcher.path(BANK_RULES_ROUTES.EXCLUDE_BULK).method('delete').create();
await (del as (body?: { ids?: unknown[] }) => Promise<unknown>)({ ids });
await del(body);
}
export async function fetchRecognizedTransaction(
fetcher: ApiFetcher,
recognizedTransactionId: number
): Promise<unknown> {
): Promise<RecognizedTransactionResponse> {
const get = fetcher.path(BANK_RULES_ROUTES.RECOGNIZED).method('get').create();
const { data } = await get({ recognizedTransactionId });
return data;
@@ -196,44 +220,57 @@ export async function fetchRecognizedTransactions(
params?: Record<string, unknown>
): Promise<unknown> {
const get = fetcher.path(BANK_RULES_ROUTES.RECOGNIZED_LIST).method('get').create();
const { data } = await (get as (q?: Record<string, unknown>) => Promise<{ data: unknown }>)(
params ?? {}
);
const { data } = await get(params ?? {});
return data;
}
export async function fetchExcludedBankTransactions(
fetcher: ApiFetcher,
params?: Record<string, unknown>
params?: GetExcludedBankTransactionsQuery
): Promise<unknown> {
const get = fetcher.path(BANK_RULES_ROUTES.EXCLUDED_LIST).method('get').create();
const { data } = await (get as (q?: Record<string, unknown>) => Promise<{ data: unknown }>)(
params ?? {}
);
const { data } = await get(params ?? {});
return data;
}
export async function fetchPendingTransactions(
fetcher: ApiFetcher,
params?: Record<string, unknown>
params?: GetPendingTransactionsQuery
): Promise<unknown> {
const get = fetcher.path(BANK_RULES_ROUTES.PENDING).method('get').create();
const { data } = await (get as (q?: Record<string, unknown>) => Promise<{ data: unknown }>)(
params ?? {}
);
const { data } = await get(params ?? {});
return data;
}
export async function fetchAutofillCategorizeTransaction(
fetcher: ApiFetcher,
uncategorizedTransactionIds: number[]
): Promise<unknown> {
): Promise<AutofillCategorizeTransactionResponse> {
const get = fetcher
.path(BANK_RULES_ROUTES.UNCATEGORIZED_AUTOFILL)
.method('get')
.create();
const { data } = await (get as (q: unknown) => Promise<{ data: unknown }>)({
// Server expects uncategorizedTransactionIds (array). Schema types update after openapi regen.
const { data } = await get({
uncategorizedTransactionIds,
});
return data;
} as never);
return data as AutofillCategorizeTransactionResponse;
}
/**
* Uncategorize bank transactions in bulk (DELETE /api/banking/categorize/bulk with query uncategorizedTransactionIds).
*/
export async function uncategorizeTransactionsBulk(
fetcher: ApiFetcher,
uncategorizedTransactionIds: number[],
): Promise<void> {
const del = fetcher
.path(BANK_RULES_ROUTES.CATEGORIZE_BULK)
.method('delete')
.create();
await (del as (params: {
query?: { uncategorizedTransactionIds: number[] };
}) => Promise<unknown>)({
query: { uncategorizedTransactionIds },
});
}

View File

@@ -17,6 +17,11 @@ export type Bill = OpResponseBody<OpForPath<typeof BILLS_ROUTES.BY_ID, 'get'>>;
export type CreateBillBody = OpRequestBody<OpForPath<typeof BILLS_ROUTES.LIST, 'post'>>;
export type EditBillBody = OpRequestBody<OpForPath<typeof BILLS_ROUTES.BY_ID, 'put'>>;
export type GetBillsQuery = OpQueryParams<OpForPath<typeof BILLS_ROUTES.LIST, 'get'>>;
export type BulkDeleteBillsBody = OpRequestBody<OpForPath<typeof BILLS_ROUTES.BULK_DELETE, 'post'>>;
export type ValidateBulkDeleteBillsResponse = OpResponseBody<
OpForPath<typeof BILLS_ROUTES.VALIDATE_BULK_DELETE, 'post'>
>;
export type GetDueBillsQuery = OpQueryParams<OpForPath<typeof BILLS_ROUTES.DUE, 'get'>>;
export async function fetchBills(
fetcher: ApiFetcher,
@@ -56,3 +61,51 @@ export async function deleteBill(fetcher: ApiFetcher, id: number): Promise<void>
const del = fetcher.path(BILLS_ROUTES.BY_ID).method('delete').create();
await del({ id });
}
export async function openBill(fetcher: ApiFetcher, id: number): Promise<void> {
const patch = fetcher.path(BILLS_ROUTES.OPEN).method('patch').create();
await patch({ id });
}
export async function bulkDeleteBills(
fetcher: ApiFetcher,
body: BulkDeleteBillsBody
): Promise<void> {
const post = fetcher.path(BILLS_ROUTES.BULK_DELETE).method('post').create();
await post(body);
}
export async function validateBulkDeleteBills(
fetcher: ApiFetcher,
body: BulkDeleteBillsBody
): Promise<ValidateBulkDeleteBillsResponse> {
const post = fetcher
.path(BILLS_ROUTES.VALIDATE_BULK_DELETE)
.method('post')
.create();
const { data } = await post(body);
return data;
}
export async function fetchDueBills(
fetcher: ApiFetcher,
query?: GetDueBillsQuery
): Promise<unknown[]> {
const get = fetcher.path(BILLS_ROUTES.DUE).method('get').create();
const { data } = await (get as (params?: GetDueBillsQuery) => Promise<{ data: unknown }>)(
(query ?? {}) as GetDueBillsQuery
);
return Array.isArray(data) ? data : [];
}
export async function fetchBillPaymentTransactions(
fetcher: ApiFetcher,
id: number
): Promise<unknown[]> {
const get = fetcher
.path(BILLS_ROUTES.PAYMENT_TRANSACTIONS)
.method('get')
.create();
const { data } = await get({ id });
return (data as unknown[]) ?? [];
}

View File

@@ -9,6 +9,13 @@ export const BANKING_ACCOUNTS_ROUTES = {
export type BankingAccountsListResponse = OpResponseBody<OpForPath<typeof BANKING_ACCOUNTS_ROUTES.LIST, 'get'>>;
/** Bank account summary response (schema does not define response body). */
export interface BankingAccountSummaryResponse {
name: string;
totalUncategorizedTransactions: number;
totalRecognizedTransactions: number;
}
export async function fetchBankingAccounts(fetcher: ApiFetcher): Promise<BankingAccountsListResponse> {
const get = fetcher.path(BANKING_ACCOUNTS_ROUTES.LIST).method('get').create();
const { data } = await get({});
@@ -18,8 +25,8 @@ export async function fetchBankingAccounts(fetcher: ApiFetcher): Promise<Banking
export async function fetchBankingAccountSummary(
fetcher: ApiFetcher,
bankAccountId: number
): Promise<unknown> {
): Promise<BankingAccountSummaryResponse> {
const get = fetcher.path(BANKING_ACCOUNTS_ROUTES.SUMMARY).method('get').create();
const { data } = await get({ bankAccountId });
return data;
return data as BankingAccountSummaryResponse;
}

View File

@@ -2,14 +2,24 @@ import type { ApiFetcher } from './fetch-utils';
import { paths } from './schema';
import { OpForPath, OpResponseBody } from './utils';
export const CONTACTS_ROUTES = {
const CONTACTS_ROUTES = {
BY_ID: '/api/contacts/{id}',
AUTO_COMPLETE: '/api/contacts/auto-complete',
ACTIVATE: '/api/contacts/{id}/activate',
INACTIVATE: '/api/contacts/{id}/inactivate',
} as const satisfies Record<string, keyof paths>;
} as const;
export { CONTACTS_ROUTES };
export type ContactResponse = OpResponseBody<OpForPath<typeof CONTACTS_ROUTES.BY_ID, 'get'>>;
export type ContactsAutoCompleteResponse = OpResponseBody<OpForPath<typeof CONTACTS_ROUTES.AUTO_COMPLETE, 'get'>>;
export async function fetchContact(fetcher: ApiFetcher, id: number): Promise<ContactResponse> {
const get = fetcher.path(CONTACTS_ROUTES.BY_ID as keyof paths).method('get').create();
const { data } = await get({ id });
return data as ContactResponse;
}
export async function fetchContactsAutoComplete(fetcher: ApiFetcher): Promise<ContactsAutoCompleteResponse> {
const get = fetcher.path(CONTACTS_ROUTES.AUTO_COMPLETE).method('get').create();
const { data } = await get({});

View File

@@ -15,6 +15,12 @@ export type Expense = OpResponseBody<OpForPath<typeof EXPENSES_ROUTES.BY_ID, 'ge
export type CreateExpenseBody = OpRequestBody<OpForPath<typeof EXPENSES_ROUTES.LIST, 'post'>>;
export type EditExpenseBody = OpRequestBody<OpForPath<typeof EXPENSES_ROUTES.BY_ID, 'put'>>;
export type GetExpensesQuery = OpQueryParams<OpForPath<typeof EXPENSES_ROUTES.LIST, 'get'>>;
export type BulkDeleteExpensesBody = OpRequestBody<
OpForPath<typeof EXPENSES_ROUTES.BULK_DELETE, 'post'>
>;
export type ValidateBulkDeleteExpensesResponse = OpResponseBody<
OpForPath<typeof EXPENSES_ROUTES.VALIDATE_BULK_DELETE, 'post'>
>;
export async function fetchExpenses(
fetcher: ApiFetcher,
@@ -59,3 +65,23 @@ export async function publishExpense(fetcher: ApiFetcher, id: number): Promise<v
const post = fetcher.path(EXPENSES_ROUTES.PUBLISH).method('post').create();
await post({ id });
}
export async function bulkDeleteExpenses(
fetcher: ApiFetcher,
body: BulkDeleteExpensesBody
): Promise<void> {
const post = fetcher.path(EXPENSES_ROUTES.BULK_DELETE).method('post').create();
await post(body);
}
export async function validateBulkDeleteExpenses(
fetcher: ApiFetcher,
body: BulkDeleteExpensesBody
): Promise<ValidateBulkDeleteExpensesResponse> {
const post = fetcher
.path(EXPENSES_ROUTES.VALIDATE_BULK_DELETE)
.method('post')
.create();
const { data } = await post(body);
return data;
}

View File

@@ -4517,6 +4517,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/contacts/{id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Get contact by ID (customer or vendor) */
get: operations["ContactsController_getContact"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/contacts/auto-complete": {
parameters: {
query?: never;
@@ -40037,6 +40054,27 @@ export interface operations {
};
};
};
ContactsController_getContact: {
parameters: {
query?: never;
header?: never;
path: {
/** @description Contact ID */
id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Contact details (under "customer" key for form/duplicate use) */
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
ContactsController_getAutoComplete: {
parameters: {
query?: never;