Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/datasource-toolkit/docs/collection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
> **_NOTE:_** Implementing a Collection manually is an advanced notion. If you're just starting with Forest Admin, you should start your project with one of our existing Datasources.

## Collection

### What is a Collection?

A Collection is a set of data elements displayed in a Table view (by default), with rows (i.e. records) and columns (i.e. fields). A Collection has a specified number of columns, but can have any number of rows.

> **_NOTE:_** Implementing a Collection manually is an advanced notion. If you're just starting with Forest Admin, you should skip this for now.
7 changes: 7 additions & 0 deletions packages/datasource-toolkit/docs/datasource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
> **_NOTE:_** Implementing a Collection manually is an advanced notion. If you're just starting with Forest Admin, you should start your project with one of our existing Datasources.

## Datasource

### What is a Datasource?

A Datasource represent a list of [Collection](#collection). It is mainly used to aggregate multiples collections from an external source of data.
6 changes: 6 additions & 0 deletions packages/datasource-toolkit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* [[include:packages/datasource-toolkit/docs/datasource.md]]
* [[include:packages/datasource-toolkit/docs/collection.md]]
* @module Datasource-toolkit
*/

export * from "./interfaces/schema";
export * from "./interfaces/collection";
export * from "./interfaces/action";
Expand Down
43 changes: 43 additions & 0 deletions packages/datasource-toolkit/src/interfaces/action.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import { Projection } from "./query/projection";
import { RecordData } from "./query/record";

/**
* Interface that Actions should implements
*/
export interface Action {
/**
* Function called when an Action is triggered
* @params formValues
* @params selection
*/
execute(formValues: RecordData, selection?: Selection): Promise<ActionResponse>;
/**
* Function called to retrieve an action form
* @params selection
* @params changedField
* @params formValues
*/
getForm(
selection?: Selection,
changedField?: string,
formValues?: RecordData
): Promise<ActionForm>;
}

/**
* Represent an action form
*/
export interface ActionForm {
fields: ActionField[];
}

/**
* Represent an action field
*/
export interface ActionField {
field: string;
description?: string;
Expand All @@ -27,6 +47,7 @@ export interface ActionField {
collectionName?: string; // When type === ActionFieldType.Collection
}

/** Enumeration of supported action field types */
export enum ActionFieldType {
Boolean = "Boolean",
Collection = "Collection",
Expand All @@ -42,6 +63,7 @@ export enum ActionFieldType {
StringList = "String[]",
}

/** Enumeration of supported action response types */
export enum ActionResponseType {
Success,
Error,
Expand All @@ -50,6 +72,11 @@ export enum ActionResponseType {
Redirect,
}

/**
* Represent a success action response
*
* It will trigger a green/success toastr when calling the action
*/
export type SuccessReponse = {
type: ActionResponseType.Success;
message: string;
Expand All @@ -59,27 +86,43 @@ export type SuccessReponse = {
};
};

/**
* Represent an error action response
*
* It will trigger a red/danger toastr when calling the action
*/
export type ErrorResponse = SuccessReponse & { type: ActionResponseType.Error };

/** Represent an action response of type webhook */
export type WebHookReponse = {
type: ActionResponseType.Webhook;
/** URL of the webhook */
url: string;
/** Method used to call the webhook */
method: "GET" | "POST";
/** A set of headers to happen to the webhook */
headers: { [key: string]: string };
/** The body to use when calling the webhook */
body: unknown;
};

/** Represent an action response of type file */
export type FileResponse = {
type: ActionResponseType.File;
/** Mime type of the response */
mimeType: string;
/** A stream of the file to respond */
stream: ReadableStream;
};

/** Represent an action response of type redirection */
export type RedirectResponse = {
type: ActionResponseType.Redirect;
/** Path to redirect to */
path: string;
};

/** Represent the type of action response */
export type ActionResponse =
| SuccessReponse
| ErrorResponse
Expand Down
64 changes: 64 additions & 0 deletions packages/datasource-toolkit/src/interfaces/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,91 @@ import { CompositeId, RecordData } from "./query/record";
import { PaginatedFilter, Filter } from "./query/selection";
import { CollectionSchema } from "./schema";

/**
* Represent a datasource
*
* A datasource is a list of collections from a single source of data
*/
export interface DataSource {
/** List of collections associated with the datasource */
get collections(): Collection[];

/**
* Get a collection by name from the datasource
* @params name The name of the collection
* @return The collection when found
*/
getCollection(name: string): Collection;
}

/**
* Represent a collection
*/
export interface Collection {
/** The datasource the collection is associated with */
get dataSource(): DataSource;
/** Name of the collection */
get name(): string;
/** Schema of the collection */
get schema(): CollectionSchema;

/**
* Get an action by name
*
* @params name The name of the action to retrieve
* @return The action when it exists
*/
getAction(name: string): Action;

/**
* Get record data by id
*
* @params id The requested record id
* @params projection The requested record projection
* @return An promise of a record data
*/
getById(id: CompositeId, projection: Projection): Promise<RecordData>;

/**
* Create a list of records
*
* @params data An array of records data to create
* @return An promise containing the created record
*/
create(data: RecordData[]): Promise<RecordData[]>;

/**
* List records based on specific list of filters
*
* @params filter A filter representing a selection of records to return
* @params projection The requested record projection
* @return An promise containing a list of records matching the parameters provided
*/
list(filter: PaginatedFilter, projection: Projection): Promise<RecordData[]>;

/**
* Update a list of records
*
* @params filter A filter representing a selection of records to update
* @params patch The patch to apply of records selected by the filter
* @return An promise containing the list of records that were updated
*/
update(filter: Filter, patch: RecordData): Promise<void>;

/**
* Delete a list of records
*
* @params filter A filter representing a selection of records to delete
* @return An empty promise
*/
delete(filter: Filter): Promise<void>;

/**
* Aggregate filtered records
*
* @params filter A filter representing a selection of records to aggregate
* @params aggregation The aggregation operation
* @return The aggregated results
*/
aggregate(filter: PaginatedFilter, aggregation: Aggregation): Promise<AggregateResult[]>;
}
59 changes: 54 additions & 5 deletions packages/datasource-toolkit/src/interfaces/schema.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,105 @@
import { Filter, Operator, Aggregator } from "./query/selection";

/**
* Schema representation of a collection
*
* It is used to generated the `.forestadmin-schema.json` file
*/
export type CollectionSchema = {
actions: Array<{
name: string;
scope: "single" | "bulk" | "global";
forceDownload?: boolean;
}>;
/**
* Declare action(s) associated with a collection
*/
actions: Array<ActionSchema>;
/** Declare the list of fields of the collection */
fields: { [fieldName: string]: FieldSchema };
/** When "true", the collection is searchable */
searchable: boolean;
/** Declare a list of segment name */
segments: string[];
/**
* Declare a filter that every records should match
*/
validation?: Filter;
};

/**
* Schema representation of an action
*/
export type ActionSchema = {
/** Visible name of the action */
name: string;
/**
* Scope of the action.
* - "single": the action is only available for one selected record at a time
* - "bulk": the action will be available when you click on one or several desired records
* - "global": the action is always available and will be executed on all records
*/
scope: "single" | "bulk" | "global";
/** When "true", the action response will force a file download */
forceDownload?: boolean;
};

/** Schema representation of a field */
export type FieldSchema =
| ColumnSchema
| ManyToOneSchema
| OneToManySchema
| OneToOneSchema
| ManyToManySchema;

/** Schema representation of a column */
export type ColumnSchema = {
/** Type of the column */
columnType: ColumnType;
/** List of all the supported operators */
filterOperators: Set<Operator>;
defaultValue?: unknown;
/** Array of possible values for the column */
enumValues?: string[];
/** When "true", the column is considerer as a primary key */
isPrimaryKey?: boolean;
/** When "true", the column is considerer as a read-only */
isReadOnly?: boolean;
/** When "true", the column is considerer as a sortable */
isSortable?: boolean;
type: FieldTypes.Column;
validation?:
| { aggregator: Aggregator; conditions: ColumnSchema["validation"] }
| { operator: Operator; field: string; value: unknown };
};

/** Schema representation of a many to one relationship */
export type ManyToOneSchema = {
/** Targetted collection */
foreignCollection: string;
/** Targetted key of the collection */
foreignKey: string;
type: FieldTypes.ManyToOne;
};

/** Schema representation of a one to many relationship */
export type OneToManySchema = {
/** Targetted collection */
foreignCollection: string;
/** Targetted key of the collection */
foreignKey: string;
type: FieldTypes.OneToMany;
};

/** Schema representation of a one to one relationship */
export type OneToOneSchema = {
/** Targetted collection */
foreignCollection: string;
/** Targetted key of the collection */
foreignKey: string;
type: FieldTypes.OneToOne;
};

/** Schema representation of a many to many relationship */
export type ManyToManySchema = {
/** Targetted collection */
foreignCollection?: string;
/** Targetted key of the collection */
foreignKey?: string;
otherField?: string;
throughCollection?: string;
Expand All @@ -61,6 +108,7 @@ export type ManyToManySchema = {

export type ColumnType = PrimitiveTypes | { [key: string]: ColumnType } | [ColumnType];

/** Enumeration of supported primitive types */
export enum PrimitiveTypes {
Boolean = "Boolean",
Date = "Date",
Expand All @@ -74,6 +122,7 @@ export enum PrimitiveTypes {
Uuid = "Uuid",
}

/** Enumeration of supported field types */
export enum FieldTypes {
Column = "Column",
ManyToOne = "ManyToOne",
Expand Down
6 changes: 1 addition & 5 deletions typedoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
"excludePrivate": true,
"hideGenerator": true,
"entryPointStrategy": "packages",
"entryPoints": [
"packages/agent",
"packages/datasource-toolkit",
"packages/datasource-sequelize"
],
"entryPoints": ["packages/agent", "packages/datasource-toolkit", "packages/datasource-sequelize"],
"includes": [""],
"out": "dist-docs"
}