diff --git a/packages/datasource-toolkit/src/interfaces/action.ts b/packages/datasource-toolkit/src/interfaces/action.ts new file mode 100644 index 0000000000..381a6bd138 --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/action.ts @@ -0,0 +1,88 @@ +import { Projection } from "./query/projection"; +import { RecordData } from "./query/record"; + +export interface Action { + execute(formValues: RecordData, selection?: Selection): Promise; + getForm( + selection?: Selection, + changedField?: string, + formValues?: RecordData + ): Promise; +} + +export interface ActionForm { + fields: ActionField[]; +} + +export interface ActionField { + field: string; + description?: string; + type: ActionFieldType; + isRequired?: boolean; + isReadOnly?: boolean; + defaultValue?: unknown; + useChangeHook: boolean; + + enums?: unknown[]; // When type === ActionFieldType.Enum + collectionName?: string; // When type === ActionFieldType.Collection +} + +export enum ActionFieldType { + Boolean = "Boolean", + Collection = "Collection", + Date = "Date", + Dateonly = "Dateonly", + Enum = "Enum", + File = "File", + Number = "Number", + String = "String", + Json = "Json", + EnumList = "Enum[]", + NumberList = "Number[]", + StringList = "String[]", +} + +export enum ActionResponseType { + Success, + Error, + Webhook, + File, + Redirect, +} + +export type SuccessReponse = { + type: ActionResponseType.Success; + message: string; + invalidatedDependencies: Projection; + options: { + type: "html" | "text"; + }; +}; + +export type ErrorResponse = SuccessReponse & { type: ActionResponseType.Error }; + +export type WebHookReponse = { + type: ActionResponseType.Webhook; + url: string; + method: "GET" | "POST"; + headers: { [key: string]: string }; + body: unknown; +}; + +export type FileResponse = { + type: ActionResponseType.File; + mimeType: string; + stream: ReadableStream; +}; + +export type RedirectResponse = { + type: ActionResponseType.Redirect; + path: string; +}; + +export type ActionResponse = + | SuccessReponse + | ErrorResponse + | WebHookReponse + | FileResponse + | RedirectResponse; diff --git a/packages/datasource-toolkit/src/interfaces/collection.ts b/packages/datasource-toolkit/src/interfaces/collection.ts new file mode 100644 index 0000000000..783f5da022 --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/collection.ts @@ -0,0 +1,31 @@ +import { Action } from "./action"; +import { AggregateResult, Aggregation } from "./query/aggregation"; +import { Projection } from "./query/projection"; +import { CompositeId, RecordData } from "./query/record"; +import { PaginatedFilter, Filter } from "./query/selection"; +import { CollectionSchema } from "./schema"; + +export interface IDataSource { + get collections(): ICollection[]; + getCollection(name: string): ICollection; +} + +export interface ICollection { + get dataSource(): IDataSource; + get name(): string; + get schema(): CollectionSchema; + + getAction(name: string): Action; + + getById(id: CompositeId, projection: Projection): Promise; + + create(data: RecordData[]): Promise; + + list(filter: PaginatedFilter, projection: Projection): Promise; + + update(filter: Filter, patch: RecordData): Promise; + + delete(filter: Filter): Promise; + + aggregate(filter: Filter, aggregation: Aggregation, limit?: number): Promise; +} diff --git a/packages/datasource-toolkit/src/interfaces/query/aggregation.ts b/packages/datasource-toolkit/src/interfaces/query/aggregation.ts new file mode 100644 index 0000000000..f5bf05f4d8 --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/query/aggregation.ts @@ -0,0 +1,30 @@ +export enum AggregationOperation { + Count = "Count", + Sum = "Sum", + Average = "Avg", + Max = "Max", + Min = "Min", +} + +export enum DateOperation { + ToYear = "Year", + ToMonth = "Month", + ToWeek = "Week", + toDay = "Day", +} + +export type Aggregation = { + field?: string; + operation: AggregationOperation; + ascending?: boolean; + + groups?: Array<{ + field: string; + operation?: DateOperation; + }>; +}; + +export type AggregateResult = { + value: number; + group: { [field: string]: string }; +}; diff --git a/packages/datasource-toolkit/src/interfaces/query/projection.ts b/packages/datasource-toolkit/src/interfaces/query/projection.ts new file mode 100644 index 0000000000..158d5c23e7 --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/query/projection.ts @@ -0,0 +1 @@ +export type Projection = string[]; diff --git a/packages/datasource-toolkit/src/interfaces/query/record.ts b/packages/datasource-toolkit/src/interfaces/query/record.ts new file mode 100644 index 0000000000..7e3a1aeb7a --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/query/record.ts @@ -0,0 +1,2 @@ +export type CompositeId = Array; +export type RecordData = Record; diff --git a/packages/datasource-toolkit/src/interfaces/query/selection.ts b/packages/datasource-toolkit/src/interfaces/query/selection.ts new file mode 100644 index 0000000000..adeea412da --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/query/selection.ts @@ -0,0 +1,61 @@ +export type Filter = { + conditionTree?: ConditionTree; + search?: string; + searchExtended?: boolean; + segment?: string; + timezone?: string; +}; + +export type PaginatedFilter = Filter & { + sort?: Array<{ + field: string; + ascending: boolean; + }>; + page?: { + skip?: number; + limit?: number; + }; +}; + +export enum Operator { + Blank = "blank", + Contains = "contains", + EndsWith = "ends_with", + Equal = "equal", + GreaterThan = "greater_than", + In = "in", + IncludesAll = "includes_all", + LessThan = "less_than", + NotContains = "not_contains", + NotEqual = "not_equal", + NotIn = "not_in", + Present = "present", + StartsWith = "starts_with", + + AfterXHoursAgo = "after_x_hours_ago", + BeforeXHoursAgo = "before_x_hours_ago", + Future = "future", + Past = "past", + PreviousMonthToDate = "previous_month_to_date", + PreviousMonth = "previous_month", + PreviousQuarterToDate = "previous_quarter_to_date", + PreviousQuarter = "previous_quarter", + PreviousWeekToDate = "previous_week_to_date", + PreviousWeek = "previous_week", + PreviousXDaysToDate = "previous_x_days_to_date", + PreviousXDays = "previous_x_days", + PreviousYearToDate = "previous_year_to_date", + PreviousYear = "previous_year", + Today = "today", + Yesterday = "yesterday", +} + +export enum Aggregator { + And = "and", + Or = "or", +} + +export type ConditionTreeNot = { condition: ConditionTree }; +export type ConditionTreeLeaf = { operator: Operator; field: string; value: unknown }; +export type ConditionTreeBranch = { aggregator: Aggregator; conditions: ConditionTree[] }; +export type ConditionTree = ConditionTreeBranch | ConditionTreeLeaf | ConditionTreeNot; diff --git a/packages/datasource-toolkit/src/interfaces/schema.ts b/packages/datasource-toolkit/src/interfaces/schema.ts new file mode 100644 index 0000000000..d7e9aeb6c3 --- /dev/null +++ b/packages/datasource-toolkit/src/interfaces/schema.ts @@ -0,0 +1,83 @@ +import { Filter, Operator, Aggregator } from "./query/selection"; + +export type CollectionSchema = { + actions: Array<{ + name: string; + scope: "single" | "bulk" | "global"; + forceDownload: boolean; + }>; + fields: { [fieldName: string]: FieldSchema }; + searchable: boolean; + segments: string[]; + validation?: Filter; +}; + +export type FieldSchema = + | ColumnSchema + | BelongsToSchema + | HasManySchema + | HasOneSchema + | BelongsToManySchema; + +export type ColumnSchema = { + columnType: ColumnType; + filterOperators: Set; + defaultValue: unknown; + enumValues?: string[]; + isPrimaryKey: boolean; + isReadOnly: boolean; + isSortable: boolean; + type: FieldTypes.Column; + validation?: + | { aggregator: Aggregator; conditions: ColumnSchema["validation"] } + | { operator: Operator; field: string; value: unknown }; +}; + +export type BelongsToSchema = { + foreignCollection: string; + foreignKey: string; + type: FieldTypes.ManyToOne; +}; + +export type HasManySchema = { + foreignCollection: string; + foreignKey: string; + type: FieldTypes.OneToMany; +}; + +export type HasOneSchema = { + foreignCollection: string; + foreignKey: string; + type: FieldTypes.OneToOne; +}; + +export type BelongsToManySchema = { + foreignCollection?: string; + foreignKey?: string; + otherField?: string; + throughCollection?: string; + type: FieldTypes.ManyToMany; +}; + +export type ColumnType = PrimitiveTypes | { [key: string]: ColumnType } | [ColumnType]; + +export enum PrimitiveTypes { + Boolean = "Boolean", + Date = "Date", + Dateonly = "Dateonly", + Enum = "Enum", + Json = "Json", + Number = "Number", + Point = "Point", + String = "String", + Timeonly = "Timeonly", + Uuid = "Uuid", +} + +export enum FieldTypes { + Column = "Column", + ManyToOne = "ManyToOne", + OneToOne = "OneToOne", + OneToMany = "OneToMany", + ManyToMany = "ManyToMany", +}