diff --git a/shared/sdk-ts/openapi.json b/shared/sdk-ts/openapi.json index e88dbb6cb..f1b132381 100644 --- a/shared/sdk-ts/openapi.json +++ b/shared/sdk-ts/openapi.json @@ -62,6 +62,182 @@ ] } }, + "/api/auth/signin": { + "post": { + "operationId": "AuthController_signin", + "summary": "Sign in a user", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "password"], + "properties": { + "email": { "type": "string", "example": "user@example.com", "description": "User email address" }, + "password": { "type": "string", "example": "password123", "description": "User password" } + } + } + } + } + }, + "responses": { + "200": { + "description": "Sign-in successful", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { "type": "string", "description": "JWT access token" }, + "organizationId": { "type": "string", "description": "Organization ID" }, + "tenantId": { "type": "number", "description": "Tenant ID" }, + "userId": { "type": "number", "description": "User ID" } + } + } + } + } + } + }, + "tags": ["Auth"] + } + }, + "/api/auth/signup": { + "post": { + "operationId": "AuthController_signup", + "summary": "Sign up a new user", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["firstName", "lastName", "email", "password"], + "properties": { + "firstName": { "type": "string", "example": "John", "description": "User first name" }, + "lastName": { "type": "string", "example": "Doe", "description": "User last name" }, + "email": { "type": "string", "format": "email", "example": "john.doe@example.com", "description": "User email address" }, + "password": { "type": "string", "example": "password123", "description": "User password" } + } + } + } + } + }, + "responses": { + "201": { "description": "Sign-up initiated. Check email for confirmation." } + }, + "tags": ["Auth"] + } + }, + "/api/auth/signup/verify": { + "post": { + "operationId": "AuthController_signupConfirm", + "summary": "Confirm user signup", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email", "token"], + "properties": { + "email": { "type": "string", "example": "user@example.com", "description": "User email address" }, + "token": { "type": "string", "example": "confirmation-token", "description": "Signup confirmation token from email" } + } + } + } + } + }, + "responses": { + "200": { "description": "Signup confirmed successfully." } + }, + "tags": ["Auth"] + } + }, + "/api/auth/send_reset_password": { + "post": { + "operationId": "AuthController_sendResetPassword", + "summary": "Send reset password email", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["email"], + "properties": { + "email": { "type": "string", "example": "user@example.com", "description": "User email address to send reset link to" } + } + } + } + } + }, + "responses": { + "200": { "description": "Reset password email sent if the account exists." } + }, + "tags": ["Auth"] + } + }, + "/api/auth/reset_password/{token}": { + "post": { + "operationId": "AuthController_resetPassword", + "summary": "Reset password using token", + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "description": "Reset password token from email link", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["password"], + "properties": { + "password": { "type": "string", "example": "new-password", "description": "New password" } + } + } + } + } + }, + "responses": { + "200": { "description": "Password reset successfully." } + }, + "tags": ["Auth"] + } + }, + "/api/auth/meta": { + "get": { + "operationId": "AuthController_meta", + "summary": "Get auth metadata (e.g. signup disabled)", + "parameters": [], + "responses": { + "200": { + "description": "Auth metadata for the login/signup page", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "signupDisabled": { "type": "boolean", "description": "Whether signup is disabled" } + } + } + } + } + } + }, + "tags": ["Auth"] + } + }, "/api/api-keys/generate": { "post": { "operationId": "AuthApiKeysController_generate", diff --git a/shared/sdk-ts/src/accounts.ts b/shared/sdk-ts/src/accounts.ts index 238035b83..b2f7411a6 100644 --- a/shared/sdk-ts/src/accounts.ts +++ b/shared/sdk-ts/src/accounts.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const ACCOUNTS_ROUTES = { LIST: '/api/accounts', @@ -12,24 +13,15 @@ export const ACCOUNTS_ROUTES = { INACTIVATE: '/api/accounts/{id}/inactivate', } as const satisfies Record; -type GetAccounts = paths[typeof ACCOUNTS_ROUTES.LIST]['get']; -type GetAccount = paths[typeof ACCOUNTS_ROUTES.BY_ID]['get']; -type GetAccountTypes = paths[typeof ACCOUNTS_ROUTES.TYPES]['get']; -type GetAccountTransactions = paths[typeof ACCOUNTS_ROUTES.TRANSACTIONS]['get']; -type CreateAccount = paths[typeof ACCOUNTS_ROUTES.LIST]['post']; -type EditAccount = paths[typeof ACCOUNTS_ROUTES.BY_ID]['put']; -type BulkDeleteAccounts = paths[typeof ACCOUNTS_ROUTES.BULK_DELETE]['post']; -type ValidateBulkDelete = paths[typeof ACCOUNTS_ROUTES.VALIDATE_BULK_DELETE]['post']; - -export type AccountsList = GetAccounts['responses'][200]['content']['application/json']; -export type Account = GetAccount['responses'][200]['content']['application/json']; -export type AccountTypesList = GetAccountTypes['responses'][200]['content']['application/json']; -export type AccountTransactionsList = GetAccountTransactions['responses'][200]['content']['application/json']; -export type CreateAccountBody = CreateAccount['requestBody']['content']['application/json']; -export type EditAccountBody = EditAccount['requestBody']['content']['application/json']; -export type BulkDeleteBody = BulkDeleteAccounts['requestBody']['content']['application/json']; -export type ValidateBulkDeleteResponse = ValidateBulkDelete['responses'][200]['content']['application/json']; -export type GetAccountsQuery = NonNullable; +export type AccountsList = OpResponseBody>; +export type Account = OpResponseBody>; +export type AccountTypesList = OpResponseBody>; +export type AccountTransactionsList = OpResponseBody>; +export type CreateAccountBody = OpRequestBody>; +export type EditAccountBody = OpRequestBody>; +export type BulkDeleteBody = OpRequestBody>; +export type ValidateBulkDeleteResponse = OpResponseBody>; +export type GetAccountsQuery = OpQueryParams>; function normalizeAccountsResponse( data: AccountsList | { accounts: AccountsList } diff --git a/shared/sdk-ts/src/api-keys.ts b/shared/sdk-ts/src/api-keys.ts index 566655af0..7a8069a94 100644 --- a/shared/sdk-ts/src/api-keys.ts +++ b/shared/sdk-ts/src/api-keys.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const API_KEYS_ROUTES = { LIST: '/api/api-keys', @@ -7,12 +8,8 @@ export const API_KEYS_ROUTES = { REVOKE: '/api/api-keys/{id}/revoke', } as const satisfies Record; -type GetApiKeys = paths[typeof API_KEYS_ROUTES.LIST]['get']; -type GenerateApiKey = paths[typeof API_KEYS_ROUTES.GENERATE]['post']; -type RevokeApiKey = paths[typeof API_KEYS_ROUTES.REVOKE]['put']; - -export type ApiKeysList = GetApiKeys['responses'][200]['content']['application/json']; -export type GenerateApiKeyBody = GenerateApiKey['requestBody']['content']['application/json']; +export type ApiKeysList = OpResponseBody>; +export type GenerateApiKeyBody = OpRequestBody>; export async function fetchApiKeys(fetcher: ApiFetcher): Promise { const get = fetcher.path(API_KEYS_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/attachments.ts b/shared/sdk-ts/src/attachments.ts index bd3455a7c..6908ed159 100644 --- a/shared/sdk-ts/src/attachments.ts +++ b/shared/sdk-ts/src/attachments.ts @@ -1,11 +1,42 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; export const ATTACHMENTS_ROUTES = { + LIST: '/api/attachments', BY_ID: '/api/attachments/{id}', PRESIGNED_URL: '/api/attachments/{id}/presigned-url', } as const satisfies Record; +/** Response shape from POST /api/attachments (upload). Schema may not define it; server returns { data }. */ +export interface UploadAttachmentResponse { + id: number; + key: string; + mimeType: string; + originName: string; + size: number; + createdAt: string; +} + +/** + * Upload an attachment via multipart/form-data. Uses the fetcher's baseUrl and init (headers). + * The OpenAPI client may not support FormData, so we use fetch with the same config. + */ +export async function uploadAttachment( + fetcher: ApiFetcher, + formData: FormData +): Promise { + const post = fetcher.path(ATTACHMENTS_ROUTES.LIST).method('post').create(); + // Generated client expects typed body; FormData is valid for multipart/form-data at runtime + const res = await ( + post as unknown as (body: FormData) => Promise<{ data?: { data?: UploadAttachmentResponse } }> + )(formData); + const data = (res as { data?: { data?: UploadAttachmentResponse } })?.data?.data; + if (!data) { + throw new Error('Upload attachment: no data in response'); + } + return data; +} + export async function deleteAttachment(fetcher: ApiFetcher, id: string): Promise { const del = fetcher.path(ATTACHMENTS_ROUTES.BY_ID).method('delete').create(); await del({ id }); diff --git a/shared/sdk-ts/src/authentication.ts b/shared/sdk-ts/src/authentication.ts index 9898650c3..e027c0fac 100644 --- a/shared/sdk-ts/src/authentication.ts +++ b/shared/sdk-ts/src/authentication.ts @@ -1,18 +1,154 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; +// All auth routes (schema paths). After running generate:sdk-types, these match the OpenAPI spec. export const AUTH_ROUTES = { ACCOUNT: '/api/auth/account', RESEND_SIGNUP: '/api/auth/signup/verify/resend', + SIGNIN: '/api/auth/signin', + SIGNUP: '/api/auth/signup', + SIGNUP_VERIFY: '/api/auth/signup/verify', + SEND_RESET_PASSWORD: '/api/auth/send_reset_password', + RESET_PASSWORD: '/api/auth/reset_password/{token}', + META: '/api/auth/meta', } as const satisfies Record; -type GetAuthedAccount = paths[typeof AUTH_ROUTES.ACCOUNT]['get']; +// Schema-derived types (from paths/operations). No manual interfaces. +export type AuthedAccount = OpResponseBody< + OpForPath +>; -type GetAuthedAccount200 = GetAuthedAccount['responses'][200]; -export type AuthedAccount = GetAuthedAccount200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type ResendSignupConfirmBody = OpRequestBody< + OpForPath +>; -export async function fetchAuthedAccount(fetcher: ApiFetcher): Promise { +export type AuthSigninBody = OpRequestBody< + OpForPath +>; +export type AuthSigninResponse = OpResponseBody< + OpForPath +>; + +export type AuthSignupBody = OpRequestBody< + OpForPath +>; + +export type AuthSignupVerifyBody = OpRequestBody< + OpForPath +>; + +export type AuthSendResetPasswordBody = OpRequestBody< + OpForPath +>; + +export type AuthResetPasswordBody = OpRequestBody< + OpForPath +>; + +export type AuthMetaResponse = OpResponseBody< + OpForPath +>; + +/** + * Fetches the authenticated account (requires auth token). + */ +export async function fetchAuthedAccount( + fetcher: ApiFetcher +): Promise { const get = fetcher.path(AUTH_ROUTES.ACCOUNT).method('get').create(); const { data } = await get({}); + return data as AuthedAccount; +} + +/** + * Resends the signup confirmation message. + */ +export async function resendSignupConfirm( + fetcher: ApiFetcher, + body?: ResendSignupConfirmBody +): Promise { + const post = fetcher.path(AUTH_ROUTES.RESEND_SIGNUP).method('post').create(); + await post(body ?? ({} as ResendSignupConfirmBody)); +} + +/** + * Sign in with email and password. + */ +export async function signin( + fetcher: ApiFetcher, + body: AuthSigninBody +): Promise { + const post = fetcher.path(AUTH_ROUTES.SIGNIN).method('post').create(); + const { data } = await post(body as never); + return data as AuthSigninResponse; +} + +/** + * Sign up a new user. + */ +export async function signup( + fetcher: ApiFetcher, + body: AuthSignupBody +): Promise { + const post = fetcher.path(AUTH_ROUTES.SIGNUP).method('post').create(); + const { data } = await post(body as never); return data; } + +/** + * Confirm user signup with email and token. + */ +export async function signupConfirm( + fetcher: ApiFetcher, + body: AuthSignupVerifyBody +): Promise { + const post = fetcher + .path(AUTH_ROUTES.SIGNUP_VERIFY) + .method('post') + .create(); + const { data } = await post(body as never); + return data; +} + +/** + * Send reset password email. + */ +export async function sendResetPassword( + fetcher: ApiFetcher, + body: AuthSendResetPasswordBody +): Promise { + const post = fetcher + .path(AUTH_ROUTES.SEND_RESET_PASSWORD) + .method('post') + .create(); + const { data } = await post(body as never); + return data; +} + +/** + * Reset password using token (from email link). + */ +export async function resetPassword( + fetcher: ApiFetcher, + token: string, + body: AuthResetPasswordBody +): Promise { + const post = fetcher + .path(AUTH_ROUTES.RESET_PASSWORD) + .method('post') + .create(); + const { data } = await post({ token, ...(body as object) } as never); + return data; +} + +/** + * Get auth metadata (e.g. for login page). + */ +export async function fetchAuthMeta( + fetcher: ApiFetcher +): Promise { + const get = fetcher.path(AUTH_ROUTES.META).method('get').create(); + const { data } = await get({}); + return data as AuthMetaResponse; +} diff --git a/shared/sdk-ts/src/bank-rules.ts b/shared/sdk-ts/src/bank-rules.ts index b7c1c90b9..a728a93f9 100644 --- a/shared/sdk-ts/src/bank-rules.ts +++ b/shared/sdk-ts/src/bank-rules.ts @@ -1,11 +1,14 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const BANK_RULES_ROUTES = { RULES: '/api/banking/rules', RULE_BY_ID: '/api/banking/rules/{id}', ACCOUNTS_DISCONNECT: '/api/banking/accounts/{id}/disconnect', ACCOUNTS_REFRESH: '/api/banking/accounts/{id}/refresh', + ACCOUNTS_PAUSE: '/api/banking/accounts/{id}/pause', + ACCOUNTS_RESUME: '/api/banking/accounts/{id}/resume', MATCHING_MATCHED: '/api/banking/matching/matched', MATCHING_MATCH: '/api/banking/matching/match', MATCHING_UNMATCH: '/api/banking/matching/unmatch/{uncategorizedTransactionId}', @@ -18,33 +21,15 @@ export const BANK_RULES_ROUTES = { UNCATEGORIZED_AUTOFILL: '/api/banking/uncategorized/autofill', } as const satisfies Record; -type GetBankRules = paths[typeof BANK_RULES_ROUTES.RULES]['get']; -type GetBankRule = paths[typeof BANK_RULES_ROUTES.RULE_BY_ID]['get']; -type CreateBankRule = paths[typeof BANK_RULES_ROUTES.RULES]['post']; -type EditBankRule = paths[typeof BANK_RULES_ROUTES.RULE_BY_ID]['put']; -type DeleteBankRule = paths[typeof BANK_RULES_ROUTES.RULE_BY_ID]['delete']; +export type BankRulesListResponse = OpResponseBody>; +export type BankRuleResponse = OpResponseBody>; +export type CreateBankRuleBody = OpRequestBody>; +export type EditBankRuleBody = OpRequestBody>; +export type CreateBankRuleResponse = OpResponseBody>; -type GetBankRules200 = GetBankRules['responses'][200]; -type GetBankRule200 = GetBankRule['responses'][200]; -type CreateBankRule201 = CreateBankRule['responses'][201]; - -export type BankRulesListResponse = GetBankRules200 extends { - content?: { 'application/json': infer J }; -} - ? J - : unknown; -export type BankRuleResponse = GetBankRule200 extends { - content?: { 'application/json': infer J }; -} - ? J - : unknown; -export type CreateBankRuleBody = CreateBankRule['requestBody']['content']['application/json']; -export type EditBankRuleBody = EditBankRule['requestBody']['content']['application/json']; -export type CreateBankRuleResponse = CreateBankRule201 extends { - content?: { 'application/json': infer J }; -} - ? J - : unknown; +/** Path params for pause/resume bank account (id = bankAccountId). */ +export type PauseBankAccountParams = OpForPath extends { parameters: { path: infer P } } ? P : never; +export type ResumeBankAccountParams = OpForPath extends { parameters: { path: infer P } } ? P : never; export async function fetchBankRules(fetcher: ApiFetcher): Promise { const get = fetcher.path(BANK_RULES_ROUTES.RULES).method('get').create(); @@ -106,6 +91,28 @@ export async function refreshBankAccount( await post({ id }); } +export async function pauseBankAccount( + fetcher: ApiFetcher, + id: number +): Promise { + const post = fetcher + .path(BANK_RULES_ROUTES.ACCOUNTS_PAUSE) + .method('post') + .create(); + await post({ id }); +} + +export async function resumeBankAccount( + fetcher: ApiFetcher, + id: number +): Promise { + const post = fetcher + .path(BANK_RULES_ROUTES.ACCOUNTS_RESUME) + .method('post') + .create(); + await post({ id }); +} + export async function fetchMatchedTransactions( fetcher: ApiFetcher, uncategorizedTransactionIds: number[] diff --git a/shared/sdk-ts/src/bills.ts b/shared/sdk-ts/src/bills.ts index 9f5fe9830..14cc92f70 100644 --- a/shared/sdk-ts/src/bills.ts +++ b/shared/sdk-ts/src/bills.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const BILLS_ROUTES = { LIST: '/api/bills', @@ -11,22 +12,20 @@ export const BILLS_ROUTES = { BULK_DELETE: '/api/bills/bulk-delete', } as const satisfies Record; -type GetBills = paths[typeof BILLS_ROUTES.LIST]['get']; -type GetBill = paths[typeof BILLS_ROUTES.BY_ID]['get']; -type CreateBill = paths[typeof BILLS_ROUTES.LIST]['post']; -type EditBill = paths[typeof BILLS_ROUTES.BY_ID]['put']; -type DeleteBill = paths[typeof BILLS_ROUTES.BY_ID]['delete']; +export type BillsListResponse = OpResponseBody>; +export type Bill = OpResponseBody>; +export type CreateBillBody = OpRequestBody>; +export type EditBillBody = OpRequestBody>; +export type GetBillsQuery = OpQueryParams>; -export type BillsListResponse = GetBills['responses'][200]['content']['application/json']; -export type Bill = GetBill['responses'][200]['content']['application/json']; -export type CreateBillBody = CreateBill['requestBody']['content']['application/json']; -export type EditBillBody = EditBill['requestBody']['content']['application/json']; - -export async function fetchBills(fetcher: ApiFetcher): Promise { +export async function fetchBills( + fetcher: ApiFetcher, + query?: GetBillsQuery +): Promise { const get = fetcher.path(BILLS_ROUTES.LIST).method('get').create(); - // Schema incorrectly marks path.id on list endpoint; route has no {id} - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { data } = await (get as (params?: any) => Promise<{ data: BillsListResponse }>)({}); + const { data } = await ( + get as unknown as (params?: GetBillsQuery) => Promise<{ data: BillsListResponse }> + )(query ?? {}); return data; } diff --git a/shared/sdk-ts/src/branches.ts b/shared/sdk-ts/src/branches.ts index 5d2bd612e..fba219651 100644 --- a/shared/sdk-ts/src/branches.ts +++ b/shared/sdk-ts/src/branches.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const BRANCHES_ROUTES = { LIST: '/api/branches', @@ -8,16 +9,10 @@ export const BRANCHES_ROUTES = { MARK_AS_PRIMARY: '/api/branches/{id}/mark-as-primary', } as const satisfies Record; -type GetBranches = paths[typeof BRANCHES_ROUTES.LIST]['get']; -type GetBranch = paths[typeof BRANCHES_ROUTES.BY_ID]['get']; -type CreateBranch = paths[typeof BRANCHES_ROUTES.LIST]['post']; -type EditBranch = paths[typeof BRANCHES_ROUTES.BY_ID]['put']; -type DeleteBranch = paths[typeof BRANCHES_ROUTES.BY_ID]['delete']; - -export type BranchesListResponse = GetBranches['responses'][200]['content']['application/json']; -export type Branch = GetBranch['responses'][200]['content']['application/json']; -export type CreateBranchBody = CreateBranch['requestBody']['content']['application/json']; -export type EditBranchBody = EditBranch['requestBody']['content']['application/json']; +export type BranchesListResponse = OpResponseBody>; +export type Branch = OpResponseBody>; +export type CreateBranchBody = OpRequestBody>; +export type EditBranchBody = OpRequestBody>; export async function fetchBranches(fetcher: ApiFetcher): Promise { const get = fetcher.path(BRANCHES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/cashflow-accounts.ts b/shared/sdk-ts/src/cashflow-accounts.ts index 2de2f500e..12d53e205 100644 --- a/shared/sdk-ts/src/cashflow-accounts.ts +++ b/shared/sdk-ts/src/cashflow-accounts.ts @@ -1,15 +1,13 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const BANKING_ACCOUNTS_ROUTES = { LIST: '/api/banking/accounts', SUMMARY: '/api/banking/accounts/{bankAccountId}/summary', } as const satisfies Record; -type GetBankingAccounts = paths[typeof BANKING_ACCOUNTS_ROUTES.LIST]['get']; - -type GetBankingAccounts200 = GetBankingAccounts['responses'][200]; -export type BankingAccountsListResponse = GetBankingAccounts200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type BankingAccountsListResponse = OpResponseBody>; export async function fetchBankingAccounts(fetcher: ApiFetcher): Promise { const get = fetcher.path(BANKING_ACCOUNTS_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/contacts.ts b/shared/sdk-ts/src/contacts.ts index 0285c4e16..9d1c19d15 100644 --- a/shared/sdk-ts/src/contacts.ts +++ b/shared/sdk-ts/src/contacts.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const CONTACTS_ROUTES = { AUTO_COMPLETE: '/api/contacts/auto-complete', @@ -7,10 +8,7 @@ export const CONTACTS_ROUTES = { INACTIVATE: '/api/contacts/{id}/inactivate', } as const satisfies Record; -type AutoComplete = paths[typeof CONTACTS_ROUTES.AUTO_COMPLETE]['get']; - -type AutoComplete200 = AutoComplete['responses'][200]; -export type ContactsAutoCompleteResponse = AutoComplete200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type ContactsAutoCompleteResponse = OpResponseBody>; export async function fetchContactsAutoComplete(fetcher: ApiFetcher): Promise { const get = fetcher.path(CONTACTS_ROUTES.AUTO_COMPLETE).method('get').create(); diff --git a/shared/sdk-ts/src/credit-notes.ts b/shared/sdk-ts/src/credit-notes.ts index a4913b67d..dd3297efd 100644 --- a/shared/sdk-ts/src/credit-notes.ts +++ b/shared/sdk-ts/src/credit-notes.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const CREDIT_NOTES_ROUTES = { LIST: '/api/credit-notes', @@ -15,36 +16,25 @@ export const CREDIT_NOTES_ROUTES = { APPLIED_INVOICE_BY_ID: '/api/credit-notes/applied-invoices/{applyCreditToInvoicesId}', } as const satisfies Record; -type GetCreditNotes = paths[typeof CREDIT_NOTES_ROUTES.LIST]['get']; -type GetCreditNote = paths[typeof CREDIT_NOTES_ROUTES.BY_ID]['get']; -type GetCreditNoteState = paths[typeof CREDIT_NOTES_ROUTES.STATE]['get']; -type CreateCreditNote = paths[typeof CREDIT_NOTES_ROUTES.LIST]['post']; -type EditCreditNote = paths[typeof CREDIT_NOTES_ROUTES.BY_ID]['put']; -type DeleteCreditNote = paths[typeof CREDIT_NOTES_ROUTES.BY_ID]['delete']; -type OpenCreditNote = paths[typeof CREDIT_NOTES_ROUTES.OPEN]['put']; -type ValidateBulkDeleteCreditNotes = paths[typeof CREDIT_NOTES_ROUTES.VALIDATE_BULK_DELETE]['post']; -type BulkDeleteCreditNotes = paths[typeof CREDIT_NOTES_ROUTES.BULK_DELETE]['post']; -type GetCreditNoteRefunds = paths[typeof CREDIT_NOTES_ROUTES.REFUNDS]['get']; -type CreateRefundCreditNote = paths[typeof CREDIT_NOTES_ROUTES.REFUNDS]['post']; -type DeleteRefundCreditNote = paths[typeof CREDIT_NOTES_ROUTES.REFUND_BY_ID]['delete']; -type GetAppliedInvoices = paths[typeof CREDIT_NOTES_ROUTES.APPLIED_INVOICES]['get']; -type GetApplyInvoices = paths[typeof CREDIT_NOTES_ROUTES.APPLY_INVOICES]['get']; -type ApplyCreditNoteToInvoices = paths[typeof CREDIT_NOTES_ROUTES.APPLY_INVOICES]['post']; -type DeleteApplyCreditNoteToInvoices = paths[typeof CREDIT_NOTES_ROUTES.APPLIED_INVOICE_BY_ID]['delete']; +export type CreditNotesListResponse = OpResponseBody>; +export type CreditNote = OpResponseBody>; +export type CreateCreditNoteBody = OpRequestBody>; +export type EditCreditNoteBody = OpRequestBody>; +export type ValidateBulkDeleteCreditNotesBody = OpRequestBody>; +export type ValidateBulkDeleteCreditNotesResponse = OpResponseBody>; +export type BulkDeleteCreditNotesBody = OpRequestBody>; +export type CreateRefundCreditNoteBody = OpRequestBody>; +export type ApplyCreditNoteToInvoicesBody = OpRequestBody>; +export type GetCreditNotesQuery = OpQueryParams>; -export type CreditNotesListResponse = GetCreditNotes['responses'][200]['content']['application/json']; -export type CreditNote = GetCreditNote['responses'][200]['content']['application/json']; -export type CreateCreditNoteBody = CreateCreditNote['requestBody']['content']['application/json']; -export type EditCreditNoteBody = EditCreditNote['requestBody']['content']['application/json']; -export type ValidateBulkDeleteCreditNotesBody = ValidateBulkDeleteCreditNotes['requestBody']['content']['application/json']; -export type ValidateBulkDeleteCreditNotesResponse = ValidateBulkDeleteCreditNotes['responses'][200]['content']['application/json']; -export type BulkDeleteCreditNotesBody = BulkDeleteCreditNotes['requestBody']['content']['application/json']; -export type CreateRefundCreditNoteBody = CreateRefundCreditNote['requestBody']['content']['application/json']; -export type ApplyCreditNoteToInvoicesBody = ApplyCreditNoteToInvoices['requestBody']['content']['application/json']; - -export async function fetchCreditNotes(fetcher: ApiFetcher): Promise { +export async function fetchCreditNotes( + fetcher: ApiFetcher, + query?: GetCreditNotesQuery +): Promise { const getCreditNotes = fetcher.path(CREDIT_NOTES_ROUTES.LIST).method('get').create(); - const { data } = await getCreditNotes({}); + const { data } = await ( + getCreditNotes as (params: GetCreditNotesQuery) => Promise<{ data: CreditNotesListResponse }> + )(query ?? {}); return data; } diff --git a/shared/sdk-ts/src/currencies.ts b/shared/sdk-ts/src/currencies.ts index ff3b136b5..73c88096a 100644 --- a/shared/sdk-ts/src/currencies.ts +++ b/shared/sdk-ts/src/currencies.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const CURRENCIES_ROUTES = { LIST: '/api/currencies', @@ -8,18 +9,10 @@ export const CURRENCIES_ROUTES = { BY_CURRENCY_CODE: '/api/currencies/{currencyCode}', } as const satisfies Record; -type GetCurrencies = paths[typeof CURRENCIES_ROUTES.LIST]['get']; -type GetCurrencyByCode = paths[typeof CURRENCIES_ROUTES.BY_CURRENCY_CODE]['get']; -type CreateCurrency = paths[typeof CURRENCIES_ROUTES.LIST]['post']; -type EditCurrency = paths[typeof CURRENCIES_ROUTES.BY_ID]['put']; -type DeleteCurrency = paths[typeof CURRENCIES_ROUTES.BY_CODE]['delete']; - -type GetCurrencies200 = GetCurrencies['responses'][200]; -type GetCurrencyByCode200 = GetCurrencyByCode['responses'][200]; -export type CurrenciesListResponse = GetCurrencies200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type Currency = GetCurrencyByCode200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateCurrencyBody = CreateCurrency extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; -export type EditCurrencyBody = EditCurrency extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; +export type CurrenciesListResponse = OpResponseBody>; +export type Currency = OpResponseBody>; +export type CreateCurrencyBody = OpRequestBody>; +export type EditCurrencyBody = OpRequestBody>; export async function fetchCurrencies(fetcher: ApiFetcher): Promise { const get = fetcher.path(CURRENCIES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/customers.ts b/shared/sdk-ts/src/customers.ts index 114c4a6a6..394514c39 100644 --- a/shared/sdk-ts/src/customers.ts +++ b/shared/sdk-ts/src/customers.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const CUSTOMERS_ROUTES = { LIST: '/api/customers', @@ -9,24 +10,22 @@ export const CUSTOMERS_ROUTES = { BULK_DELETE: '/api/customers/bulk-delete', } as const satisfies Record; -type GetCustomers = paths[typeof CUSTOMERS_ROUTES.LIST]['get']; -type GetCustomer = paths[typeof CUSTOMERS_ROUTES.BY_ID]['get']; -type CreateCustomer = paths[typeof CUSTOMERS_ROUTES.LIST]['post']; -type EditCustomer = paths[typeof CUSTOMERS_ROUTES.BY_ID]['put']; -type DeleteCustomer = paths[typeof CUSTOMERS_ROUTES.BY_ID]['delete']; -type ValidateBulkDelete = paths[typeof CUSTOMERS_ROUTES.VALIDATE_BULK_DELETE]['post']; -type BulkDelete = paths[typeof CUSTOMERS_ROUTES.BULK_DELETE]['post']; +export type CustomersListResponse = OpResponseBody>; +export type Customer = OpResponseBody>; +export type CreateCustomerBody = OpRequestBody>; +export type EditCustomerBody = OpRequestBody>; +export type ValidateBulkDeleteCustomersResponse = OpResponseBody>; +export type BulkDeleteCustomersBody = OpRequestBody>; +export type GetCustomersQuery = OpQueryParams>; -export type CustomersListResponse = GetCustomers['responses'][200]['content']['application/json']; -export type Customer = GetCustomer['responses'][200]['content']['application/json']; -export type CreateCustomerBody = CreateCustomer['requestBody']['content']['application/json']; -export type EditCustomerBody = EditCustomer['requestBody']['content']['application/json']; -export type ValidateBulkDeleteCustomersResponse = ValidateBulkDelete['responses'][200]['content']['application/json']; -export type BulkDeleteCustomersBody = BulkDelete['requestBody']['content']['application/json']; - -export async function fetchCustomers(fetcher: ApiFetcher): Promise { +export async function fetchCustomers( + fetcher: ApiFetcher, + query?: GetCustomersQuery +): Promise { const get = fetcher.path(CUSTOMERS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params: GetCustomersQuery) => Promise<{ data: CustomersListResponse }>)( + query ?? {} + ); return data; } diff --git a/shared/sdk-ts/src/expenses.ts b/shared/sdk-ts/src/expenses.ts index 3b909e8df..e0d397d9a 100644 --- a/shared/sdk-ts/src/expenses.ts +++ b/shared/sdk-ts/src/expenses.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const EXPENSES_ROUTES = { LIST: '/api/expenses', @@ -9,22 +10,20 @@ export const EXPENSES_ROUTES = { BULK_DELETE: '/api/expenses/bulk-delete', } as const satisfies Record; -type GetExpenses = paths[typeof EXPENSES_ROUTES.LIST]['get']; -type GetExpense = paths[typeof EXPENSES_ROUTES.BY_ID]['get']; -type CreateExpense = paths[typeof EXPENSES_ROUTES.LIST]['post']; -type EditExpense = paths[typeof EXPENSES_ROUTES.BY_ID]['put']; -type DeleteExpense = paths[typeof EXPENSES_ROUTES.BY_ID]['delete']; +export type ExpensesListResponse = OpResponseBody>; +export type Expense = OpResponseBody>; +export type CreateExpenseBody = OpRequestBody>; +export type EditExpenseBody = OpRequestBody>; +export type GetExpensesQuery = OpQueryParams>; -type GetExpenses200 = GetExpenses['responses'][200]; -type GetExpense200 = GetExpense['responses'][200]; -export type ExpensesListResponse = GetExpenses200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type Expense = GetExpense200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateExpenseBody = CreateExpense['requestBody']['content']['application/json']; -export type EditExpenseBody = EditExpense['requestBody']['content']['application/json']; - -export async function fetchExpenses(fetcher: ApiFetcher): Promise { +export async function fetchExpenses( + fetcher: ApiFetcher, + query?: GetExpensesQuery +): Promise { const get = fetcher.path(EXPENSES_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params?: GetExpensesQuery) => Promise<{ data: ExpensesListResponse }>)( + query ?? {} + ); return data; } diff --git a/shared/sdk-ts/src/generic-resource.ts b/shared/sdk-ts/src/generic-resource.ts index 9bf25ebeb..ededf0ca6 100644 --- a/shared/sdk-ts/src/generic-resource.ts +++ b/shared/sdk-ts/src/generic-resource.ts @@ -1,14 +1,12 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const GENERIC_RESOURCE_ROUTES = { META: '/api/resources/{resourceModel}/meta', } as const satisfies Record; -type GetResourceMeta = paths[typeof GENERIC_RESOURCE_ROUTES.META]['get']; - -type GetResourceMeta200 = GetResourceMeta['responses'][200]; -export type ResourceMetaResponse = GetResourceMeta200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type ResourceMetaResponse = OpResponseBody>; export async function fetchResourceMeta( fetcher: ApiFetcher, diff --git a/shared/sdk-ts/src/index.ts b/shared/sdk-ts/src/index.ts index 0fc553ba4..ff1ceef9d 100644 --- a/shared/sdk-ts/src/index.ts +++ b/shared/sdk-ts/src/index.ts @@ -2,521 +2,48 @@ * Re-export OpenAPI-generated types for use by server and webapp. * Run `pnpm run generate:sdk-types` from repo root to regenerate schema from the server OpenAPI spec. */ -export type { paths, components, operations } from './schema'; -export type { ApiFetcher, CreateApiFetcherConfig } from './fetch-utils'; -export { normalizeApiPath, createApiFetcher } from './fetch-utils'; -export { - ACCOUNTS_ROUTES, - fetchAccounts, - fetchAccount, - fetchAccountTypes, - fetchAccountTransactions, - createAccount, - editAccount, - deleteAccount, - activateAccount, - inactivateAccount, - bulkDeleteAccounts, - validateBulkDeleteAccounts, -} from './accounts'; -export type { - AccountsList, - Account, - AccountTypesList, - AccountTransactionsList, - CreateAccountBody, - EditAccountBody, - BulkDeleteBody, - ValidateBulkDeleteResponse, - GetAccountsQuery, -} from './accounts'; -export { - CREDIT_NOTES_ROUTES, - fetchCreditNotes, - fetchCreditNote, - fetchCreditNoteState, - createCreditNote, - editCreditNote, - deleteCreditNote, - openCreditNote, - validateBulkDeleteCreditNotes, - bulkDeleteCreditNotes, - fetchCreditNoteRefunds, - createRefundCreditNote, - deleteRefundCreditNote, - fetchAppliedInvoices, - fetchCreditNoteAssociatedInvoicesToApply, - applyCreditNoteToInvoices, - deleteApplyCreditNoteToInvoices, -} from './credit-notes'; -export type { - CreditNotesListResponse, - CreditNote, - CreateCreditNoteBody, - EditCreditNoteBody, - ValidateBulkDeleteCreditNotesBody, - ValidateBulkDeleteCreditNotesResponse, - BulkDeleteCreditNotesBody, - CreateRefundCreditNoteBody, - ApplyCreditNoteToInvoicesBody, -} from './credit-notes'; -export { - API_KEYS_ROUTES, - fetchApiKeys, - generateApiKey, - revokeApiKey, -} from './api-keys'; -export type { ApiKeysList, GenerateApiKeyBody } from './api-keys'; -export { - SALE_INVOICES_ROUTES, - fetchSaleInvoices, - fetchSaleInvoice, - createSaleInvoice, - editSaleInvoice, - deleteSaleInvoice, -} from './sale-invoices'; -export type { - SaleInvoicesListResponse, - SaleInvoice, - CreateSaleInvoiceBody, - EditSaleInvoiceBody, -} from './sale-invoices'; -export { - CUSTOMERS_ROUTES, - fetchCustomers, - fetchCustomer, - createCustomer, - editCustomer, - deleteCustomer, - validateBulkDeleteCustomers, - bulkDeleteCustomers, -} from './customers'; -export type { - CustomersListResponse, - Customer, - CreateCustomerBody, - EditCustomerBody, - ValidateBulkDeleteCustomersResponse, - BulkDeleteCustomersBody, -} from './customers'; -export { - VENDORS_ROUTES, - fetchVendors, - fetchVendor, - createVendor, - editVendor, - deleteVendor, - validateBulkDeleteVendors, - bulkDeleteVendors, -} from './vendors'; -export type { - VendorsListResponse, - Vendor, - CreateVendorBody, - EditVendorBody, - ValidateBulkDeleteVendorsResponse, - BulkDeleteVendorsBody, -} from './vendors'; -export { - BILLS_ROUTES, - fetchBills, - fetchBill, - createBill, - editBill, - deleteBill, -} from './bills'; -export type { - BillsListResponse, - Bill, - CreateBillBody, - EditBillBody, -} from './bills'; -export { - ITEMS_ROUTES, - fetchItems, - fetchItem, - createItem, - editItem, - deleteItem, - inactivateItem, - activateItem, - validateBulkDeleteItems, - bulkDeleteItems, -} from './items'; -export type { - ItemsListResponse, - Item, - CreateItemBody, - EditItemBody, - BulkDeleteItemsBody, - ValidateBulkDeleteItemsResponse, -} from './items'; -export { - BRANCHES_ROUTES, - fetchBranches, - fetchBranch, - createBranch, - editBranch, - deleteBranch, - activateBranches, - markBranchAsPrimary, -} from './branches'; -export type { - BranchesListResponse, - Branch, - CreateBranchBody, - EditBranchBody, -} from './branches'; -export { - WAREHOUSES_ROUTES, - fetchWarehouses, - fetchWarehouse, - createWarehouse, - editWarehouse, - deleteWarehouse, - activateWarehouses, - markWarehousePrimary, -} from './warehouses'; -export type { - WarehousesListResponse, - Warehouse, - CreateWarehouseBody, - EditWarehouseBody, -} from './warehouses'; -export { - EXPENSES_ROUTES, - fetchExpenses, - fetchExpense, - createExpense, - editExpense, - deleteExpense, - publishExpense, -} from './expenses'; -export type { - ExpensesListResponse, - Expense, - CreateExpenseBody, - EditExpenseBody, -} from './expenses'; -export { - MANUAL_JOURNALS_ROUTES, - fetchManualJournals, - fetchManualJournal, - createManualJournal, - editManualJournal, - deleteManualJournal, - publishManualJournal, -} from './manual-journals'; -export type { - ManualJournalsListResponse, - ManualJournal, - CreateManualJournalBody, - EditManualJournalBody, -} from './manual-journals'; -export { - ROLES_ROUTES, - fetchRoles, - fetchRole, - createRole, - editRole, - deleteRole, - fetchRolePermissionsSchema, -} from './roles'; -export type { - RolesListResponse, - Role, - CreateRoleBody, - EditRoleBody, - RolePermissionsSchema, -} from './roles'; -export { - USERS_ROUTES, - fetchUsers, - fetchUser, - editUser, - deleteUser, - activateUser, - inactivateUser, -} from './users'; -export type { - UsersListResponse, - User, - EditUserBody, - GetUsersQuery, -} from './users'; -export { - SETTINGS_ROUTES, - fetchSettings, - saveSettings, -} from './settings'; -export type { SettingsResponse, SaveSettingsBody } from './settings'; -export { - ORGANIZATION_ROUTES, - fetchOrganizationCurrent, - fetchOrganization, - updateOrganization, -} from './organization'; -export type { - OrganizationCurrent, - Organization, - UpdateOrganizationBody, -} from './organization'; -export { - SUBSCRIPTION_ROUTES, - fetchSubscriptions, - cancelSubscription, - resumeSubscription, -} from './subscription'; -export type { SubscriptionsListResponse } from './subscription'; -export { - CURRENCIES_ROUTES, - fetchCurrencies, - fetchCurrency, - fetchCurrencyByCode, - createCurrency, - editCurrency, - deleteCurrency, -} from './currencies'; -export type { - CurrenciesListResponse, - Currency, - CreateCurrencyBody, - EditCurrencyBody, -} from './currencies'; -export { - TAX_RATES_ROUTES, - fetchTaxRates, - fetchTaxRate, - createTaxRate, - editTaxRate, - deleteTaxRate, - activateTaxRate, - inactivateTaxRate, -} from './tax-rates'; -export type { - TaxRatesListResponse, - TaxRate, - CreateTaxRateBody, - EditTaxRateBody, -} from './tax-rates'; -export { - ATTACHMENTS_ROUTES, - deleteAttachment, - fetchAttachmentPresignedUrl, -} from './attachments'; -export { - INVITE_ROUTES, - inviteUser, - resendInvite, - acceptInvite, - fetchInviteCheck, -} from './invite'; -export type { InviteUserBody, AcceptInviteBody } from './invite'; -export { - AUTH_ROUTES, - fetchAuthedAccount, -} from './authentication'; -export type { AuthedAccount } from './authentication'; -export { - CONTACTS_ROUTES, - fetchContactsAutoComplete, - activateContact, - inactivateContact, -} from './contacts'; -export type { ContactsAutoCompleteResponse } from './contacts'; -export { - ITEMS_CATEGORIES_ROUTES, - fetchItemCategories, - fetchItemCategory, - createItemCategory, - editItemCategory, - deleteItemCategory, -} from './items-categories'; -export type { - ItemCategoriesListResponse, - ItemCategory, - CreateItemCategoryBody, - EditItemCategoryBody, -} from './items-categories'; -export { - VIEWS_ROUTES, - fetchResourceView, -} from './views'; -export type { ResourceViewResponse } from './views'; -export { - TRANSACTIONS_LOCKING_ROUTES, - fetchTransactionsLocking, - fetchTransactionsLockingByModule, - lockTransactions, - cancelLockTransactions, - unlockPartialTransactions, - cancelUnlockPartialTransactions, -} from './transactions-locking'; -export type { TransactionsLockingListResponse } from './transactions-locking'; -export { - VENDOR_CREDITS_ROUTES, - fetchVendorCredits, - fetchVendorCredit, - createVendorCredit, - editVendorCredit, - deleteVendorCredit, - openVendorCredit, -} from './vendor-credits'; -export type { - VendorCreditsListResponse, - VendorCredit, - CreateVendorCreditBody, - EditVendorCreditBody, -} from './vendor-credits'; -export { - SALE_ESTIMATES_ROUTES, - fetchSaleEstimates, - fetchSaleEstimate, - createSaleEstimate, - editSaleEstimate, - deleteSaleEstimate, - bulkDeleteSaleEstimates, - validateBulkDeleteSaleEstimates, - deliverSaleEstimate, - approveSaleEstimate, - rejectSaleEstimate, - notifySaleEstimateBySms, - fetchSaleEstimateSmsDetails, - fetchSaleEstimateMail, - sendSaleEstimateMail, - fetchSaleEstimatesState, -} from './sale-estimates'; -export type { - SaleEstimatesListResponse, - SaleEstimate, - CreateSaleEstimateBody, - EditSaleEstimateBody, - BulkDeleteEstimatesBody, - ValidateBulkDeleteEstimatesResponse, -} from './sale-estimates'; -export { - SALE_RECEIPTS_ROUTES, - fetchSaleReceipts, - fetchSaleReceipt, - createSaleReceipt, - editSaleReceipt, - deleteSaleReceipt, -} from './sale-receipts'; -export type { - SaleReceiptsListResponse, - SaleReceipt, - CreateSaleReceiptBody, - EditSaleReceiptBody, -} from './sale-receipts'; -export { - PAYMENTS_RECEIVED_ROUTES, - fetchPaymentsReceived, - fetchPaymentReceived, - createPaymentReceived, - editPaymentReceived, - deletePaymentReceived, - bulkDeletePaymentsReceived, - validateBulkDeletePaymentsReceived, - fetchPaymentReceiveEditPage, - fetchPaymentReceiveMail, - sendPaymentReceiveMail, - fetchPaymentReceivedState, -} from './payment-receives'; -export type { - PaymentsReceivedListResponse, - PaymentReceived, - CreatePaymentReceivedBody, - EditPaymentReceivedBody, - BulkDeletePaymentsReceivedBody, - ValidateBulkDeletePaymentsReceivedResponse, -} from './payment-receives'; -export { - BILL_PAYMENTS_ROUTES, - fetchBillPayments, - fetchBillPayment, - createBillPayment, - editBillPayment, - deleteBillPayment, - fetchBillPaymentEditPage, -} from './payment-mades'; -export type { - BillPaymentsListResponse, - BillPayment, - CreateBillPaymentBody, - EditBillPaymentBody, -} from './payment-mades'; -export { - INVENTORY_ADJUSTMENTS_ROUTES, - fetchInventoryAdjustments, - fetchInventoryAdjustment, - createQuickInventoryAdjustment, - deleteInventoryAdjustment, - publishInventoryAdjustment, -} from './inventory-adjustments'; -export type { - InventoryAdjustmentsListResponse, - InventoryAdjustment, - CreateQuickInventoryAdjustmentBody, -} from './inventory-adjustments'; -export { - WAREHOUSE_TRANSFERS_ROUTES, - fetchWarehouseTransfers, - fetchWarehouseTransfer, - createWarehouseTransfer, - editWarehouseTransfer, - deleteWarehouseTransfer, - initiateWarehouseTransfer, - transferredWarehouseTransfer, -} from './warehouse-transfers'; -export type { - WarehouseTransfersListResponse, - WarehouseTransfer, - CreateWarehouseTransferBody, - EditWarehouseTransferBody, -} from './warehouse-transfers'; -export { - LANDED_COST_ROUTES, - fetchLandedCostTransactions, -} from './landed-cost'; -export type { LandedCostTransactionsResponse } from './landed-cost'; -export { - GENERIC_RESOURCE_ROUTES, - fetchResourceMeta, -} from './generic-resource'; -export type { ResourceMetaResponse } from './generic-resource'; -export { - BANKING_ACCOUNTS_ROUTES, - fetchBankingAccounts, - fetchBankingAccountSummary, -} from './cashflow-accounts'; -export type { BankingAccountsListResponse } from './cashflow-accounts'; -export { - BANK_RULES_ROUTES, - fetchBankRules, - fetchBankRule, - createBankRule, - editBankRule, - deleteBankRule, - disconnectBankAccount, - refreshBankAccount, - fetchMatchedTransactions, - matchTransaction, - unmatchMatchedTransaction, - excludeBankTransaction, - unexcludeBankTransaction, - excludeBankTransactionsBulk, - unexcludeBankTransactionsBulk, - fetchRecognizedTransaction, - fetchRecognizedTransactions, - fetchExcludedBankTransactions, - fetchPendingTransactions, - fetchAutofillCategorizeTransaction, -} from './bank-rules'; -export type { - BankRulesListResponse, - BankRuleResponse, - CreateBankRuleBody, - EditBankRuleBody, - CreateBankRuleResponse, - MatchTransactionBody, -} from './bank-rules'; +export type * from './schema'; +export * from './fetch-utils'; +export * from './accounts'; +export * from './credit-notes'; +export * from './api-keys'; +export * from './sale-invoices'; +export * from './customers'; +export * from './vendors'; +export * from './bills'; +export * from './items'; +export * from './branches'; +export * from './warehouses'; +export * from './expenses'; +export * from './manual-journals'; +export * from './roles'; +export * from './users'; +export * from './settings'; +export * from './organization'; +export * from './subscription'; +export * from './currencies'; +export * from './tax-rates'; +export * from './attachments'; +export * from './pdf-templates'; +export * from './invite'; +export * from './authentication'; +export * from './contacts'; +export * from './items-categories'; +export * from './views'; +export * from './transactions-locking'; +export * from './vendor-credits'; +export * from './sale-estimates'; +export * from './sale-receipts'; +export * from './payment-receives'; +export * from './payment-mades'; +export * from './inventory-adjustments'; +export * from './warehouse-transfers'; +export * from './landed-cost'; +export * from './generic-resource'; +export * from './cashflow-accounts'; +export * from './bank-rules'; + +/** + * Utility types for request/response types from schema paths. + */ +export * from './utils'; \ No newline at end of file diff --git a/shared/sdk-ts/src/inventory-adjustments.ts b/shared/sdk-ts/src/inventory-adjustments.ts index 2f9f29fd5..1d5890048 100644 --- a/shared/sdk-ts/src/inventory-adjustments.ts +++ b/shared/sdk-ts/src/inventory-adjustments.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const INVENTORY_ADJUSTMENTS_ROUTES = { LIST: '/api/inventory-adjustments', @@ -8,16 +9,9 @@ export const INVENTORY_ADJUSTMENTS_ROUTES = { PUBLISH: '/api/inventory-adjustments/{id}/publish', } as const satisfies Record; -type GetInventoryAdjustments = paths[typeof INVENTORY_ADJUSTMENTS_ROUTES.LIST]['get']; -type GetInventoryAdjustment = paths[typeof INVENTORY_ADJUSTMENTS_ROUTES.BY_ID]['get']; -type CreateQuick = paths[typeof INVENTORY_ADJUSTMENTS_ROUTES.QUICK]['post']; -type DeleteInventoryAdjustment = paths[typeof INVENTORY_ADJUSTMENTS_ROUTES.BY_ID]['delete']; - -type GetInventoryAdjustments200 = GetInventoryAdjustments['responses'][200]; -type GetInventoryAdjustment200 = GetInventoryAdjustment['responses'][200]; -export type InventoryAdjustmentsListResponse = GetInventoryAdjustments200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type InventoryAdjustment = GetInventoryAdjustment200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateQuickInventoryAdjustmentBody = CreateQuick['requestBody']['content']['application/json']; +export type InventoryAdjustmentsListResponse = OpResponseBody>; +export type InventoryAdjustment = OpResponseBody>; +export type CreateQuickInventoryAdjustmentBody = OpRequestBody>; export async function fetchInventoryAdjustments(fetcher: ApiFetcher): Promise { const get = fetcher.path(INVENTORY_ADJUSTMENTS_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/invite.ts b/shared/sdk-ts/src/invite.ts index 26c098e88..d4f9a4bf2 100644 --- a/shared/sdk-ts/src/invite.ts +++ b/shared/sdk-ts/src/invite.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody } from './utils'; export const INVITE_ROUTES = { INVITE: '/api/invite', @@ -8,13 +9,8 @@ export const INVITE_ROUTES = { CHECK: '/api/invite/check/{token}', } as const satisfies Record; -type InviteUser = paths[typeof INVITE_ROUTES.INVITE]['patch']; -type ResendInvite = paths[typeof INVITE_ROUTES.RESEND]['post']; -type AcceptInvite = paths[typeof INVITE_ROUTES.ACCEPT]['post']; -type CheckInvite = paths[typeof INVITE_ROUTES.CHECK]['get']; - -export type InviteUserBody = InviteUser['requestBody']['content']['application/json']; -export type AcceptInviteBody = AcceptInvite extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; +export type InviteUserBody = OpRequestBody>; +export type AcceptInviteBody = OpRequestBody>; export async function acceptInvite( fetcher: ApiFetcher, diff --git a/shared/sdk-ts/src/items-categories.ts b/shared/sdk-ts/src/items-categories.ts index c0f5d5ad5..c917b7e35 100644 --- a/shared/sdk-ts/src/items-categories.ts +++ b/shared/sdk-ts/src/items-categories.ts @@ -1,23 +1,16 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const ITEMS_CATEGORIES_ROUTES = { LIST: '/api/item-categories', BY_ID: '/api/item-categories/{id}', } as const satisfies Record; -type GetItemCategories = paths[typeof ITEMS_CATEGORIES_ROUTES.LIST]['get']; -type GetItemCategory = paths[typeof ITEMS_CATEGORIES_ROUTES.BY_ID]['get']; -type CreateItemCategory = paths[typeof ITEMS_CATEGORIES_ROUTES.LIST]['post']; -type EditItemCategory = paths[typeof ITEMS_CATEGORIES_ROUTES.BY_ID]['put']; -type DeleteItemCategory = paths[typeof ITEMS_CATEGORIES_ROUTES.BY_ID]['delete']; - -type GetItemCategories200 = GetItemCategories['responses'][200]; -type GetItemCategory200 = GetItemCategory['responses'][200]; -export type ItemCategoriesListResponse = GetItemCategories200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type ItemCategory = GetItemCategory200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateItemCategoryBody = CreateItemCategory['requestBody']['content']['application/json']; -export type EditItemCategoryBody = EditItemCategory['requestBody']['content']['application/json']; +export type ItemCategoriesListResponse = OpResponseBody>; +export type ItemCategory = OpResponseBody>; +export type CreateItemCategoryBody = OpRequestBody>; +export type EditItemCategoryBody = OpRequestBody>; export async function fetchItemCategories(fetcher: ApiFetcher): Promise { const get = fetcher.path(ITEMS_CATEGORIES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/items.ts b/shared/sdk-ts/src/items.ts index 2bf49bd26..8e7dbd077 100644 --- a/shared/sdk-ts/src/items.ts +++ b/shared/sdk-ts/src/items.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const ITEMS_ROUTES = { LIST: '/api/items', @@ -15,24 +16,22 @@ export const ITEMS_ROUTES = { WAREHOUSES: '/api/items/{id}/warehouses', } as const satisfies Record; -type GetItems = paths[typeof ITEMS_ROUTES.LIST]['get']; -type GetItem = paths[typeof ITEMS_ROUTES.BY_ID]['get']; -type CreateItem = paths[typeof ITEMS_ROUTES.LIST]['post']; -type EditItem = paths[typeof ITEMS_ROUTES.BY_ID]['put']; -type DeleteItem = paths[typeof ITEMS_ROUTES.BY_ID]['delete']; -type BulkDelete = paths[typeof ITEMS_ROUTES.BULK_DELETE]['post']; -type ValidateBulkDelete = paths[typeof ITEMS_ROUTES.VALIDATE_BULK_DELETE]['post']; +export type ItemsListResponse = OpResponseBody>; +export type Item = OpResponseBody>; +export type CreateItemBody = OpRequestBody>; +export type EditItemBody = OpRequestBody>; +export type BulkDeleteItemsBody = OpRequestBody>; +export type ValidateBulkDeleteItemsResponse = OpResponseBody>; +export type GetItemsQuery = OpQueryParams>; -export type ItemsListResponse = GetItems['responses'][200]['content']['application/json']; -export type Item = GetItem['responses'][200]['content']['application/json']; -export type CreateItemBody = CreateItem['requestBody']['content']['application/json']; -export type EditItemBody = EditItem['requestBody']['content']['application/json']; -export type BulkDeleteItemsBody = BulkDelete['requestBody']['content']['application/json']; -export type ValidateBulkDeleteItemsResponse = ValidateBulkDelete['responses'][200]['content']['application/json']; - -export async function fetchItems(fetcher: ApiFetcher): Promise { +export async function fetchItems( + fetcher: ApiFetcher, + query?: GetItemsQuery +): Promise { const get = fetcher.path(ITEMS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params?: GetItemsQuery) => Promise<{ data: ItemsListResponse }>)( + query ?? {} + ); return data; } @@ -90,3 +89,33 @@ export async function bulkDeleteItems( const post = fetcher.path(ITEMS_ROUTES.BULK_DELETE).method('post').create(); await post(body); } + +export async function fetchItemInvoices(fetcher: ApiFetcher, id: number): Promise { + const get = fetcher.path(ITEMS_ROUTES.INVOICES).method('get').create(); + const { data } = await get({ id }); + return (data as { data?: unknown[] })?.data ?? []; +} + +export async function fetchItemBills(fetcher: ApiFetcher, id: number): Promise { + const get = fetcher.path(ITEMS_ROUTES.BILLS).method('get').create(); + const { data } = await get({ id }); + return (data as { data?: unknown[] })?.data ?? []; +} + +export async function fetchItemEstimates(fetcher: ApiFetcher, id: number): Promise { + const get = fetcher.path(ITEMS_ROUTES.ESTIMATES).method('get').create(); + const { data } = await get({ id }); + return (data as { data?: unknown[] })?.data ?? []; +} + +export async function fetchItemReceipts(fetcher: ApiFetcher, id: number): Promise { + const get = fetcher.path(ITEMS_ROUTES.RECEIPTS).method('get').create(); + const { data } = await get({ id }); + return (data as { data?: unknown[] })?.data ?? []; +} + +export async function fetchItemWarehouses(fetcher: ApiFetcher, id: number): Promise { + const get = fetcher.path(ITEMS_ROUTES.WAREHOUSES).method('get').create(); + const { data } = await get({ id }); + return (data as { item_warehouses?: unknown[] })?.item_warehouses ?? []; +} diff --git a/shared/sdk-ts/src/landed-cost.ts b/shared/sdk-ts/src/landed-cost.ts index 84a137f28..32790bfe5 100644 --- a/shared/sdk-ts/src/landed-cost.ts +++ b/shared/sdk-ts/src/landed-cost.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpResponseBody } from './utils'; export const LANDED_COST_ROUTES = { TRANSACTIONS: '/api/landed-cost/transactions', @@ -8,13 +9,16 @@ export const LANDED_COST_ROUTES = { BILL_TRANSACTIONS: '/api/landed-cost/bills/{billId}/transactions', } as const satisfies Record; -type GetLandedCostTransactions = paths[typeof LANDED_COST_ROUTES.TRANSACTIONS]['get']; +export type LandedCostTransactionsResponse = OpResponseBody>; +export type GetLandedCostTransactionsQuery = OpQueryParams>; -type GetLandedCostTransactions200 = GetLandedCostTransactions['responses'][200]; -export type LandedCostTransactionsResponse = GetLandedCostTransactions200 extends { content?: { 'application/json': infer J } } ? J : unknown; - -export async function fetchLandedCostTransactions(fetcher: ApiFetcher): Promise { +export async function fetchLandedCostTransactions( + fetcher: ApiFetcher, + query?: GetLandedCostTransactionsQuery +): Promise { const get = fetcher.path(LANDED_COST_ROUTES.TRANSACTIONS).method('get').create(); - const { data } = await get({}); + const { data } = await ( + get as (params?: GetLandedCostTransactionsQuery) => Promise<{ data: LandedCostTransactionsResponse }> + )(query ?? {}); return data; } diff --git a/shared/sdk-ts/src/manual-journals.ts b/shared/sdk-ts/src/manual-journals.ts index e81991425..765c5063f 100644 --- a/shared/sdk-ts/src/manual-journals.ts +++ b/shared/sdk-ts/src/manual-journals.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const MANUAL_JOURNALS_ROUTES = { LIST: '/api/manual-journals', @@ -9,22 +10,22 @@ export const MANUAL_JOURNALS_ROUTES = { BULK_DELETE: '/api/manual-journals/bulk-delete', } as const satisfies Record; -type GetManualJournals = paths[typeof MANUAL_JOURNALS_ROUTES.LIST]['get']; -type GetManualJournal = paths[typeof MANUAL_JOURNALS_ROUTES.BY_ID]['get']; -type CreateManualJournal = paths[typeof MANUAL_JOURNALS_ROUTES.LIST]['post']; -type EditManualJournal = paths[typeof MANUAL_JOURNALS_ROUTES.BY_ID]['put']; -type DeleteManualJournal = paths[typeof MANUAL_JOURNALS_ROUTES.BY_ID]['delete']; +export type ManualJournalsListResponse = OpResponseBody>; +export type ManualJournal = OpResponseBody>; +export type CreateManualJournalBody = OpRequestBody>; +export type EditManualJournalBody = OpRequestBody>; +export type BulkDeleteManualJournalsBody = OpRequestBody>; +export type ValidateBulkDeleteManualJournalsResponse = OpResponseBody>; +export type ManualJournalsListQuery = OpQueryParams>; -type GetManualJournals200 = GetManualJournals['responses'][200]; -type GetManualJournal200 = GetManualJournal['responses'][200]; -export type ManualJournalsListResponse = GetManualJournals200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type ManualJournal = GetManualJournal200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateManualJournalBody = CreateManualJournal['requestBody']['content']['application/json']; -export type EditManualJournalBody = EditManualJournal['requestBody']['content']['application/json']; - -export async function fetchManualJournals(fetcher: ApiFetcher): Promise { +export async function fetchManualJournals( + fetcher: ApiFetcher, + query?: ManualJournalsListQuery +): Promise { const get = fetcher.path(MANUAL_JOURNALS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await ( + get as unknown as (params?: ManualJournalsListQuery) => Promise<{ data: ManualJournalsListResponse }> + )(query ?? {}); return data; } @@ -60,3 +61,20 @@ export async function publishManualJournal(fetcher: ApiFetcher, id: number): Pro const patch = fetcher.path(MANUAL_JOURNALS_ROUTES.PUBLISH).method('patch').create(); await patch({ id }); } + +export async function bulkDeleteManualJournals( + fetcher: ApiFetcher, + body: BulkDeleteManualJournalsBody +): Promise { + const post = fetcher.path(MANUAL_JOURNALS_ROUTES.BULK_DELETE).method('post').create(); + await post({ ids: body.ids, skipUndeletable: body.skipUndeletable ?? false }); +} + +export async function validateBulkDeleteManualJournals( + fetcher: ApiFetcher, + body: { ids: number[] } +): Promise { + const post = fetcher.path(MANUAL_JOURNALS_ROUTES.VALIDATE_BULK_DELETE).method('post').create(); + const { data } = await post({ ids: body.ids, skipUndeletable: false }); + return data; +} diff --git a/shared/sdk-ts/src/organization.ts b/shared/sdk-ts/src/organization.ts index 14cea5317..997352837 100644 --- a/shared/sdk-ts/src/organization.ts +++ b/shared/sdk-ts/src/organization.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const ORGANIZATION_ROUTES = { CURRENT: '/api/organization/current', @@ -9,12 +10,8 @@ export const ORGANIZATION_ROUTES = { UPDATE: '/api/organization', } as const satisfies Record; -type GetCurrent = paths[typeof ORGANIZATION_ROUTES.CURRENT]['get']; -type UpdateOrganization = paths[typeof ORGANIZATION_ROUTES.UPDATE]['put']; - -type GetCurrent200 = GetCurrent['responses'][200]; -export type OrganizationCurrent = GetCurrent200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type UpdateOrganizationBody = UpdateOrganization['requestBody']['content']['application/json']; +export type OrganizationCurrent = OpResponseBody>; +export type UpdateOrganizationBody = OpRequestBody>; export async function fetchOrganizationCurrent(fetcher: ApiFetcher): Promise { const get = fetcher.path(ORGANIZATION_ROUTES.CURRENT).method('get').create(); diff --git a/shared/sdk-ts/src/payment-mades.ts b/shared/sdk-ts/src/payment-mades.ts index 63c524586..b1ae0a703 100644 --- a/shared/sdk-ts/src/payment-mades.ts +++ b/shared/sdk-ts/src/payment-mades.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const BILL_PAYMENTS_ROUTES = { LIST: '/api/bill-payments', @@ -9,18 +10,10 @@ export const BILL_PAYMENTS_ROUTES = { EDIT_PAGE: '/api/bill-payments/{billPaymentId}/edit-page', } as const satisfies Record; -type GetBillPayments = paths[typeof BILL_PAYMENTS_ROUTES.LIST]['get']; -type GetBillPayment = paths[typeof BILL_PAYMENTS_ROUTES.BY_ID]['get']; -type CreateBillPayment = paths[typeof BILL_PAYMENTS_ROUTES.LIST]['post']; -type EditBillPayment = paths[typeof BILL_PAYMENTS_ROUTES.BY_ID]['put']; -type DeleteBillPayment = paths[typeof BILL_PAYMENTS_ROUTES.BY_ID]['delete']; - -type GetBillPayments200 = GetBillPayments['responses'][200]; -type GetBillPayment200 = GetBillPayment['responses'][200]; -export type BillPaymentsListResponse = GetBillPayments200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type BillPayment = GetBillPayment200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateBillPaymentBody = CreateBillPayment['requestBody']['content']['application/json']; -export type EditBillPaymentBody = EditBillPayment['requestBody']['content']['application/json']; +export type BillPaymentsListResponse = OpResponseBody>; +export type BillPayment = OpResponseBody>; +export type CreateBillPaymentBody = OpRequestBody>; +export type EditBillPaymentBody = OpRequestBody>; export async function fetchBillPayments(fetcher: ApiFetcher): Promise { const get = fetcher.path(BILL_PAYMENTS_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/payment-receives.ts b/shared/sdk-ts/src/payment-receives.ts index a2bc937ae..1664d6979 100644 --- a/shared/sdk-ts/src/payment-receives.ts +++ b/shared/sdk-ts/src/payment-receives.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const PAYMENTS_RECEIVED_ROUTES = { LIST: '/api/payments-received', @@ -11,18 +12,10 @@ export const PAYMENTS_RECEIVED_ROUTES = { MAIL: '/api/payments-received/{id}/mail', } as const satisfies Record; -type GetPaymentsReceived = paths[typeof PAYMENTS_RECEIVED_ROUTES.LIST]['get']; -type GetPaymentReceived = paths[typeof PAYMENTS_RECEIVED_ROUTES.BY_ID]['get']; -type CreatePaymentReceived = paths[typeof PAYMENTS_RECEIVED_ROUTES.LIST]['post']; -type EditPaymentReceived = paths[typeof PAYMENTS_RECEIVED_ROUTES.BY_ID]['put']; -type DeletePaymentReceived = paths[typeof PAYMENTS_RECEIVED_ROUTES.BY_ID]['delete']; - -type GetPaymentsReceived200 = GetPaymentsReceived['responses'][200]; -type GetPaymentReceived200 = GetPaymentReceived['responses'][200]; -export type PaymentsReceivedListResponse = GetPaymentsReceived200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type PaymentReceived = GetPaymentReceived200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreatePaymentReceivedBody = CreatePaymentReceived['requestBody']['content']['application/json']; -export type EditPaymentReceivedBody = EditPaymentReceived['requestBody']['content']['application/json']; +export type PaymentsReceivedListResponse = OpResponseBody>; +export type PaymentReceived = OpResponseBody>; +export type CreatePaymentReceivedBody = OpRequestBody>; +export type EditPaymentReceivedBody = OpRequestBody>; export async function fetchPaymentsReceived(fetcher: ApiFetcher): Promise { const get = fetcher.path(PAYMENTS_RECEIVED_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/pdf-templates.ts b/shared/sdk-ts/src/pdf-templates.ts new file mode 100644 index 000000000..c80358191 --- /dev/null +++ b/shared/sdk-ts/src/pdf-templates.ts @@ -0,0 +1,106 @@ +import type { ApiFetcher } from './fetch-utils'; +import type { paths } from './schema'; + +export const PDF_TEMPLATES_ROUTES = { + LIST: '/api/pdf-templates', + BY_ID: '/api/pdf-templates/{id}', + STATE: '/api/pdf-templates/state', + ASSIGN_DEFAULT: '/api/pdf-templates/{id}/assign-default', +} as const satisfies Record; + +// Schema does not define request/response content for PDF template operations; use explicit types. +export interface CreatePdfTemplateBody { + templateName: string; + resource: string; + attributes: Record; +} + +export interface EditPdfTemplateBody { + templateName: string; + attributes: Record; +} + +export interface PdfTemplateResponse { + templateName: string; + companyLogoUri?: string | null; + attributes: Record; + predefined: boolean; + default: boolean; + createdAt: string; + updatedAt: string | null; +} + +export interface PdfTemplatesListResponse { + templates?: PdfTemplateResponse[]; + [key: string]: unknown; +} + +export interface PdfTemplateBrandingStateResponse { + companyName: string; + companyAddress: string; + companyLogoUri: string; + companyLogoKey: string; + primaryColor: string; +} + +export interface GetPdfTemplatesQuery { + resource?: string; +} + +export async function fetchPdfTemplates( + fetcher: ApiFetcher, + query?: GetPdfTemplatesQuery +): Promise { + const get = fetcher.path(PDF_TEMPLATES_ROUTES.LIST).method('get').create(); + const { data } = await (get as (params?: GetPdfTemplatesQuery) => Promise<{ data: PdfTemplatesListResponse }>)( + query ?? {} + ); + return data; +} + +export async function fetchPdfTemplate( + fetcher: ApiFetcher, + id: number +): Promise { + const get = fetcher.path(PDF_TEMPLATES_ROUTES.BY_ID).method('get').create(); + const { data } = await (get as (params: { id: number }) => Promise<{ data: PdfTemplateResponse }>)({ id }); + return data; +} + +export async function createPdfTemplate( + fetcher: ApiFetcher, + values: CreatePdfTemplateBody +): Promise { + const post = fetcher.path(PDF_TEMPLATES_ROUTES.LIST).method('post').create(); + await (post as (body: CreatePdfTemplateBody) => Promise)(values); +} + +export async function editPdfTemplate( + fetcher: ApiFetcher, + id: number, + values: EditPdfTemplateBody +): Promise { + const put = fetcher.path(PDF_TEMPLATES_ROUTES.BY_ID).method('put').create(); + await (put as (params: { id: number } & EditPdfTemplateBody) => Promise)({ id, ...values }); +} + +export async function deletePdfTemplate(fetcher: ApiFetcher, id: number): Promise { + const del = fetcher.path(PDF_TEMPLATES_ROUTES.BY_ID).method('delete').create(); + await del({ id }); +} + +export async function assignPdfTemplateAsDefault( + fetcher: ApiFetcher, + id: number +): Promise { + const put = fetcher.path(PDF_TEMPLATES_ROUTES.ASSIGN_DEFAULT).method('put').create(); + await put({ id }); +} + +export async function fetchPdfTemplateBrandingState( + fetcher: ApiFetcher +): Promise { + const get = fetcher.path(PDF_TEMPLATES_ROUTES.STATE).method('get').create(); + const { data } = await (get as (params?: object) => Promise<{ data: PdfTemplateBrandingStateResponse }>)({}); + return data; +} diff --git a/shared/sdk-ts/src/roles.ts b/shared/sdk-ts/src/roles.ts index 70d1df0b4..22c61efec 100644 --- a/shared/sdk-ts/src/roles.ts +++ b/shared/sdk-ts/src/roles.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const ROLES_ROUTES = { LIST: '/api/roles', @@ -7,21 +8,11 @@ export const ROLES_ROUTES = { PERMISSIONS_SCHEMA: '/api/roles/permissions/schema', } as const satisfies Record; -type GetRoles = paths[typeof ROLES_ROUTES.LIST]['get']; -type GetRole = paths[typeof ROLES_ROUTES.BY_ID]['get']; -type CreateRole = paths[typeof ROLES_ROUTES.LIST]['post']; -type EditRole = paths[typeof ROLES_ROUTES.BY_ID]['put']; -type DeleteRole = paths[typeof ROLES_ROUTES.BY_ID]['delete']; -type GetPermissionsSchema = paths[typeof ROLES_ROUTES.PERMISSIONS_SCHEMA]['get']; - -type GetRoles200 = GetRoles['responses'][200]; -type GetRole200 = GetRole['responses'][200]; -type GetPermissionsSchema200 = GetPermissionsSchema['responses'][200]; -export type RolesListResponse = GetRoles200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type Role = GetRole200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateRoleBody = CreateRole['requestBody']['content']['application/json']; -export type EditRoleBody = EditRole['requestBody']['content']['application/json']; -export type RolePermissionsSchema = GetPermissionsSchema200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type RolesListResponse = OpResponseBody>; +export type Role = OpResponseBody>; +export type CreateRoleBody = OpRequestBody>; +export type EditRoleBody = OpRequestBody>; +export type RolePermissionsSchema = OpResponseBody>; export async function fetchRoles(fetcher: ApiFetcher): Promise { const get = fetcher.path(ROLES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/sale-estimates.ts b/shared/sdk-ts/src/sale-estimates.ts index 9f85d0802..099948cf9 100644 --- a/shared/sdk-ts/src/sale-estimates.ts +++ b/shared/sdk-ts/src/sale-estimates.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const SALE_ESTIMATES_ROUTES = { LIST: '/api/sale-estimates', @@ -15,18 +16,10 @@ export const SALE_ESTIMATES_ROUTES = { MAIL: '/api/sale-estimates/{id}/mail', } as const satisfies Record; -type GetSaleEstimates = paths[typeof SALE_ESTIMATES_ROUTES.LIST]['get']; -type GetSaleEstimate = paths[typeof SALE_ESTIMATES_ROUTES.BY_ID]['get']; -type CreateSaleEstimate = paths[typeof SALE_ESTIMATES_ROUTES.LIST]['post']; -type EditSaleEstimate = paths[typeof SALE_ESTIMATES_ROUTES.BY_ID]['put']; -type DeleteSaleEstimate = paths[typeof SALE_ESTIMATES_ROUTES.BY_ID]['delete']; - -type GetSaleEstimates200 = GetSaleEstimates['responses'][200]; -type GetSaleEstimate200 = GetSaleEstimate['responses'][200]; -export type SaleEstimatesListResponse = GetSaleEstimates200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type SaleEstimate = GetSaleEstimate200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateSaleEstimateBody = CreateSaleEstimate['requestBody']['content']['application/json']; -export type EditSaleEstimateBody = EditSaleEstimate['requestBody']['content']['application/json']; +export type SaleEstimatesListResponse = OpResponseBody>; +export type SaleEstimate = OpResponseBody>; +export type CreateSaleEstimateBody = OpRequestBody>; +export type EditSaleEstimateBody = OpRequestBody>; export async function fetchSaleEstimates(fetcher: ApiFetcher): Promise { const get = fetcher.path(SALE_ESTIMATES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/sale-invoices.ts b/shared/sdk-ts/src/sale-invoices.ts index 24c67c769..d29c5f04e 100644 --- a/shared/sdk-ts/src/sale-invoices.ts +++ b/shared/sdk-ts/src/sale-invoices.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const SALE_INVOICES_ROUTES = { LIST: '/api/sale-invoices', @@ -17,20 +18,23 @@ export const SALE_INVOICES_ROUTES = { BULK_DELETE: '/api/sale-invoices/bulk-delete', } as const satisfies Record; -type GetSaleInvoices = paths[typeof SALE_INVOICES_ROUTES.LIST]['get']; -type GetSaleInvoice = paths[typeof SALE_INVOICES_ROUTES.BY_ID]['get']; -type CreateSaleInvoice = paths[typeof SALE_INVOICES_ROUTES.LIST]['post']; -type EditSaleInvoice = paths[typeof SALE_INVOICES_ROUTES.BY_ID]['put']; -type DeleteSaleInvoice = paths[typeof SALE_INVOICES_ROUTES.BY_ID]['delete']; +export type SaleInvoicesListResponse = OpResponseBody>; +export type SaleInvoice = OpResponseBody>; +export type CreateSaleInvoiceBody = OpRequestBody>; +export type EditSaleInvoiceBody = OpRequestBody>; +export type BulkDeleteSaleInvoicesBody = OpRequestBody>; +export type ValidateBulkDeleteSaleInvoicesResponse = OpResponseBody>; +export type SaleInvoiceStateResponse = OpResponseBody>; +export type GetSaleInvoicesQuery = OpQueryParams>; -export type SaleInvoicesListResponse = GetSaleInvoices['responses'][200]['content']['application/json']; -export type SaleInvoice = GetSaleInvoice['responses'][200]['content']['application/json']; -export type CreateSaleInvoiceBody = CreateSaleInvoice['requestBody']['content']['application/json']; -export type EditSaleInvoiceBody = EditSaleInvoice['requestBody']['content']['application/json']; - -export async function fetchSaleInvoices(fetcher: ApiFetcher): Promise { +export async function fetchSaleInvoices( + fetcher: ApiFetcher, + query?: GetSaleInvoicesQuery +): Promise { const get = fetcher.path(SALE_INVOICES_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await ( + get as (params?: GetSaleInvoicesQuery) => Promise<{ data: SaleInvoicesListResponse }> + )(query ?? {}); return data; } @@ -61,3 +65,103 @@ export async function deleteSaleInvoice(fetcher: ApiFetcher, id: number): Promis const del = fetcher.path(SALE_INVOICES_ROUTES.BY_ID).method('delete').create(); await del({ id }); } + +export async function bulkDeleteSaleInvoices( + fetcher: ApiFetcher, + body: BulkDeleteSaleInvoicesBody +): Promise { + const post = fetcher.path(SALE_INVOICES_ROUTES.BULK_DELETE).method('post').create(); + await post({ ids: body.ids, skipUndeletable: body.skipUndeletable ?? false } as never); +} + +export async function validateBulkDeleteSaleInvoices( + fetcher: ApiFetcher, + ids: number[] +): Promise { + const post = fetcher.path(SALE_INVOICES_ROUTES.VALIDATE_BULK_DELETE).method('post').create(); + const { data } = await post({ ids, skipUndeletable: false } as never); + return data as ValidateBulkDeleteSaleInvoicesResponse; +} + +export async function deliverSaleInvoice(fetcher: ApiFetcher, id: number): Promise { + const put = fetcher.path(SALE_INVOICES_ROUTES.DELIVER).method('put').create(); + await put({ id }); +} + +export async function writeOffSaleInvoice( + fetcher: ApiFetcher, + id: number, + body?: Record +): Promise { + const post = fetcher.path(SALE_INVOICES_ROUTES.WRITEOFF).method('post').create(); + await post({ id, ...(body ?? {}) } as never); +} + +export async function cancelWrittenOffSaleInvoice(fetcher: ApiFetcher, id: number): Promise { + const post = fetcher.path(SALE_INVOICES_ROUTES.CANCEL_WRITEOFF).method('post').create(); + await post({ id }); +} + +export async function fetchReceivableSaleInvoices( + fetcher: ApiFetcher, + customerId?: number +): Promise { + const get = fetcher.path(SALE_INVOICES_ROUTES.RECEIVABLE).method('get').create(); + const { data } = await (get as (params?: { customerId?: number }) => Promise<{ data: unknown }>)( + customerId != null ? { customerId } : {} + ); + return data; +} + +export async function fetchSaleInvoiceMailState( + fetcher: ApiFetcher, + id: number +): Promise { + const get = fetcher.path(SALE_INVOICES_ROUTES.MAIL).method('get').create(); + const { data } = await get({ id }); + return data; +} + +export async function sendSaleInvoiceMail( + fetcher: ApiFetcher, + id: number, + body?: Record +): Promise { + const post = fetcher.path(SALE_INVOICES_ROUTES.MAIL).method('post').create(); + await post({ id, ...(body ?? {}) } as never); +} + +export async function fetchSaleInvoiceState( + fetcher: ApiFetcher +): Promise { + const get = fetcher.path(SALE_INVOICES_ROUTES.STATE).method('get').create(); + const { data } = await (get as (params?: object) => Promise<{ data: SaleInvoiceStateResponse }>)({}); + return data; +} + +export async function fetchInvoicePayments( + fetcher: ApiFetcher, + id: number +): Promise { + const get = fetcher.path(SALE_INVOICES_ROUTES.PAYMENTS).method('get').create(); + const { data } = await get({ id }); + return data; +} + +export async function fetchSaleInvoiceHtml( + fetcher: ApiFetcher, + id: number +): Promise<{ htmlContent: string }> { + const get = fetcher.path(SALE_INVOICES_ROUTES.HTML).method('get').create(); + const { data } = await (get as (params: { id: number }) => Promise<{ data: { htmlContent: string } }>)({ id }); + return data as { htmlContent: string }; +} + +export async function generateSaleInvoiceSharableLink( + fetcher: ApiFetcher, + id: number +): Promise<{ link: string }> { + const post = fetcher.path(SALE_INVOICES_ROUTES.GENERATE_LINK).method('post').create(); + const { data } = await (post as (params: { id: number }) => Promise<{ data: { link: string } }>)({ id }); + return data as { link: string }; +} diff --git a/shared/sdk-ts/src/sale-receipts.ts b/shared/sdk-ts/src/sale-receipts.ts index 8d411c05c..c8cf861a0 100644 --- a/shared/sdk-ts/src/sale-receipts.ts +++ b/shared/sdk-ts/src/sale-receipts.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const SALE_RECEIPTS_ROUTES = { LIST: '/api/sale-receipts', @@ -7,24 +8,27 @@ export const SALE_RECEIPTS_ROUTES = { STATE: '/api/sale-receipts/state', VALIDATE_BULK_DELETE: '/api/sale-receipts/validate-bulk-delete', BULK_DELETE: '/api/sale-receipts/bulk-delete', + CLOSE: '/api/sale-receipts/{id}/close', + MAIL: '/api/sale-receipts/{id}/mail', } as const satisfies Record; -type GetSaleReceipts = paths[typeof SALE_RECEIPTS_ROUTES.LIST]['get']; -type GetSaleReceipt = paths[typeof SALE_RECEIPTS_ROUTES.BY_ID]['get']; -type CreateSaleReceipt = paths[typeof SALE_RECEIPTS_ROUTES.LIST]['post']; -type EditSaleReceipt = paths[typeof SALE_RECEIPTS_ROUTES.BY_ID]['put']; -type DeleteSaleReceipt = paths[typeof SALE_RECEIPTS_ROUTES.BY_ID]['delete']; +export type SaleReceiptsListResponse = OpResponseBody>; +export type SaleReceipt = OpResponseBody>; +export type CreateSaleReceiptBody = OpRequestBody>; +export type EditSaleReceiptBody = OpRequestBody>; +export type BulkDeleteReceiptsBody = OpRequestBody>; +export type ValidateBulkDeleteReceiptsResponse = OpResponseBody>; +export type SaleReceiptStateResponse = OpResponseBody>; +export type GetSaleReceiptsQuery = OpQueryParams>; -type GetSaleReceipts200 = GetSaleReceipts['responses'][200]; -type GetSaleReceipt200 = GetSaleReceipt['responses'][200]; -export type SaleReceiptsListResponse = GetSaleReceipts200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type SaleReceipt = GetSaleReceipt200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateSaleReceiptBody = CreateSaleReceipt['requestBody']['content']['application/json']; -export type EditSaleReceiptBody = EditSaleReceipt['requestBody']['content']['application/json']; - -export async function fetchSaleReceipts(fetcher: ApiFetcher): Promise { +export async function fetchSaleReceipts( + fetcher: ApiFetcher, + query?: GetSaleReceiptsQuery +): Promise { const get = fetcher.path(SALE_RECEIPTS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await ( + get as (params?: GetSaleReceiptsQuery) => Promise<{ data: SaleReceiptsListResponse }> + )(query ?? {}); return data; } @@ -55,3 +59,51 @@ export async function deleteSaleReceipt(fetcher: ApiFetcher, id: number): Promis const del = fetcher.path(SALE_RECEIPTS_ROUTES.BY_ID).method('delete').create(); await del({ id }); } + +export async function bulkDeleteSaleReceipts( + fetcher: ApiFetcher, + body: BulkDeleteReceiptsBody +): Promise { + const post = fetcher.path(SALE_RECEIPTS_ROUTES.BULK_DELETE).method('post').create(); + await post({ ids: body.ids, skipUndeletable: body.skipUndeletable ?? false } as never); +} + +export async function validateBulkDeleteSaleReceipts( + fetcher: ApiFetcher, + ids: number[] +): Promise { + const post = fetcher.path(SALE_RECEIPTS_ROUTES.VALIDATE_BULK_DELETE).method('post').create(); + const { data } = await post({ ids, skipUndeletable: false } as never); + return data as ValidateBulkDeleteReceiptsResponse; +} + +export async function closeSaleReceipt(fetcher: ApiFetcher, id: number): Promise { + const post = fetcher.path(SALE_RECEIPTS_ROUTES.CLOSE).method('post').create(); + await post({ id }); +} + +export async function fetchSaleReceiptMail( + fetcher: ApiFetcher, + id: number +): Promise { + const get = fetcher.path(SALE_RECEIPTS_ROUTES.MAIL).method('get').create(); + const { data } = await get({ id }); + return data; +} + +export async function sendSaleReceiptMail( + fetcher: ApiFetcher, + id: number, + body?: Record +): Promise { + const post = fetcher.path(SALE_RECEIPTS_ROUTES.MAIL).method('post').create(); + await post({ id, ...(body ?? {}) } as never); +} + +export async function fetchSaleReceiptState( + fetcher: ApiFetcher +): Promise { + const get = fetcher.path(SALE_RECEIPTS_ROUTES.STATE).method('get').create(); + const { data } = await (get as (params?: object) => Promise<{ data: SaleReceiptStateResponse }>)({}); + return data; +} diff --git a/shared/sdk-ts/src/schema.ts b/shared/sdk-ts/src/schema.ts index 7baf902f2..339b76c00 100644 --- a/shared/sdk-ts/src/schema.ts +++ b/shared/sdk-ts/src/schema.ts @@ -54,6 +54,108 @@ export interface paths { patch?: never; trace?: never; }; + "/api/auth/signin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Sign in a user */ + post: operations["AuthController_signin"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/signup": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Sign up a new user */ + post: operations["AuthController_signup"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/signup/verify": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Confirm user signup */ + post: operations["AuthController_signupConfirm"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/send_reset_password": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Send reset password email */ + post: operations["AuthController_sendResetPassword"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/reset_password/{token}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Reset password using token */ + post: operations["AuthController_resetPassword"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/meta": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get auth metadata (e.g. signup disabled) */ + get: operations["AuthController_meta"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/api-keys/generate": { parameters: { query?: never; @@ -11919,6 +12021,209 @@ export interface operations { }; }; }; + AuthController_signin: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description User email address + * @example user@example.com + */ + email: string; + /** + * @description User password + * @example password123 + */ + password: string; + }; + }; + }; + responses: { + /** @description Sign-in successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @description JWT access token */ + accessToken?: string; + /** @description Organization ID */ + organizationId?: string; + /** @description Tenant ID */ + tenantId?: number; + /** @description User ID */ + userId?: number; + }; + }; + }; + }; + }; + AuthController_signup: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description User first name + * @example John + */ + firstName: string; + /** + * @description User last name + * @example Doe + */ + lastName: string; + /** + * Format: email + * @description User email address + * @example john.doe@example.com + */ + email: string; + /** + * @description User password + * @example password123 + */ + password: string; + }; + }; + }; + responses: { + /** @description Sign-up initiated. Check email for confirmation. */ + 201: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + AuthController_signupConfirm: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description User email address + * @example user@example.com + */ + email: string; + /** + * @description Signup confirmation token from email + * @example confirmation-token + */ + token: string; + }; + }; + }; + responses: { + /** @description Signup confirmed successfully. */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + AuthController_sendResetPassword: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description User email address to send reset link to + * @example user@example.com + */ + email: string; + }; + }; + }; + responses: { + /** @description Reset password email sent if the account exists. */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + AuthController_resetPassword: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Reset password token from email link */ + token: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description New password + * @example new-password + */ + password: string; + }; + }; + }; + responses: { + /** @description Password reset successfully. */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + AuthController_meta: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Auth metadata for the login/signup page */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @description Whether signup is disabled */ + signupDisabled?: boolean; + }; + }; + }; + }; + }; AuthApiKeysController_generate: { parameters: { query?: never; diff --git a/shared/sdk-ts/src/settings.ts b/shared/sdk-ts/src/settings.ts index 785111f42..fba8695ea 100644 --- a/shared/sdk-ts/src/settings.ts +++ b/shared/sdk-ts/src/settings.ts @@ -1,21 +1,23 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const SETTINGS_ROUTES = { GET_SAVE: '/api/settings', } as const satisfies Record; -type GetSettings = paths[typeof SETTINGS_ROUTES.GET_SAVE]['get']; -type SaveSettings = paths[typeof SETTINGS_ROUTES.GET_SAVE]['put']; +export type SettingsResponse = OpResponseBody>; +export type SaveSettingsBody = OpRequestBody>; +export type GetSettingsQuery = OpQueryParams>; -type GetSettings200 = GetSettings['responses'][200]; -type SaveSettingsRequestBody = SaveSettings extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; -export type SettingsResponse = GetSettings200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type SaveSettingsBody = SaveSettingsRequestBody; - -export async function fetchSettings(fetcher: ApiFetcher): Promise { +export async function fetchSettings( + fetcher: ApiFetcher, + query?: GetSettingsQuery +): Promise { const get = fetcher.path(SETTINGS_ROUTES.GET_SAVE).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params?: GetSettingsQuery) => Promise<{ data: SettingsResponse }>)( + query ?? {} + ); return data; } diff --git a/shared/sdk-ts/src/subscription.ts b/shared/sdk-ts/src/subscription.ts index 66360a290..7b7379d95 100644 --- a/shared/sdk-ts/src/subscription.ts +++ b/shared/sdk-ts/src/subscription.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const SUBSCRIPTION_ROUTES = { LIST: '/api/subscription', @@ -9,10 +10,7 @@ export const SUBSCRIPTION_ROUTES = { CHANGE: '/api/subscription/change', } as const satisfies Record; -type GetSubscriptions = paths[typeof SUBSCRIPTION_ROUTES.LIST]['get']; - -type GetSubscriptions200 = GetSubscriptions['responses'][200]; -export type SubscriptionsListResponse = GetSubscriptions200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type SubscriptionsListResponse = OpResponseBody>; export async function fetchSubscriptions(fetcher: ApiFetcher): Promise { const get = fetcher.path(SUBSCRIPTION_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/tax-rates.ts b/shared/sdk-ts/src/tax-rates.ts index f97a35c0c..cb6ac7cf5 100644 --- a/shared/sdk-ts/src/tax-rates.ts +++ b/shared/sdk-ts/src/tax-rates.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const TAX_RATES_ROUTES = { LIST: '/api/tax-rates', @@ -8,18 +9,10 @@ export const TAX_RATES_ROUTES = { INACTIVATE: '/api/tax-rates/{id}/inactivate', } as const satisfies Record; -type GetTaxRates = paths[typeof TAX_RATES_ROUTES.LIST]['get']; -type GetTaxRate = paths[typeof TAX_RATES_ROUTES.BY_ID]['get']; -type CreateTaxRate = paths[typeof TAX_RATES_ROUTES.LIST]['post']; -type EditTaxRate = paths[typeof TAX_RATES_ROUTES.BY_ID]['put']; -type DeleteTaxRate = paths[typeof TAX_RATES_ROUTES.BY_ID]['delete']; - -type GetTaxRates200 = GetTaxRates['responses'][200]; -type GetTaxRate200 = GetTaxRate['responses'][200]; -export type TaxRatesListResponse = GetTaxRates200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type TaxRate = GetTaxRate200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateTaxRateBody = CreateTaxRate extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; -export type EditTaxRateBody = EditTaxRate extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; +export type TaxRatesListResponse = OpResponseBody>; +export type TaxRate = OpResponseBody>; +export type CreateTaxRateBody = OpRequestBody>; +export type EditTaxRateBody = OpRequestBody>; export async function fetchTaxRates(fetcher: ApiFetcher): Promise { const get = fetcher.path(TAX_RATES_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/transactions-locking.ts b/shared/sdk-ts/src/transactions-locking.ts index 737471b9c..028ce92e9 100644 --- a/shared/sdk-ts/src/transactions-locking.ts +++ b/shared/sdk-ts/src/transactions-locking.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const TRANSACTIONS_LOCKING_ROUTES = { LOCK: '/api/transactions-locking/lock', @@ -10,7 +11,7 @@ export const TRANSACTIONS_LOCKING_ROUTES = { BY_MODULE: '/api/transactions-locking/{module}', } as const satisfies Record; -export type TransactionsLockingListResponse = unknown; +export type TransactionsLockingListResponse = OpResponseBody>; export async function fetchTransactionsLocking(fetcher: ApiFetcher): Promise { const get = fetcher.path(TRANSACTIONS_LOCKING_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/users.ts b/shared/sdk-ts/src/users.ts index e96bffcc5..85767478f 100644 --- a/shared/sdk-ts/src/users.ts +++ b/shared/sdk-ts/src/users.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const USERS_ROUTES = { LIST: '/api/users', @@ -8,17 +9,10 @@ export const USERS_ROUTES = { INACTIVATE: '/api/users/{id}/inactivate', } as const satisfies Record; -type GetUsers = paths[typeof USERS_ROUTES.LIST]['get']; -type GetUser = paths[typeof USERS_ROUTES.BY_ID]['get']; -type EditUser = paths[typeof USERS_ROUTES.BY_ID]['put']; - -type GetUsers200 = GetUsers['responses'][200]; -type GetUser200 = GetUser['responses'][200]; -export type UsersListResponse = GetUsers200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type User = GetUser200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type EditUserBody = EditUser['requestBody']['content']['application/json']; - -export type GetUsersQuery = GetUsers['parameters']['query']; +export type UsersListResponse = OpResponseBody>; +export type User = OpResponseBody>; +export type EditUserBody = OpRequestBody>; +export type GetUsersQuery = OpQueryParams>; export async function fetchUsers( fetcher: ApiFetcher, diff --git a/shared/sdk-ts/src/utils/index.ts b/shared/sdk-ts/src/utils/index.ts new file mode 100644 index 000000000..8a3de1c53 --- /dev/null +++ b/shared/sdk-ts/src/utils/index.ts @@ -0,0 +1,30 @@ +import { paths } from "../schema"; + +type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'; + +// Helpers: derive request/response types from schema paths (single source of truth). +// When a path exists in the schema we get the real types; otherwise a safe fallback. +export type OpForPath

= P extends keyof paths + ? paths[P] extends Record + ? O + : never + : never; + +/** Query params for a GET (or other) operation. Use with OpForPath. */ +export type OpQueryParams = O extends { parameters: { query?: infer Q } } + ? (Q extends undefined ? Record : NonNullable) + : Record; + +export type OpRequestBody = [O] extends [ + { requestBody: { content: { 'application/json': infer B } } }, +] + ? B + : Record; + +export type OpResponseBody = O extends { + responses: { 200: { content: { 'application/json': infer R } } }; +} + ? R + : O extends { responses: { 201: { content: { 'application/json': infer R } } } } + ? R + : unknown; diff --git a/shared/sdk-ts/src/vendor-credits.ts b/shared/sdk-ts/src/vendor-credits.ts index 8180cfb38..8a534c84c 100644 --- a/shared/sdk-ts/src/vendor-credits.ts +++ b/shared/sdk-ts/src/vendor-credits.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const VENDOR_CREDITS_ROUTES = { LIST: '/api/vendor-credits', @@ -9,18 +10,10 @@ export const VENDOR_CREDITS_ROUTES = { BULK_DELETE: '/api/vendor-credits/bulk-delete', } as const satisfies Record; -type GetVendorCredits = paths[typeof VENDOR_CREDITS_ROUTES.LIST]['get']; -type GetVendorCredit = paths[typeof VENDOR_CREDITS_ROUTES.BY_ID]['get']; -type CreateVendorCredit = paths[typeof VENDOR_CREDITS_ROUTES.LIST]['post']; -type EditVendorCredit = paths[typeof VENDOR_CREDITS_ROUTES.BY_ID]['put']; -type DeleteVendorCredit = paths[typeof VENDOR_CREDITS_ROUTES.BY_ID]['delete']; - -type GetVendorCredits200 = GetVendorCredits['responses'][200]; -type GetVendorCredit200 = GetVendorCredit['responses'][200]; -export type VendorCreditsListResponse = GetVendorCredits200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type VendorCredit = GetVendorCredit200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateVendorCreditBody = CreateVendorCredit['requestBody']['content']['application/json']; -export type EditVendorCreditBody = EditVendorCredit['requestBody']['content']['application/json']; +export type VendorCreditsListResponse = OpResponseBody>; +export type VendorCredit = OpResponseBody>; +export type CreateVendorCreditBody = OpRequestBody>; +export type EditVendorCreditBody = OpRequestBody>; export async function fetchVendorCredits(fetcher: ApiFetcher): Promise { const get = fetcher.path(VENDOR_CREDITS_ROUTES.LIST).method('get').create(); diff --git a/shared/sdk-ts/src/vendors.ts b/shared/sdk-ts/src/vendors.ts index 51c899160..f399995af 100644 --- a/shared/sdk-ts/src/vendors.ts +++ b/shared/sdk-ts/src/vendors.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const VENDORS_ROUTES = { LIST: '/api/vendors', @@ -9,25 +10,22 @@ export const VENDORS_ROUTES = { BULK_DELETE: '/api/vendors/bulk-delete', } as const satisfies Record; -type GetVendors = paths[typeof VENDORS_ROUTES.LIST]['get']; -type GetVendor = paths[typeof VENDORS_ROUTES.BY_ID]['get']; -type CreateVendor = paths[typeof VENDORS_ROUTES.LIST]['post']; -type EditVendor = paths[typeof VENDORS_ROUTES.BY_ID]['put']; -type ValidateBulkDelete = paths[typeof VENDORS_ROUTES.VALIDATE_BULK_DELETE]['post']; -type BulkDelete = paths[typeof VENDORS_ROUTES.BULK_DELETE]['post']; +export type VendorsListResponse = OpResponseBody>; +export type Vendor = OpResponseBody>; +export type CreateVendorBody = OpRequestBody>; +export type EditVendorBody = OpRequestBody>; +export type ValidateBulkDeleteVendorsResponse = OpResponseBody>; +export type BulkDeleteVendorsBody = OpRequestBody>; +export type GetVendorsQuery = OpQueryParams>; -type GetVendors200 = GetVendors['responses'][200]; -type GetVendor200 = GetVendor['responses'][200]; -export type VendorsListResponse = GetVendors200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type Vendor = GetVendor200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateVendorBody = CreateVendor['requestBody']['content']['application/json']; -export type EditVendorBody = EditVendor['requestBody']['content']['application/json']; -export type ValidateBulkDeleteVendorsResponse = ValidateBulkDelete['responses'][200]['content']['application/json']; -export type BulkDeleteVendorsBody = BulkDelete['requestBody']['content']['application/json']; - -export async function fetchVendors(fetcher: ApiFetcher): Promise { +export async function fetchVendors( + fetcher: ApiFetcher, + query?: GetVendorsQuery +): Promise { const get = fetcher.path(VENDORS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params: GetVendorsQuery) => Promise<{ data: VendorsListResponse }>)( + query ?? {} + ); return data; } diff --git a/shared/sdk-ts/src/views.ts b/shared/sdk-ts/src/views.ts index b5416e5d5..cc7c81302 100644 --- a/shared/sdk-ts/src/views.ts +++ b/shared/sdk-ts/src/views.ts @@ -1,14 +1,12 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpResponseBody } from './utils'; export const VIEWS_ROUTES = { RESOURCE: '/api/views/resource/{resourceModel}', } as const satisfies Record; -type GetResourceView = paths[typeof VIEWS_ROUTES.RESOURCE]['get']; - -type GetResourceView200 = GetResourceView['responses'][200]; -export type ResourceViewResponse = GetResourceView200 extends { content?: { 'application/json': infer J } } ? J : unknown; +export type ResourceViewResponse = OpResponseBody>; export async function fetchResourceView( fetcher: ApiFetcher, diff --git a/shared/sdk-ts/src/warehouse-transfers.ts b/shared/sdk-ts/src/warehouse-transfers.ts index 515c31fca..df36c306d 100644 --- a/shared/sdk-ts/src/warehouse-transfers.ts +++ b/shared/sdk-ts/src/warehouse-transfers.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils'; export const WAREHOUSE_TRANSFERS_ROUTES = { LIST: '/api/warehouse-transfers', @@ -8,24 +9,20 @@ export const WAREHOUSE_TRANSFERS_ROUTES = { TRANSFERRED: '/api/warehouse-transfers/{id}/transferred', } as const satisfies Record; -type GetWarehouseTransfers = paths[typeof WAREHOUSE_TRANSFERS_ROUTES.LIST]['get']; -type GetWarehouseTransfer = paths[typeof WAREHOUSE_TRANSFERS_ROUTES.BY_ID]['get']; -type CreateWarehouseTransfer = paths[typeof WAREHOUSE_TRANSFERS_ROUTES.LIST]['post']; -type EditWarehouseTransfer = paths[typeof WAREHOUSE_TRANSFERS_ROUTES.BY_ID]['put']; -type DeleteWarehouseTransfer = paths[typeof WAREHOUSE_TRANSFERS_ROUTES.BY_ID]['delete']; +export type WarehouseTransfersListResponse = OpResponseBody>; +export type WarehouseTransfer = OpResponseBody>; +export type CreateWarehouseTransferBody = OpRequestBody>; +export type EditWarehouseTransferBody = OpRequestBody>; +export type GetWarehouseTransfersQuery = OpQueryParams>; -type GetWarehouseTransfers200 = GetWarehouseTransfers['responses'][200]; -type GetWarehouseTransfer200 = GetWarehouseTransfer['responses'][200]; -export type WarehouseTransfersListResponse = GetWarehouseTransfers200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type WarehouseTransfer = GetWarehouseTransfer200 extends { content?: { 'application/json': infer J } } ? J : unknown; -export type CreateBody = CreateWarehouseTransfer extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; -type EditBody = EditWarehouseTransfer extends { requestBody: { content: { 'application/json': infer J } } } ? J : Record; -export type CreateWarehouseTransferBody = CreateBody; -export type EditWarehouseTransferBody = EditBody; - -export async function fetchWarehouseTransfers(fetcher: ApiFetcher): Promise { +export async function fetchWarehouseTransfers( + fetcher: ApiFetcher, + query?: GetWarehouseTransfersQuery +): Promise { const get = fetcher.path(WAREHOUSE_TRANSFERS_ROUTES.LIST).method('get').create(); - const { data } = await get({}); + const { data } = await (get as (params: GetWarehouseTransfersQuery) => Promise<{ data: WarehouseTransfersListResponse }>)( + query ?? {} + ); return data; } @@ -49,7 +46,7 @@ export async function editWarehouseTransfer( values: EditWarehouseTransferBody ): Promise { const put = fetcher.path(WAREHOUSE_TRANSFERS_ROUTES.BY_ID).method('put').create(); - await (put as unknown as (params: { id: number } & EditWarehouseTransferBody) => Promise)({ id, ...values }); + await put({ id, ...(values as object) } as never); } export async function deleteWarehouseTransfer(fetcher: ApiFetcher, id: number): Promise { diff --git a/shared/sdk-ts/src/warehouses.ts b/shared/sdk-ts/src/warehouses.ts index 1ad633674..46057ba34 100644 --- a/shared/sdk-ts/src/warehouses.ts +++ b/shared/sdk-ts/src/warehouses.ts @@ -1,5 +1,6 @@ import type { ApiFetcher } from './fetch-utils'; -import type { paths } from './schema'; +import { paths } from './schema'; +import { OpForPath, OpRequestBody, OpResponseBody } from './utils'; export const WAREHOUSES_ROUTES = { LIST: '/api/warehouses', @@ -8,16 +9,10 @@ export const WAREHOUSES_ROUTES = { MARK_PRIMARY: '/api/warehouses/{id}/mark-primary', } as const satisfies Record; -type GetWarehouses = paths[typeof WAREHOUSES_ROUTES.LIST]['get']; -type GetWarehouse = paths[typeof WAREHOUSES_ROUTES.BY_ID]['get']; -type CreateWarehouse = paths[typeof WAREHOUSES_ROUTES.LIST]['post']; -type EditWarehouse = paths[typeof WAREHOUSES_ROUTES.BY_ID]['put']; -type DeleteWarehouse = paths[typeof WAREHOUSES_ROUTES.BY_ID]['delete']; - -export type WarehousesListResponse = GetWarehouses['responses'][200]['content']['application/json']; -export type Warehouse = GetWarehouse['responses'][200]['content']['application/json']; -export type CreateWarehouseBody = CreateWarehouse['requestBody']['content']['application/json']; -export type EditWarehouseBody = EditWarehouse['requestBody']['content']['application/json']; +export type WarehousesListResponse = OpResponseBody>; +export type Warehouse = OpResponseBody>; +export type CreateWarehouseBody = OpRequestBody>; +export type EditWarehouseBody = OpRequestBody>; export async function fetchWarehouses(fetcher: ApiFetcher): Promise { const get = fetcher.path(WAREHOUSES_ROUTES.LIST).method('get').create();