import qs from 'qs' import type { ApiClient } from './ApiClient' import type { CrudApi, GetByIdQuery, ListQuery, Paginated } from './types' export type { WhereClause, IncludeItem, ListQuery, GetByIdQuery, Paginated, HasGetMany, HasGetById, HasCreate, HasUpdateById, HasDeleteById, CrudApi, } from './types' export class BaseCrudApi< TResponse, TCreate, TUpdate = TCreate, TQuery extends ListQuery = ListQuery, > implements CrudApi { public readonly client: ApiClient public readonly basePath: string constructor(client: ApiClient, basePath: string) { this.client = client this.basePath = basePath.replace(/\/$/, '') this.getMany = this.getMany.bind(this) this.getOne = this.getOne.bind(this) this.getById = this.getById.bind(this) this.create = this.create.bind(this) this.updateById = this.updateById.bind(this) this.deleteById = this.deleteById.bind(this) } getMany( query: TQuery & { paginate: false }, headers?: Record, ): Promise getMany( query?: TQuery & { paginate?: true }, headers?: Record, ): Promise> getMany( query?: TQuery, headers?: Record, ): Promise | TResponse[]> { const queryString = query ? `?${qs.stringify(query)}` : '' return this.client.get | TResponse[]>( `${this.basePath}${queryString}`, headers, ) } /** * Returns the first resource matching the query, or `null` when none match. * Convenience over `getMany` with `limit: 1`. * @permissions $resourceName.view */ async getOne(query?: TQuery, headers?: Record): Promise { const results = await this.getMany( { ...query, limit: 1, paginate: false } as TQuery & { paginate: false }, headers, ) return results[0] ?? null } /** * Returns a single resource by ID. * @permissions $resourceName.view */ getById( id: string, query?: GetByIdQuery, headers?: Record, ): Promise { const queryString = query ? `?${qs.stringify(query)}` : '' return this.client.get(`${this.basePath}/${id}${queryString}`, headers) } /** * Creates a new resource. * @permissions $resourceName.create */ create(body: TCreate, headers?: Record): Promise { return this.client.post(this.basePath, body, headers) } /** * Updates an existing resource by ID. * @permissions $resourceName.update */ updateById(id: string, body: TUpdate, headers?: Record): Promise { return this.client.put(`${this.basePath}/${id}`, body, headers) } /** * Deletes a resource by ID. * @permissions $resourceName.delete */ deleteById(id: string, headers?: Record): Promise { return this.client.delete(`${this.basePath}/${id}`, headers) } }