import type {AxiosInstance, AxiosResponse} from "axios"; import type {Pool} from "mysql2/promise"; import {eFetchDependencies} from "./dynamicFetching"; import {Modify} from "./modifyTypes"; import {JoinType, OrderTerm, SQLComparisonOperator, SQLExpression} from "./mysqlTypes"; import type {CarbonReact, iStateAdapter} from "@carbonorm/carbonreact"; import type {OrmGenerics} from "./ormGenerics"; import {restOrm} from "../api/restOrm"; import type {SqlDialect, SqlDialectName} from "../orm/dialects/SqlDialect"; type RestOrmFactory = typeof restOrm>; type RestOrmReturn = ReturnType; export type iRestMethods = 'GET' | 'POST' | 'PUT' | 'DELETE'; export const POST = 'POST'; export const PUT = 'PUT'; export const GET = 'GET'; export const DELETE = 'DELETE'; export interface stringMap { [key: string]: string; } export interface stringNumberMap { [key: string]: string | number; } export interface RegExpMap { [key: string]: RegExp | RegExpMap; } export interface complexMap { [key: string]: stringMap | stringNumberMap | stringMap[] | RegExpMap; } export interface iTypeValidation { MYSQL_TYPE: string; MAX_LENGTH: string; AUTO_INCREMENT: boolean; SKIP_COLUMN_IN_POST: boolean; } export type SubSelect = { subSelect: true; table: string; args: RequestQueryBody<'GET', T>; alias: string; }; export type SelectField = | keyof T | SQLExpression | SubSelect; export type WhereClause = Partial | LogicalGroup | ComparisonClause; export type LogicalGroup = { [logicalGroup: string]: Array> }; export type ComparisonClause = [keyof T, SQLComparisonOperator, any]; export type JoinTableCondition = Partial | WhereClause[] | ComparisonClause[]; export type JoinClause = { [table: string]: JoinTableCondition; }; export type Join = { [K in JoinType]?: JoinClause; }; export type IndexHints = Partial>; export type Pagination = { PAGE?: number; LIMIT?: number | null; ORDER?: OrderTerm[]; }; export type RequestGetPutDeleteBody = T | { SELECT?: SelectField[]; UPDATE?: Partial; DELETE?: boolean; WHERE?: WhereClause; JOIN?: Join; ORDER?: OrderTerm[]; GROUP_BY?: string | string[]; HAVING?: WhereClause; INDEX_HINTS?: IndexHints; PAGINATION?: Pagination; }; export type RequestPostBody = T | { INSERT?: Partial; REPLACE?: Partial; }; export type iAPI = T & { DB?: string; dataInsertMultipleRows?: T[]; cacheResults?: boolean; skipReactBootstrap?: boolean; fetchDependencies?: number | eFetchDependencies | Awaited>[]; debug?: boolean; success?: string | ((r: AxiosResponse) => string | void); error?: string | ((r: AxiosResponse) => string | void); }; // TODO - Eventually I believe we can support complex posts. export type RequestQueryBody< Method extends iRestMethods, T extends { [key: string]: any }, Custom extends { [key: string]: any } = {}, Overrides extends { [key: string]: any } = {} > = Method extends 'GET' | 'PUT' | 'DELETE' ? iAPI & Custom>> : iAPI & Custom>>; export interface iCacheRequestConfig { method?: string; url?: string; headers?: any; } export interface iCacheResponse { data: ResponseDataType; config?: iCacheRequestConfig; [key: string]: any; } export interface iCacheAPI { requestArgumentsSerialized: string; request: Promise>; allowListStatus?: "allowed" | "denied" | "not verified"; response?: iCacheResponse & { __carbonTiming?: { start: number; end: number; duration: number; } }, final?: boolean; } export interface iChangeC6Data { rowCount: number; } // New discriminated REST response type based on HTTP method export type C6RestResponse< Method extends iRestMethods, RestData extends { [key: string]: any }, Overrides = {} > = { rest: Method extends 'GET' ? Modify[] : never; session?: any; sql?: any; } & (Method extends 'GET' ? { next?: () => Promise>, evictFromCache?: () => boolean } : { affected: number, insertId?: number | string, }); export interface iC6RestResponse { // Backwards compatibility: base interface for rest/sql/session (singular) rest: RestData; session?: any; sql?: any; } export interface iDeleteC6RestResponse extends iChangeC6Data, C6RestResponse<'DELETE', RestData> { deleted: boolean | number | string | RequestData; } export interface iPostC6RestResponse extends C6RestResponse<'POST', RestData> { created: boolean | number | string; } export interface iPutC6RestResponse extends iChangeC6Data, C6RestResponse<'PUT', RestData> { updated: boolean | number | string | RequestData; } export interface iGetC6RestResponse< ResponseDataType extends { [key: string]: any }, ResponseDataOverrides = {} > extends C6RestResponse<'GET', ResponseDataType, ResponseDataOverrides> {} export type DetermineResponseDataType< Method extends iRestMethods, RestTableInterface extends { [key: string]: any }, ResponseDataOverrides = {} > = (Method extends 'POST' ? iPostC6RestResponse : Method extends 'GET' ? iGetC6RestResponse : Method extends 'PUT' ? iPutC6RestResponse : Method extends 'DELETE' ? iDeleteC6RestResponse : never); export type iRestSqlExecutionContext = { sql: string; values: any[]; }; export type iRestLifecycleResponse = AxiosResponse> | { data: { success: boolean; } & DetermineResponseDataType; }; export type iRestWebsocketPayload = { REST: { TABLE_NAME: string; TABLE_PREFIX: string; METHOD: iRestMethods; REQUEST: Record; REQUEST_PRIMARY_KEY: Record | null; RESPONSE?: Record | Record[]; RESPONSE_PRIMARY_KEY?: Record | null; }; }; export type tWebsocketBroadcast = (payload: iRestWebsocketPayload) => void | Promise; export interface iPostgresQueryResult { rows?: Row[]; rowCount?: number | null; } export interface iPostgresClient { query: (sql: string, values?: any[]) => Promise | iPostgresQueryResult; release: () => void; } export interface iPostgresPool { connect: () => Promise | iPostgresClient; } export interface iDatabaseRuntimeOptions { sqlDialect?: SqlDialectName | SqlDialect; mysqlPool?: Pool; postgresPool?: iPostgresPool; axios?: AxiosInstance; restURL?: string; withCredentials?: boolean; C6?: iC6Object; reactBootstrap?: CarbonReact; stateAdapter?: iStateAdapter; websocketBroadcast?: tWebsocketBroadcast; logLevel?: number; verbose?: boolean; sqlAllowListPath?: string; sqlQueryNormalizer?: (sql: string) => string; } export type iDatabaseRuntimeConfig = Pool | iPostgresPool | (iDatabaseRuntimeOptions & Record); export interface iRest< RestShortTableName extends string = any, RestTableInterface extends Record = any, PrimaryKey extends keyof RestTableInterface & string = keyof RestTableInterface & string > { C6: iC6Object; axios?: AxiosInstance; restURL?: string; mysqlPool?: Pool; withCredentials?: boolean; restModel: C6RestfulModel; reactBootstrap?: CarbonReact; stateAdapter?: iStateAdapter; requestMethod: iRestMethods; sqlDialect?: SqlDialectName | SqlDialect; clearCache?: () => void; skipPrimaryCheck?: boolean; websocketBroadcast?: tWebsocketBroadcast; logLevel?: number; verbose?: boolean; sqlAllowListPath?: string; sqlQueryNormalizer?: (sql: string) => string; databases?: Record; defaultDatabase?: string; postgresPool?: iPostgresPool; } export interface iConstraint { TABLE: string; COLUMN: string; CONSTRAINT: string; } export type tColumns = { [K in keyof T & string as `${TableName}.${K}`]: K; }; export type tPrimaryKeys = PK extends any ? `${TableName}.${PK}` : never; type UppercaseFieldMap = { [K in keyof T as Uppercase]: any; }; export type C6RestfulModel< RestShortTableName extends string, RestTableInterface extends Record = any, PrimaryKey extends (keyof RestTableInterface & string) = keyof RestTableInterface & string > = { TABLE_NAME: RestShortTableName; RELATION_TYPE?: "TABLE" | "VIEW"; READ_ONLY?: boolean; PRIMARY: Array>; PRIMARY_SHORT: Array; COLUMNS: tColumns; TYPE_VALIDATION: { [key: string]: iTypeValidation }; REGEX_VALIDATION: RegExpMap; LIFECYCLE_HOOKS: iRestHooks>; TABLE_REFERENCES: { [columnName: string]: iConstraint[] }; TABLE_REFERENCED_BY: { [columnName: string]: iConstraint[] }; } & Required>; export type iRestReactiveLifecycle = { beforeProcessing?: { [key: string]: (args: { config: iRest; request: RequestQueryBody; }) => void | Promise; }; beforeExecution?: { [key: string]: (args: { config: iRest; request: RequestQueryBody; sqlExecution?: iRestSqlExecutionContext; }) => void | Promise; }; afterExecution?: { [key: string]: (args: { config: iRest; request: RequestQueryBody; response: iRestLifecycleResponse; }) => void | Promise; }; afterCommit?: { [key: string]: (args: { config: iRest; request: RequestQueryBody; response: iRestLifecycleResponse; }) => void | Promise; }; }; export type iRestHooks = { [Method in iRestMethods]: iRestReactiveLifecycle; }; export interface iDynamicApiImport { default: iRestApiFunctions; postState?: (response: AxiosResponse>, request: iAPI, id: string | number | boolean) => void; deleteState?: (response: AxiosResponse>, request: iAPI) => void; putState?: (response: AxiosResponse>, request: iAPI) => void; } export interface iRestApiFunctions { Delete: (request?: RequestQueryBody<'DELETE', RestData>) => iDeleteC6RestResponse; Post: (request?: RequestQueryBody<'POST', RestData>) => iPostC6RestResponse; Get: (request?: RequestQueryBody<'GET', RestData>) => iGetC6RestResponse; Put: (request?: RequestQueryBody<'PUT', RestData>) => iPutC6RestResponse; } // TODO - remove the key export interface iC6Object< RestTableInterfaces extends { [key: string]: any } = { [key: string]: any }, > { C6VERSION: string; TABLES: { [K in Extract]: C6RestfulModel; }; PREFIX: string; ORM: { [K in Extract]: C6RestfulModel & RestOrmReturn } [key: string]: any; } export interface tC6RestApi { [key: string]: { REST: iRestApiFunctions; PUT: Function; POST: Function; DELETE: Function; }; }