clean up docs that are in the wiki

This commit is contained in:
Finn Stutzenstein 2021-07-05 07:57:41 +02:00
parent 610a10712d
commit 65bd70375d
No known key found for this signature in database
GPG Key ID: 9042F605C6324654
18 changed files with 5 additions and 976 deletions

View File

@ -1 +0,0 @@
<mxfile host="www.draw.io" modified="2020-01-05T12:52:00.802Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" etag="kaO24BhHNubXhychog86" version="12.4.8" type="device"><diagram id="28u5DdNZLLq8z4gjoHRL" name="Page-1">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</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@ -1,53 +0,0 @@
# Documentation for the streaming Client-Server-Protocol
0) Notation
Special words with a definied meaning are surrounded by underscores. E.g. the
word _request_ would be the unique term for the http-message send form the
client to the server.
1) What?
This document provides the specification for the unidirectional server to client
streaming. This is realized with HTTP1.1 streaming responses. The general
pattern starts with an _initial request_ form the client to a streaming endpoint.
The server can send _packets_ of data to the client whenever the server wants to.
The connection stays open, until one side closes it manually.
2) Production usage
To keep the amount of tcp connections, connection establishments and the
associated overhead low, it is advised to ensure HTTP2 in production to make use
out of tcp multiplexing. All streaming (and other non-streaming) content is
multiplexed through a single tcp connection.
3) Request meta information
The method, headers and payload of the initiating request are arbitrary and not
bounded to an restriction. The response content type header must be set to
`application/octet-stream` to avoid unwanted buffering in some browsers.
Otherwise all other headers are free to choose. If errors happen before the
response was send, the response status code can be a non-200 one.
4) Message format: Packets
The server sends JSON objects (with an object as the outer most JSON-object) as
the _content_ of a package. So one package contains one JSON object. The content
is terminated with a line feed (0x0A). The terminated content is the _payload_,
which is send over the connection. It must be taken care of not sending
prettified JSON, to not include any unwanted line feeds and to not send
unnecessary bytes. To clearify, the terminating line feed must be the only line
feed in the payload. Note that linefeeds must be escaped in JSON-strings (see
RFC 7159).
5) Errors
If an error happen in an established connection, the status code can not be
altered. To indicate errors, the following convention is used: To send an error
to the client, send a JSON-object with the single key `error` with an object as
value (e.g. {"error": {"detail": "Failed successfully"}}`). The server must
close the connection afterwards, because the client cannot react to the error.
For normal packets, it is forbidden to send content with an error key in the
outer most JSON-object.
5) Outlook
There might be an additional reserved keys. The protocol may be extended to:
- a watchdog: the server sends a ping in regular time intervals. The client
take note of them and notices, if there was a missing ping. E.g. the timeout
for the client is twice the send interval time. If a missing ping was
detected, the connection is reestablished.

File diff suppressed because one or more lines are too long

View File

@ -1,80 +0,0 @@
// Actions Service Interface
/**
* Executes multiple actions in the context of the user given by the user_id.
* There are two modes of execution:
* atomic=true (default):
* All actions are validated in common, so if one action or one payload of
* one action fails, the request is aborted with an ActionException indicating
* the problematic action with both indices.
* => The whole request is handled atomically.
* atomic=false:
* Each action is validated by it's own. If there is an error, the error must
* be reported via an ActionError in the ActionsResponse. The actions result
* is not written into the Datastore. It might raise an ActionException if the
* single write request to the Datastore fails (e.g. because of locking there)
*
* For general, non specific action-related, errors an ActionException is used.
*
* @throws ActionException
*/
handle_request(payload: Action[], user_id: Id, atomic?: boolean): ActionsResponse
interface Action {
action: string;
data: object[];
}
interace ActionsResponse {
success: true;
message: string;
/**
* This is a potentially double-array with entries for each action
* and, if not null, an array for each data provided for each action.
*
* If an action does not produce a result, the inner array can be omitted and
* null can be used. If the inner array is given, each entry must be an object
* with the result (e.g. for a create action `{id: <the id>}`) or null.
*
* E.g. for valid arrays (two actions with two data entries each):
* - [null, null]
* - [null, [null, null]]
* - [null, [{id: 2}, null]]
* - [[{id: 2}, {id: 3}], [{id: 5}, {id: 8}]]
*
*
* To report errors, use the ActionError format!
**/
results: (object[] | ActionError | null )[]
}
/**
* If action_data_error_index is given, the error can be directly associated with the
* respective action data. If not, the error is of general fashion and/or not directly
* associated with a single action data.
*
* Note: ActionError can only be used if atomic=false.
*/
interface ActionError {
success: false;
message: string;
action_data_error_index?: number;
}
/**
* JSON resonse with a status code of !=200. If a specific action raised the error,
* use the action_error_index and action_data_error_index to indicate the errored
* action and data, respectively. If there were general errors, both indices must be
* omitted or null.
*
* If the atomic flag was false in the request, it is not allowed to send
* action-specific errors with this exception. It must be responded with an
* ActionError through ActionsResponse (resulting in a status code of 200).
*/
Exception ActionException {
success: false;
message: string;
action_error_index?: number;
action_data_error_index?: number;
}

View File

@ -1,131 +0,0 @@
// Description of the authentication-service
// It is listening on port '9004'
// Routes with a prefix 'secure' are protected routes, that can only accessed with a valid ticket.
// That are routes, that call internally 'authenticate'
// The properties of this interface have to be passed as HTTP-headers in a request.
Interface Ticket {
authentication: string,
cookies: {
refreshId: string,
[name: string]: string
}
}
// This describes, which information is received by requesting `secure/authenticate`.
Interface LoginInformation {
userId: number;
sessionId: string;
}
/**
* Describes an http-response, which is sent back to any requesting service.
*/
Interface Response <T> {
set-authentication-header: string // If an old access-token is expired and refreshed, it is set as authentication-header.
// This determines if a request was successful.
success: boolean,
// This sends back a describing message. For example, the reason of a failured request.
message: string,
// Optional data, which is appended, if a request was successful.
data?: T
}
/**
* The credentials for login/authentication are not valid.
*/
Exception InvalidCredentials {
success: false,
message: string
}
/**
* POST to /system/auth/login
*
* A user can login with its credentials for authentication.
* If they are correct, the service answers with a signed Token and sets a cookie, containing the sessionId of the client.
*
* If they aren't correct, the service throws an error.
*
* @throws InvalidCredentials
*/
login (username: string, password: string): Response<void>;
/**
* POST to /internal/auth/authenticate
*
* This will be a library to act as part of the auth-service. The other services have not to
* request the auth-service for authentication. Instead, they use this library-function in their own
* and receive knowledge about authentication without request.
*
* Throws an exception, if the token is not valid. E.g. if the signature is not valid.
*
* @throws InvalidCredentials
*/
authenticate (ticket: Ticket): Response<LoginInformation>;
/**
* POST to /system/auth/who-am-i
*
* A request to get knowledge about themselves. This information is contained in the payload of
* a Token. So, this function handles the refreshing of a Token.
* Expects a jwt as string in a cookie (called 'refreshId').
*
* Sends back a new Token (passed as http-header).
*
* Throws an exception, if the cookie is empty, the transmitted sessionId is wrong or the signature is wrong.
*
* @throws InvalidCredentials
*/
who-am-i (refreshId: string): Response<LoginInformation>;
/**
* POST to /system/auth/secure/clear-session-by-id
*
* Function to sign out one specific client from a user by its corresponding session-id.
*/
secure/clear-session-by-id (sessionId: string, ticket: Ticket): Response<void> publishes LogoutSessionEvent;
/**
* POST to /system/auth/secure/clear-all-session-except-themselves
*
* Function to kill all current opened sessions from one user except the one, which is requesting.
*/
secure/clear-all-sessions-except-themselves (sessionId: string, ticket: Ticket): Response<void> publishes LogoutSessionEvent;
/**
* POST to /system/auth/secure/logout
*
* The service deletes the session depending on the given Token.
*
* @throws InvalidCredentials
*/
secure/logout (ticket: Ticket): Response<void> publishes LogoutSessionEvent;
/**
* GET to system/auth/secure/list-sessions
*
* Returns all currently active sessions.
*
* @returns an array containing currently active sessions.
*/
secure/list-sessions (ticket: Ticket): Response<{sessions: string[]}>;
/**
* POST to /internal/auth/hash
*
* Hashes a given value. A random salt (64bit) is generated and added to the hashed value.
*
* @returns the hashed value. The hashed value is structured as follows: [salt + hash].
*/
hash (toHash: string): Response<{hash: string}>;
/**
* POST to /internal/auth/is-equals
*
* Compares a given value with an given hash.
*
* @returns a boolean, if the hashed value of the given value is equals to the passed hash.
*/
is-equals (toHash: string, toCompare: string): Response<{isEquals: boolean}>;

View File

@ -1,145 +0,0 @@
/**
* SyntaxError is returned, when the syntax of the request body is wrong.
* This error is returned on the beginning of a request with http-status-code
* 400.
*/
Exception SyntaxError(msg: string);
/**
* JsonError is returned, when the body does not contain valid json. This error
* is returned on the beginning of a request with http-status-code 400.
*/
Exception JsonError(msg: string);
/**
* ValueError is returned, when the value of a field does not have the expected
* format. E.g. there is an indicated relation to a key, but the data are no
* foreign ids/fqids. The exception may happen, if the stream is used at
* runtime, because this cannot be detected when the caller makes the request.
*/
Exception ValueError(msg: string);
/**
* InternalError is an unexpected error on the server side. When it happens at
* the beginning of a request, an http-status-code 500 is used. But it can also
* happen after the first data have been streamed to the client. The error does
* not contain any useful information. More information can be found in the
* server log. This is the only error that generates a server log message.
*/
Expection InternalError(msg: string);
/**
* This methods subscribes to a list of given request. The response is a stream
* (language dependent) updating all models according to the ModelRequest if new
* data is available. On subscription, initial data must be pushed to the caller
* as soon as possible. The stream can be closed by closing the stream (e.g. the
* underlying network connection).
*
* @throws SyntaxError
* @throws JsonError
* @throws ValueError
* @throws InternalError
*/
subscribe(request: ModelRequest[]): stream<ModelData>;
/**
* This is the main interface for requesting models in a structured, nested way.
* The initial request targets some models as the root models of one collection
* with all the same fields. This build a tree of dependencies, because the
* value of some fields may be a GenericRelationFieldDescriptor again.
*
* For a description of `fields` and `collection`, see
* GenericRelationFieldDescriptor and RelationFieldDescriptor.
*
* `ids`: This is a list of ids for a collection, that should be provided. All
* models, that the user can see, must be included in the response. The model
* fields are handled according to `GenericRelationFieldDescriptor`.
*/
Interface ModelRequest extends Fields {
ids: ID[];
collection: Collection;
}
Interface Fields {
fields: {
[field: Field]: GenericRelationFieldDescriptor
| RelationFieldDescriptor
| StructuredFieldDescriptor
| null;
}
}
/**
* For an overview, see `ModelRequest`.
*
* `fields` (inherited from `Fields`, see above):
* Regardless of the value of a field, the restricted values are given in the
* response (multiple for structured fields). If the restricted value is null,
* the field must be included here.
*
* If the value is not null, it is indicated, that there is a reference to
* follow. There are three types of values:
* - GenericRelationFieldDescriptor: The reference is a generic one. This means,
* that the actual value from the model is a fqid or an array of fqids.
* - RelationFieldDescriptor: A collection is given, so it can be expected, that
* the actual model value is an id or an array of ids.
* - StructuredFieldDescriptor: The field is a template field and should be
* handled in this way.
*/
Interface GenericRelationFieldDecriptor extends Fields {
type: 'generic-relation' | 'generic-relation-list';
}
/**
* For an overview, see `ModelRequest`. For `fields`, see
* GenericRelationFieldDescriptor.
*
* `collection`:
* This is the collection, the ids are associated to. The ids are provided with
* two different ways:
* - In a ModelRequest, the ids are given.
* - If this interface is used in a field indication a relation, the id(s) are
* given by the actual model data.
*/
Interface RelationFieldDescriptor extends Fields {
type: 'relation' | 'relation-list';
collection: Collection;
}
/**
* Structured Fields: A field with `$` as a placeholder can be structured. The
* `$` is a template placeholder. The `template field` holds a list of strings
* with options to insert into `$`. All these fields are the `structured
* fields`. Example:
* B_$_ids is the template field. Its value may be `["1", "test"]`, so the
* structured fields are B_1_ids and B_test_ids.
*
* This interface indicates, that there are structured fields related to the
* given template field, that should be resolved. If one would give `B_$_ids:
* null` without using this interface, the response would be just the template
* field `B_$_ids` and no structured fields.
*
* For just resolving values, leave `values` out (hence the optional parameter).
*
* For retrieveing a single structured field, use `RelationFieldDescriptor`. E.g.
* when trying to get the groups for a user from meeting 2, use `group_2_ids`
* directly and do not bother with structured fields. The terminology is a
* `specific structured field`.
*
* If the values of all structured fields are references, use the `values`
* parameter analog to the `fields` paramter as documented in
* `GenericRelationFieldDescriptor`.
*/
Interface StructuredFieldDecriptor {
type: 'template',
values?: GenericRelationFieldDescriptor | RelationFieldDescriptor;
}
/**
* This structure holds all data given by the called service as a map of
* fqfields to the fields values.
*/
Interface ModelData {
[fqfield: Fqfield]: Value;
}

View File

@ -1,332 +0,0 @@
# Datastore Interface
Enum EventType {
Create,
Update,
Delete,
Restore,
}
Exception ModelDoesNotExist(model: Fqid);
Exception ModelExist(model: Fqid);
Exception ModelNotDeleted(model: Fqid);
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;
}
Interface ModelDoesNotExistData {
type: 3;
fqid: string;
}
Interface ModelExistData {
type: 4;
fqid: string;
}
Interface ModelNotDeletedData {
type: 5;
fqid: string;
}
Interface ModelLockedData {
type: 6;
keys: string[];
}
## Writer
# Note: Different host and port than the reader!
/**
* Writes Events into the datastore.
* 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.
* Url: POST to /internal/datastore/writer/write
*
* @throws ModelDoesNotExist
* @throws ModelExists
* @throws ModelLocked
* @throws InvalidFormat
* @throws ModelNotDeleted
*/
write(request: WriteRequest | WriteRequest[]): void publishes ModifiedFieldsEvent
Interface WriteRequest {
events: (CreateEvent | RestoreEvent | UpdateEvent | DeleteEvent)[];
information: {
<fqid>: Object;
};
user_id: number;
locked_fields: {
<fqid>: Position;
<fqfield>: Position;
<CollectionField>: Position | CollectionFieldLock | CollectionFieldLock[];
}
}
Interface CreateEvent {
type: 'create';
fqid: Fqid;
fields: {
<field>: Value;
}
}
/**
* 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
*/
Interface UpdateEvent {
type: 'update';
fqid: Fqid;
fields: {
<field>: Value;
}
list_fields: {
add: {
<field>: Value[];
}
remove: {
<field>: Value[];
}
}
}
Interface RestoreEvent {
type: 'restore';
fqid: Fqid;
}
Interface DeleteEvent {
type: 'delete';
fqid: Fqid;
}
// 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.
// If no filter is given, it has the same meaning as just giving the position.
Interface CollectionFieldLock {
position: Position;
filter: Filter | null;
}
// 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[];
}
/**
* Reserves multiple sequential ids for the given collection und returns them.
* Url: POST to /internal/datastore/writer/reserve_ids
*/
reserveIds(collection: Collection, amount: number): Id[]
## Reader
# Note: Different host and port than the writer!
/** Common notes:
* - parameter `position`: Optional, if given reads the data to this position.
* - 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
* - 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, ...
*/
Enum DeletedModelsBehaviour {
NO_DELETED = 1,
ONLY_DELETED = 2,
ALL_MODELS = 3
}
/**
* Returns a model by fqid.
* Url: POST to /internal/datastore/reader/get
*
* @throws ModelDoesNotExist
* @throws InvalidFormat
*/
get(fqid: Fqid, mapped_fields?: Field[], position?: Position, get_deleted_models?: DeletedModelsBehaviour): Partial<Model>;
/**
* Returns multiple models.
* Url: POST to /internal/datastore/reader/get_many
*
* 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.
* If an id is not found, it is not included in the response instead of throwing a
* ModelDoesNotExist.
*
* @returns A mapping of collection to ids to models. Example:
* {
* "collection1": {
* "id1": {
* "field1": "foo",
* "field2": "bar",
* },
* },
* "collection2": {
* "id2": {
* "field3": 42,
* },
* },
* }
*
* @throws InvalidFormat
*/
get_many(requests: GetManyRequest[] | Fqfield[], mapped_fields?: Field[], position?: Position, get_deleted_models?: DeletedModelsBehaviour): Map<Collection, Map<Id, Partial<Model>>>;
Interface GetManyRequest {
collection: Collection;
ids: Id[];
mapped_fields?: Field[];
}
/**
* 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.
*
* @returns see get_many
* @throws InvalidFormat
*/
get_all(collection: Collection, mapped_fields?: Field[], get_deleted_models?: DeletedModelsBehaviour): Map<Id, Partial<Model>>;
/**
* 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[]>;
interface FilterResponse {
position: Position;
data: Map<Id, Partial<Model>>;
}
/**
* Returns all models of one collection that satisfy the filter condition.
* Url: POST to /internal/datastore/reader/filter
*
* The global max position of the datastore is returned next the the filtered data.
* This method does not take a position and can not be used when browsing the history.
*
* @returns see get_many
* @throws InvalidFormat
*/
filter(collection: Collection, filter: Filter, mapped_fields?: Field[]): FilterResponse
/**
* Url: POST to /internal/datastore/reader/exists
*
* See `filter`, returns true, if at least one model was found. The returned position is
* the highest position in the complete datastore.
*
* @throws InvalidFormat
*/
exists(collection: Collection, filter: Filter): {exists: boolean; position: Position;}
/**
* Url: POST to /internal/datastore/reader/count
*
* See `filter`, returns the amount of found models. The returned position is
* the highest position in the complete datastore.
*
* @throws InvalidFormat
*/
count(collection: Collection, filter: Filter): {count: number; position: Position;}
/**
* Url: POST to /internal/datastore/reader/min
*
* Executes a min aggregation on all models of one collection on
* the given field that satisfy the filter condition.
* 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.
*
* @throws InvalidFormat
*/
min(collection: Collection, filter: Filter, field: Field, type?: string): {min: Value; position: Position;}
/**
* Url: POST to /internal/datastore/reader/max
* Analogous to min.
*
* @throws InvalidFormat
*/
max(collection: Collection, filter: Filter, field: Field, type?: string): {max: Value; position: Position;}
Type Filter = And | Or | Not | FilterOperator
/**
* 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.
*/
Interface FilterOperator {
field: Field;
value: Value | null;
operator: '=' | '!=' | '<' | '>' | '>=' | '<=';
}
Interface Not {
not_filter: Filter;
}
Interface And {
and_filter: Filter[];
}
Interface Or {
or_filter: Filter[];
}

View File

@ -1,68 +0,0 @@
## How to specify an interface for a service?
There are many ways to describe, what a service should do. The main
characteristics (from an outside-view) are the provided functions. Each service
does have functions to execute with arguments and return types. Next to the
returnvalue, events can be published or errors be thrown. The way we want to
derscribe these interfaces is through a TypeScript-like interface definition.
The interface is the collection of all functions defined.
Example:
/**
* The Exception, if a model wasn't found. The missed id is given.
*/
Exception ObjectNotFound(id: Id);
/**
* Does something..
*
* @throws ObjectNotFound
do_it(data: MyRequest): void publishes MyEvent;
Interface MyRequest {
id: Id;
text: string;
some_other_value: number;
}
Event MyEvent on topic MyEventTopic {
changed_text: string;
}
There are some common types defined here:
Type Position=number;
Type Id=number;
Type Fqid=string; // `collection/id`
Type Fqfield=string; // `collection/id/field`
Type Collection=string; // `collection`
Type CollectionField=string; // `collection/field`
Type Model=object;
Type Field=string;
Type Value=any;
The language is a bit modified:
1) Exceptions
Exceptions should be declared by giving the name and parameters:
# Exception <name>(<param1>: <type1>, ...);
A comment should state, why this exception can be thrown
2) Events in the function declaration
Each function takes some arguments and have a return type. Next to the
return type, some events can be published. With the `publishes` token, one
can give a comma seperated list of events, that can be generated.
# my_function(): void publishes Event1, Event2
Events should be specified as interfaces, but named `Events`. Also there is
the `on topic` token, on which topic the message is broadcasted.
# Event MyEvent on topic MyEventTopic { ... }
3) Functiondocumentation
Each function must have a description. Also it should tell, what exceptions
could be thrown.
4) Placeholders
If you need generic names, use placeholders: `<my_placeholder>`
E.g. if the event topic is specific for one id, the topic used may be
definied as this example:
# Event MyEvent on topic MyEventForUser<user_id> { ... }

View File

@ -1,35 +0,0 @@
/**
* Saves a mediafile (encoded as base64) into this service.
*
* Technical:
* POST to /internal/media/upload_mediafile/ with JSON payload.
* Returns 200 on success, 4xx on errors with a message in the payload.
*/
upload_mediafile(file: string, id: Id, mimetype: string): {message: string} | void
/**
* Saves a resource (encoded as base64) into this service.
*
* Technical:
* POST to /internal/media/upload_resource/ with JSON payload.
* Returns 200 on success, 4xx on errors with a message in the payload.
*/
upload_resource(file: string, id: Id, mimetype: string): {message: string} | void
/**
* Retrieves a mediafile given by it's id.
*
* Technical:
* GET to /system/media/get/<mediafile_id>
* Returns 200 on success and 404, if the resource does not exist
*/
get(mediafile_id: Id): Blob
/**
* Retrieves a resource given by it's id.
*
* Technical:
* GET to /system/media/get_resource/<resource_id>
* Returns 200 on success and 404, if the resource does not exist
*/
get(resource_id: Id): Blob

View File

@ -1,53 +0,0 @@
# Permission Service Interface
/**
* Returns true, if the thing requested (identified by `name`) is allowed for each
* provided data in `dataList` by the user given by `user_id`.
*
* Convention: For each action and presenter, there will be an permission check.
* This is not enforced but a good guideline to see which permission check belongs
* to which usecase.
**/
is_allowed(name: string, user_id: Id, dataList: object[]): bool
/**
* Filters the fqfields that can ve deen by the user. Only fqfields are returned, that are accessible.
**/
restrict_fqfields(fqfields: Fqfield[], user_id: Id): Fqfield[]
/**
* Should be called for each update of the datastore with the changed data. It
* returns user ids, which should get a full update since too many/complicated
* permission changes accur.
*
* Possible reasons for additional updates:
* 1) A permission related relation to a user has changed:
* - Relation to a group
* - Relation to a meeting (via guest/temporary relation)
* - Relation to a committee (or an upgrade/downgrade as a manager)
* 2) Role of a user has changed
* 3) Permissions of a group changed -> Full update for all users in this group
* 4) Changes in specific (meeting-related) models:
* - Motion submitter:
* - Motion state: Update des Antrages
* - Motion block internal: Update des Blocks
* - Motion comment section read groups: Update aller Comments dieser Section
* - State restrictions: Update aller Anträge in dem State
* - Agendaitem visibility: Update des Agendaitems
* - Poll state: Wenn state==published volles update aller options/votes
* - Mediafile (has_)inherited_access_groups: Update der Mediafile
* - Mediafile used_as_*: Update der Mediafile
*
**/
additional_update(updated: {[fqfield: Fqfield]: Value}): Id[]
/**
* This technical interface must be implemented by the services
* users.
*/
Interface DataProvider {
get: (fqfields: Fqfield[]) => {[fqfield: Fqfield]: Value}
}

View File

@ -1,53 +0,0 @@
# Presenter Service Interface
Exception PresenterException(message: string);
/**
* Executes some presenting function on the server. The term "presenting" means
* a non writing (or modifying) idempotent request. There may be some
* side-effect allowed (like tracking calls to one presenter), but the main
* purpose is to get data of the server, which is not autoupdate-, projector-, or
* icc-data.
*
* Some purposes may be:
* - aggregating of login data
* - managing of whoami, if additional data is needed, that the auth service
* doesn't provide
* - To get history points
* - To get history data
* - To get the installed version
* - To query statistics
* - To calculate recursive trees (e.g. origins of motions; not possible with
* the autoupdate service)
* - to synchronize the servertime for countdowns
*
* @throws PresenterException This exception might be thrown, if there was an
* server error (Http-500-equivalent). For user error (e.g. wrong data) use the
* PresenterError interface.
*/
handle_request(payload: Presenter[], user_id: Id): PresenterResult[]
/**
* This interface specifies what presenter is used with the payload for the call.
*/
Interface Presenter {
presenter: string;
data: any;
}
/**
* A presenter may return anything. This is presenter-specific. But if there was
* an error (user error, not a PresenterException), the response for this
* presenter should follow `PresenterError`.
*
* TODO: Maybe we do not want to response an error but always throw an exception
* instead.
*/
type PresenterResult = any | PresenterError;
/**
* A common format for errors.
*/
Interface PresenterError {
error: object;
}

View File

@ -1,20 +0,0 @@
# Restrictions Service Interface
Exception RestrictionException(message: string);
/**
* Restricts data for given user
*
* @throws RestrictionException
*/
handle_request (payload: Restriction[]): RestrictionResult[]
interface Restriction {
user_id: number;
fqfields: Fqfield[];
}
interface RestrictionResult {
<fqfield>: Value;
}

View File

@ -1 +0,0 @@
<mxfile host="app.diagrams.net" modified="2020-12-04T15:16:43.854Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" etag="w7B7nepetiGU9l5b0a2N" version="13.10.8" type="device"><diagram id="SzigwTKClLag4ZUmXMWD" name="Page-1">7V1bd5u4Fv41eTlrOQsQ18cmbTqdOTltJ505bV6yiK3YnMHIAWzH+fUHYmSDtIPBBkm0eYoRl8DWt7f2XWfocv70MfYXs2syweGZoU2eztD7M8PQTd3N/uQjm+2IY1vbgWkcTIqL9gM3wTMuBrVidBlMcFK5MCUkTINFdXBMogiP08qYH8dkXb3sgYTV/7rwp5gbuBn7IT/632CSzopR2zL3J37DwXRW/GsDIXt7Zu7Tq4tPSWb+hKxLQ+jDGbqMCUm3v+ZPlzjMqUcJs73v6pWzuzeLcZQ2ueE/v8dr9zF5l6yd1fjHjydr8TUc5Tfkj1n54bL45OJt0w2lQUyW0QTnT9HP0MV6FqT4ZuGP87PrbNazsVk6D4vTEz+Z7a7lX7F46xWOU/xUGipe+SMmc5zGm+yS4qznFOQrAKQ7BYDWpenwimtmpZlAFEJ+AYHp7tl7ImU/Cjq1oBnFNEgiLftsEqczMiWRH/6bkEVBi//hNN0UAPeXKamSDT8F6ff89nPDKg5/FE/Lf79/Kh9s6EGUfc72Lose/qAPzA/2t70c7e+bvMt5IzuMSIS3I1dBToWX869OW0KW8RjXkaZgUT+e4rTmuoKCeFLhPh4EMQ79NFhVmbHzCaUQKzHBnJvi/F1visOCah3OennS9aMmXVN+0m2v4awXrK+d65qpV7i/wE1jWBTP/kKC7KP2l5CHhyR7VRY3u1doBCV388en29XvV9fR9d/R48XDIrr6ODIsicD5CaQFTFRdlrgAX8f0OHGhC511vTznOwQcmvWKsNhDQNlZN5suEoW4GGnnpqeZysoLdBtdfP+6/HZ/uXq+doMIPZIPI+dt5ekWSiCVrbYLj2lQPZiqnbpCSKqjRQlJl2Q+D9IU4wPq6gGNvgMl3kRVJd5wASXeAJR4uy8d3uR1+H7ZzBkynxkNNTzTVkqv523ba4zTIJpKZwhHV40hbNGKqzFkhkANGcKWprnCr2O/KRfdzzFSao5d3pkRCZ31qnXiDM08aTrtjtlOqczME9elWCnE/kgl+wRGE1IKTcbPiibXaI0mZ/cW1EYxVEeTp5Zs+mnB1NLRmoEp00DtCpgchbD0cPnwz7P/x+gpmX5/+Ht1Nf7r8X5kvkGpWyiBVG6q25R8cMiriqWRrRCUQE+jzvtO6rG0h82H/ejFeBmvdqHP7pToo9QpXWGggXPQXmS5Dg3EUqBRo1oJpIGOSFuy0Bp2vAjAUjfuXp0JM9InKAGkOlKUgPRXgmMOS7IdvaZ0v5bLc5w/5z3i2ZOCRYLFEIl1/iGTI5IDZbT0ltDCO0qHp0sZ8tY4j5dLdSGa5nLJ8jSjAhXdVF0w6bxkqo9owrpUT+qTHDUdh/cvJ3MREYz9kNUVFUSebblV5I10lSzCOmKUkPdA4rUfT/hlcUbm98tEiLQ3PKsq7S1+SXQBae/2Je0N3gkjNn3lKJZUxqDR+5P2GvW6FFBRyXCGodTWcFZRcRgAlFo6h/P8Fa0KJfUtGp0X37Psv8oU3bZWFd3I4EW3LdKYMXjl6pqkAeGZTrThp2uMpmog2ZYfnawSsd5NM9b18xtTzGul0mmGNOk04x1UHzOyLFSgVdVoRlAdiFha8bmb5+fnHKWyD0yr5PDDYBplv8cZOXCcDVC74F1xYh5MJlsrCCfBs3//8qiczotcGr98h3VxZr3PSZs9Llsdk+1C2RszU+KXiI0AWhu90Zp34/ystDYtybRGb/bBSbmSXSt1SkY3YOTw5oAY4PTltznCyhABnJbxjTwoazheRcp4fWFpfw0t4GRCAogVXFvCFHf1AEokVHgN2yJtjsHOkx4boscUjB7zDT09oKfzOgE10WOo59NwmXp6qPJAqE8D8SYnz3IcZMHcBi2T9WX+cQ8wUHbwBcdB9h25afAyVmlfcKxyeQIHuU05qGm+TGlaLWBarROVSspoZhVVJguXVxitPcduKdOfvOeN+gZorKAGUkzbWicFpDPNrboioPaIPgGMzjDBuGsHQsMobChtMGDkvR5nhv24zDvGXHy+QaNPUZL60fN+kMPqID0iNiNMAI8I1PGlN4+IJciMBZTAwRVNde39EFM0hXhFLQ3SUG6emGNUw0+6x6tqYJ6Y1RcfCK4RPYYPHEUYwTQHyQj0tUuMMMHJOA4WYJhRJDu4TARo58Q6kEjTHzsIcovvwW10Cu5jynRPYAhLFkPAyp1jVWsxd6JUkG/A5MPTmR4X5traMvsxzX98mtCh7F/sRiUvSgzZTF45MyHlzO1LYvH+g/lLTkRy9y9KvfuY0s5PkkzLnWffD51dJjhO7kg8yWyovEsEfH4R40x5HuO7VYDXwL8IQ7K+8zOW28zJMuEvgCKXQqfQZcwal59CHZpDrzdJKroby1F9CVRRLaymdrlazScsyLWivsCzDEbtoA5LWQKP9hQZGB3ZqhcLkDqQ49noS+jYfDJdLt/llwd5LKGM84aGX18OEEdanm9PDg6xqq/d1CnSvtpadxyjgpZTq62p1NNZhz7Dhgd9qP2nm9i802aO5/dQGaTAAJupOedM2rDjQgxs8QzsoNMZGCzj5/n3UPqr5CC2Iy+K7TT23DSNYu/S8j2EqtyqfIWHw/uDgiS/CUg5F8hkjlllMYvSVQCDwWXGPJ3ENnI9jukYj5K80n5abdh9KYzu0qJZ6kbXVWc6SowSmKZLnKRSWc5mohHIQ8CqRi35Sli3L0vIa7KsqcN0B1TSI9ttnMBzvZWfWR6jlZ5YfUalPpO7hLxm8XihvMsrpYsYJziSzL0Wo88D5Vl0caykfXVgfYMaKe/91s7PxdYZHLlkil8x6zpzdc+8TsZnFayoVfpfR4xy+B7PFyT2C6RKYzvXZYxBqCNOX4xnoyj6Qq6/6eHH8W/Pt5F1OwqAptj1XNdzf44umFCIKx4kpt6UCVt6djIutM0qF6rUKhakRdtOsUo2fuk4xK4kshyn2jdPqcKyOmKUoEXiYCrZB8FVbgKSva/GLnAbS75w86BOJbKT5WCKW2Dq8gzZUSNLy/CqHorRqZ2cN+ANPTZQ5fcV+AX7n54EG+qkUmSPM6ryv20H0ukkNzXGBE3yINJuhjbH0rYDqXvtkk7wJwmBxHbhDV2YtBrLdgB3cm8dXeAdBzlaDdA0VmXhMxryS/ucB6YqQiXrBaaEIDEL6j6nOiiP6RzeP2iMzhdSOOLAlhIiq7ckcZg5+GiC4F48Q2kYf5oMcloLIQcxG6aMVHKS11GjhKXP8dSPgsRXo4UgoxGYQNMxsfoAH6M6RKVfIoGjbhnvviuua9HOoINZ8HlPuOyuH4Z7mLdsIF+qN9YCtmXoj7WGbXjaDRex1pylIa/qwFZpc4E6UijEWLp7OLwrlLF4hVGFfE1jX8NQa+/31Xmo1hun0t4wTOsL2+blNBRT6qLYqNabVU5nwvE8SJK8wlIqqVh90XZdjla63lOZTK0Pf2CVWUyXGxvoQg1VG3VRmAXbKbzP8vNNhkNtTKKHYCoXdSbT6cqAenZrQsnFizFVWja81vpTHq3EhrWO6uQjIQMFplXTrRs798WdNsVAHpLsVd18xYsooJ4TphEfr1euobeUenuYWkBB2BAYYZjaiMFkqiNaSiCg3h725mjS1owBRv2AmpIh8ApvNod46od3EUmD7IO5Hi6LOFj5483dgoTBeMOfD8k0iO5eui9y59IZlrUeUZ6CtmWFFDGnL0XMFNuVnckvGloci26scDiQpVYWGe0mp3SwcmhTrFZ+EbDnx5+4+Fp27kVHFNmoh25KzzAypOkSg7M/aUn/YY5oGvkQxBG8P+YhCPFLk2SpijXbTlbTBfYVqpVlAzNRkMlSEurQJNZI4Y29lPyD5br/2K2PHRsK+QDRsd78f+YQKoyrtpyYfe1hUAEtWwchgfnEnXSzkCx9mb1kIV0E2hOhP1bgq8XE7t18ZGqhMglPQBfO05hjn/KEmMpCGtVXNzMD2DdFdmqGwfYLBzoVgz2ieksnlL2x5rBdi2bjvZHa8htyzWour/KpvCafP5c3vMheF7C6Je6YbliSc3x4scSz3FueYatk+ZZdD0f5rlV2tWn8qU1KO2UwdBtdfP+6/HZ/uXq+doMIPZIPQArvW90FCCWQek2h1FRSd242gG/dtvzvl62PPWnOpZmKdW9dmvNkucCxP5nL7i7CNmAWmFgMUoq3qed+5E8ld1u2NDZboMdey9lhTPJ99vYrTfZNs2sywfkV/wc=</diagram></mxfile>

@ -1 +1 @@
Subproject commit fa36ec3b33af85367ffdf769c43c296c1afca9cc
Subproject commit 3897450e038fb4ef6ed6e72f32d4ca613b31b347

@ -1 +1 @@
Subproject commit 2175dc2ccc277c585891551acb22b7fcbccc0d40
Subproject commit fdbb91580a8cd31ac23230b2f2a9fd5354599876

@ -1 +1 @@
Subproject commit bc083f8c07e8952759f38831f636890a72a08f34
Subproject commit 0bc9c3ef3fad0488ad4d914030ab50a2c3915f28

@ -1 +1 @@
Subproject commit daa548c253e761f65a426ca02d6c08a62a5bd221
Subproject commit 3911fee5c58eda349dc574d93f2f782e6c95ce91