2020-01-20 07:43:25 +01:00
|
|
|
# Datastore Interface
|
|
|
|
|
|
|
|
Enum EventType {
|
|
|
|
Create,
|
|
|
|
Update,
|
|
|
|
Delete,
|
|
|
|
Restore,
|
|
|
|
}
|
|
|
|
|
|
|
|
Exception ModelDoesNotExist(model: Fqid);
|
|
|
|
Exception ModelExist(model: Fqid);
|
|
|
|
Exception ModelNotDeleted(model: Fqid);
|
2020-01-22 11:39:51 +01:00
|
|
|
Exception ModelLocked(key: Fqid | Fqfield | CollectionField);
|
|
|
|
Exception InvalidFormat(msg: string);
|
|
|
|
Exception InvalidRequest(msg: string);
|
|
|
|
|
|
|
|
# Note: Error returns via HTTP 400:
|
|
|
|
Interface ErrorResponse {
|
|
|
|
error: InvalidFormatData |
|
|
|
|
InvalidRequestData |
|
|
|
|
ModelDoesNotExistData |
|
|
|
|
ModelExistData |
|
|
|
|
ModelMotDeletedData |
|
|
|
|
ModelLockedData;
|
|
|
|
}
|
|
|
|
Interface InvalidFormatData {
|
|
|
|
type: 1;
|
|
|
|
msg: string;
|
|
|
|
}
|
|
|
|
Interface InvalidRequestData {
|
|
|
|
type: 2;
|
|
|
|
msg: string;
|
2020-03-19 14:14:45 +01:00
|
|
|
}
|
2020-01-22 11:39:51 +01:00
|
|
|
Interface ModelDoesNotExistData {
|
|
|
|
type: 3;
|
|
|
|
fqid: string;
|
|
|
|
}
|
|
|
|
Interface ModelExistData {
|
|
|
|
type: 4;
|
|
|
|
fqid: string;
|
|
|
|
}
|
|
|
|
Interface ModelNotDeletedData {
|
|
|
|
type: 5;
|
|
|
|
fqid: string;
|
|
|
|
}
|
|
|
|
Interface ModelLockedData {
|
|
|
|
type: 6;
|
|
|
|
key: string;
|
|
|
|
}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
## Writer
|
2020-01-22 11:39:51 +01:00
|
|
|
# Note: Different host and port than the reader!
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-01-22 11:39:51 +01:00
|
|
|
* Writes Events into the datastore.
|
2021-02-04 13:43:14 +01:00
|
|
|
* If multiple WriteRequests are given, they are fully executed one-by-one, meaning
|
|
|
|
* if a earlier event invalidates the locked_field of a later WriteRequest, an
|
|
|
|
* exception is thrown.
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/writer/write
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
|
|
|
* @throws ModelDoesNotExist
|
|
|
|
* @throws ModelExists
|
|
|
|
* @throws ModelLocked
|
|
|
|
* @throws InvalidFormat
|
|
|
|
* @throws ModelNotDeleted
|
|
|
|
*/
|
2021-02-04 13:43:14 +01:00
|
|
|
write(request: WriteRequest | WriteRequest[]): void publishes ModifiedFieldsEvent
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
Interface WriteRequest {
|
|
|
|
events: (CreateEvent | RestoreEvent | UpdateEvent | DeleteEvent)[];
|
|
|
|
information: {
|
|
|
|
<fqid>: Object
|
|
|
|
};
|
|
|
|
user_id: number;
|
|
|
|
locked_fields: {
|
|
|
|
<fqid>: Position;
|
2020-01-22 11:39:51 +01:00
|
|
|
<fqfield>: Position;
|
2020-12-10 13:12:40 +01:00
|
|
|
<CollectionField>: Position | CollectionFieldLock;
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Interface CreateEvent {
|
2020-01-22 11:39:51 +01:00
|
|
|
type: 'create';
|
2020-01-20 07:43:25 +01:00
|
|
|
fqid: Fqid;
|
2020-01-22 11:39:51 +01:00
|
|
|
fields: {
|
2020-01-20 07:43:25 +01:00
|
|
|
<field>: Value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-13 16:18:34 +01:00
|
|
|
/**
|
|
|
|
* Note: For deleting keys, they must be set to `None`. These keys will be removed from
|
|
|
|
* the model.
|
|
|
|
* list_fields can be used to partially update list fields: the values in `add` will be
|
|
|
|
* appended to the given field, the values in `remove` will be removed from the field.
|
|
|
|
* Either fields or list_fields must be given or an error will be thrown.
|
|
|
|
* An exception will be thrown if:
|
|
|
|
* - a field in list_fields is not empty and not a list
|
|
|
|
* - a field in list_fields contains other entries than strings or ints
|
|
|
|
* Other edge cases:
|
|
|
|
* - an element should be added that is already in the list: this element is ignored,
|
|
|
|
* other potentially given elements are still added as normal
|
|
|
|
* - an element should be removed that is not in the list: this element is ignored,
|
|
|
|
* other potentially given elements are still removed as normal
|
|
|
|
* - the field does not yet exist on the model:
|
|
|
|
* - add: same function as if the value was given in `fields`
|
|
|
|
* - remove: nothing happens
|
|
|
|
*/
|
2020-01-20 07:43:25 +01:00
|
|
|
Interface UpdateEvent {
|
2020-01-22 11:39:51 +01:00
|
|
|
type: 'update';
|
|
|
|
fqid: Fqid;
|
|
|
|
fields: {
|
|
|
|
<field>: Value;
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
2020-11-13 13:04:20 +01:00
|
|
|
list_fields: {
|
|
|
|
add: {
|
|
|
|
<field>: Value[];
|
|
|
|
}
|
|
|
|
remove: {
|
|
|
|
<field>: Value[];
|
|
|
|
}
|
|
|
|
}
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Interface RestoreEvent {
|
2020-01-22 11:39:51 +01:00
|
|
|
type: 'restore';
|
2020-01-20 07:43:25 +01:00
|
|
|
fqid: Fqid;
|
|
|
|
}
|
|
|
|
|
|
|
|
Interface DeleteEvent {
|
2020-01-22 11:39:51 +01:00
|
|
|
type: 'delete';
|
2020-01-20 07:43:25 +01:00
|
|
|
fqid: Fqid;
|
|
|
|
}
|
|
|
|
|
2021-01-12 14:58:21 +01:00
|
|
|
// Collection fields can not only be locked to a specific position, but also filtered
|
|
|
|
// first, e.g. when selecting all models from a specific meeting. WARNING: the filter
|
|
|
|
// should always contain an equals check with the meeting_id, since this will be
|
|
|
|
// indexed. Other filters can lead to long query times.
|
2020-12-10 13:12:40 +01:00
|
|
|
Interface CollectionFieldLock {
|
|
|
|
position: Position;
|
|
|
|
filter: Filter;
|
|
|
|
}
|
|
|
|
|
2020-01-22 11:39:51 +01:00
|
|
|
// Note: The modified fqfields include:
|
|
|
|
// - all updated fqfields
|
|
|
|
// - all deleted fqfields
|
|
|
|
// - all fqfields of all deleted models
|
|
|
|
// - all fqfields of all created models
|
|
|
|
// - all fqfields of all restored models (logically the same as created)
|
|
|
|
Event ModifiedFieldsEvent on topic ModifiedFields {
|
|
|
|
modified: Fqfield[];
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
|
2020-01-22 11:39:51 +01:00
|
|
|
/**
|
2020-06-09 12:36:27 +02:00
|
|
|
* Reserves multiple sequential ids for the given collection und returns them.
|
2020-05-07 10:13:07 +02:00
|
|
|
* Url: POST to /internal/datastore/writer/reserve_ids
|
2020-01-22 11:39:51 +01:00
|
|
|
*/
|
2020-06-09 12:36:27 +02:00
|
|
|
reserveIds(collection: Collection, amount: number): Id[]
|
2020-01-20 07:43:25 +01:00
|
|
|
|
2020-03-19 14:14:45 +01:00
|
|
|
|
2020-01-22 11:39:51 +01:00
|
|
|
## Reader
|
|
|
|
# Note: Different host and port than the writer!
|
|
|
|
|
|
|
|
/** Common notes:
|
|
|
|
* - parameter `position`: Optional, if given reads the data to this position.
|
2020-03-19 14:14:45 +01:00
|
|
|
* - parameter `mapped_fields`: List of fields that should only be present in the response.
|
|
|
|
* - parameter `get_deleted_models`: Optional, defines which models to return
|
|
|
|
* - DeletedModelsBehaviour.NO_DELETED: (Default) only non-deleted models are returned.
|
|
|
|
* get throws a ModelDoesNotExist error if the given
|
|
|
|
* model is deleted.
|
|
|
|
* - DeletedModelsBehaviour.ONLY_DELETED: only deleted models are returned. get throws
|
|
|
|
* a ModelNotDeleted if the given model is not deleted.
|
|
|
|
* - DeletedModelsBehaviour.ALL_MODELS: all models are returned
|
2020-01-22 11:39:51 +01:00
|
|
|
* - All operations adds the fields `meta_position` and `meta_deleted` to the models.
|
|
|
|
* - The InvalidFormat exception can always be thrown, if the requested formats are
|
|
|
|
* wrong, including something like empty collections, ...
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
|
|
|
|
2020-03-19 14:14:45 +01:00
|
|
|
Enum DeletedModelsBehaviour {
|
|
|
|
NO_DELETED = 1,
|
|
|
|
ONLY_DELETED = 2,
|
|
|
|
ALL_MODELS = 3
|
|
|
|
}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-03-19 14:14:45 +01:00
|
|
|
* Returns a model by fqid.
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/get
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
|
|
|
* @throws ModelDoesNotExist
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-03-19 14:14:45 +01:00
|
|
|
get(fqid: Fqid, mapped_fields?: Field[], position?: Position, get_deleted_models?: DeletedModelsBehaviour): Partial<Model>;
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-03-19 14:14:45 +01:00
|
|
|
* Returns multiple models.
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/get_many
|
|
|
|
*
|
2020-04-28 11:59:28 +02:00
|
|
|
* Can either be called with a list of Fqfields or with a list of specific request
|
|
|
|
* objects that map a collection to the needed ids and fields. If both the lower and
|
|
|
|
* the higher level mapped_fields are given, the higher level one is merged into all
|
|
|
|
* lower level ones. If Fqfields are given, the mapped_fields are ignored.
|
2020-03-19 14:14:45 +01:00
|
|
|
* If an id is not found, it is not included in the response instead of throwing a
|
|
|
|
* ModelDoesNotExist.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-04-28 11:59:28 +02:00
|
|
|
* @returns A mapping of collection to ids to models. Example:
|
|
|
|
* {
|
|
|
|
* "collection1": {
|
|
|
|
* "id1": {
|
|
|
|
* "field1": "foo",
|
|
|
|
* "field2": "bar",
|
|
|
|
* },
|
|
|
|
* },
|
|
|
|
* "collection2": {
|
|
|
|
* "id2": {
|
|
|
|
* "field3": 42,
|
|
|
|
* },
|
|
|
|
* },
|
|
|
|
* }
|
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-05-05 11:49:17 +02:00
|
|
|
get_many(requests: GetManyRequest[] | Fqfield[], mapped_fields?: Field[], position?: Position, get_deleted_models?: DeletedModelsBehaviour): Map<Collection, Map<Id, Partial<Model>>>;
|
2020-01-20 07:43:25 +01:00
|
|
|
|
2020-03-19 14:14:45 +01:00
|
|
|
Interface GetManyRequest {
|
|
|
|
collection: Collection;
|
|
|
|
ids: Id[];
|
|
|
|
mapped_fields?: Field[];
|
|
|
|
}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Returns all models of one collection.
|
|
|
|
* Url: POST to /internal/datastore/reader/get_all
|
|
|
|
*
|
|
|
|
* It is not possible to specify a position, so this method cannot be used if the user
|
|
|
|
* browses the history. It should be noted that it is highly disencouraged to use this
|
|
|
|
* method because it might return a huge amount of data.
|
|
|
|
*
|
2020-05-19 16:04:48 +02:00
|
|
|
* @returns see get_many
|
2020-05-05 11:49:17 +02:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-05-19 16:04:48 +02:00
|
|
|
get_all(collection: Collection, mapped_fields?: Field[], get_deleted_models?: DeletedModelsBehaviour): Map<Id, Partial<Model>>;
|
2020-01-20 07:43:25 +01:00
|
|
|
|
2020-10-16 13:11:00 +02:00
|
|
|
/**
|
|
|
|
* Returns all models.
|
|
|
|
* Url: POST to /internal/datastore/reader/get_everything
|
|
|
|
*
|
|
|
|
* This is a dev route only!
|
|
|
|
*
|
|
|
|
* @returns The example data format: A mapping of a collection to a list of models.
|
|
|
|
*/
|
|
|
|
get_everything(get_deleted_models?: DeletedModelsBehaviour): Map<Collection, Model[]>;
|
|
|
|
|
2020-12-10 13:12:40 +01:00
|
|
|
interface FilterResponse {
|
2021-01-12 14:58:21 +01:00
|
|
|
position: Position;
|
2020-12-10 13:12:40 +01:00
|
|
|
data: Map<Id, Partial<Model>>;
|
|
|
|
}
|
|
|
|
|
2020-01-20 07:43:25 +01:00
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Returns all models of one collection that satisfy the filter condition.
|
|
|
|
* Url: POST to /internal/datastore/reader/filter
|
|
|
|
*
|
2020-12-10 13:12:40 +01:00
|
|
|
* The global max position of the datastore is returned next the the filtered data.
|
2020-05-05 11:49:17 +02:00
|
|
|
* This method does not take a position and can not be used when browsing the history.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-05-19 16:04:48 +02:00
|
|
|
* @returns see get_many
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-12-10 13:12:40 +01:00
|
|
|
filter(collection: Collection, filter: Filter, mapped_fields?: Field[]): FilterResponse
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/exists
|
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* See `filter`, returns true, if at least one model was found. The returned position is
|
|
|
|
* the highest position in the complete datastore.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-01-22 11:39:51 +01:00
|
|
|
exists(collection: Collection, filter: Filter): {exists: boolean; position: Position;}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/count
|
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* See `filter`, returns the amount of found models. The returned position is
|
|
|
|
* the highest position in the complete datastore.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-01-22 11:39:51 +01:00
|
|
|
count(collection: Collection, filter: Filter): {count: number; position: Position;}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/min
|
|
|
|
*
|
2020-03-19 14:14:45 +01:00
|
|
|
* Executes a min aggregation on all models of one collection on
|
|
|
|
* the given field that satisfy the filter condition.
|
2020-04-08 11:20:55 +02:00
|
|
|
* The field is cast to int by default. If aggregation of another field type is needed,
|
|
|
|
* a valid type can be passed via the type parameter.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-04-08 11:20:55 +02:00
|
|
|
min(collection: Collection, filter: Filter, field: Field, type?: string): {min: Value; position: Position;}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
/**
|
2020-05-05 11:49:17 +02:00
|
|
|
* Url: POST to /internal/datastore/reader/max
|
2020-04-08 11:20:55 +02:00
|
|
|
* Analogous to min.
|
2020-01-20 07:43:25 +01:00
|
|
|
*
|
2020-01-22 11:39:51 +01:00
|
|
|
* @throws InvalidFormat
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
2020-04-08 11:20:55 +02:00
|
|
|
max(collection: Collection, filter: Filter, field: Field, type?: string): {max: Value; position: Position;}
|
2020-01-20 07:43:25 +01:00
|
|
|
|
|
|
|
Type Filter = And | Or | Not | FilterOperator
|
|
|
|
|
|
|
|
/**
|
2020-01-22 11:39:51 +01:00
|
|
|
* The filter predicate. M[field] states the value of the field of a model M.
|
|
|
|
* For all operations the predicate if true, if `M[field] <op> value` is true.
|
2020-01-20 07:43:25 +01:00
|
|
|
*/
|
|
|
|
Interface FilterOperator {
|
|
|
|
field: Field;
|
|
|
|
value: Value | null;
|
2020-04-08 11:20:55 +02:00
|
|
|
operator: '=' | '!=' | '<' | '>' | '>=' | '<=';
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Interface Not {
|
2020-03-19 14:14:45 +01:00
|
|
|
not_filter: Filter;
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Interface And {
|
2020-03-19 14:14:45 +01:00
|
|
|
and_filter: Filter[];
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Interface Or {
|
2020-03-19 14:14:45 +01:00
|
|
|
or_filter: Filter[];
|
2020-01-20 07:43:25 +01:00
|
|
|
}
|