From 85f16e3c3b0553237e7a4630d3b24cccf9057245 Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Thu, 12 Sep 2024 16:35:57 +0200 Subject: [PATCH] [mod] --- doc/konzept.md | 34 ++-- source/helpers.ts | 139 +++++++------- source/repositories/calendar.ts | 245 ++++++++++++++----------- source/repositories/resource.ts | 314 ++++++++++++++++++++++++++++++++ source/services/calendar.ts | 247 ++++++++++++------------- source/types.ts | 91 +++++---- tools/makefile | 1 + 7 files changed, 711 insertions(+), 360 deletions(-) create mode 100644 source/repositories/resource.ts diff --git a/doc/konzept.md b/doc/konzept.md index 34ba108..aab1815 100644 --- a/doc/konzept.md +++ b/doc/konzept.md @@ -1,21 +1,23 @@ - Kalender sollen unabhängig von Nutzern bestehen können - einem Kalender können beliebig viele Nutzer zugeordnet werden, die jeweils bestimmte Berechtigungen haben (z.B. als Rollen "admin", "editor", "viewer", …) -- Events bilden keine Domäne +- Veranstaltungen bilden keine Domäne +- es gibt verschiedene Arten von Quellen: + - lokal + - enthält Veranstaltungen + - caldav + - enthält keine eigenen Veranstaltungen + - sollte read-only- und read/write-Modus haben - Berechtigungen: - - Kalender anlegen - - Kalender-Stammdaten ändern - - Kalender-Einträge lesen - - Kalender-Einträge erstellen - - Kalender-Einträge ändern - - Kalender-Einträge entfernen + - Kalender anlegen + - Stammdaten ändern + - Einträge lesen + - Einträge erstellen + - Einträge ändern + - Einträge entfernen +- Rollen (innerhalb eines Kalendars): + - `admin`: kann alles + - `editor`: kann bei lokalen - Kalender sind für gewöhnlichen öffentlich -- es gibt verschiedene Arten von Kalendern: - - konkret - - enthält Veranstaltungen - - extern - - über CalDAV - - sollte read-only- und read/write-Modus haben - nach dem Anmelden sieht man eine Kalender-Ansicht mit folgenden Kalendern kombiniert angezeigt: - - öffentliche Kalender - - nicht öffentliche Kalendar, bei welchen man Lese-Berechtigung hat -- Entwurfsname: "zeitbild" + - öffentliche Kalender + - nicht öffentliche Kalendar, bei welchen man Lese-Berechtigung hat diff --git a/source/helpers.ts b/source/helpers.ts index 9a13b93..f6965d6 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -4,69 +4,6 @@ namespace _zeitbild.helpers { - /** - */ - var _template_cache : Record = {}; - - - /** - * @todo caching - */ - export async function template_coin( - name : string, - data : Record - ) : Promise - { - let content : string; - if (! (name in _template_cache)) { - content = ( - ( - await lib_plankton.file.read( - lib_plankton.string.coin( - "templates/{{name}}.html.tpl", - { - "name": name, - } - ) - ) - ) - .toString() - ); - _template_cache[name] = content; - } - else { - content = _template_cache[name]; - } - return Promise.resolve( - lib_plankton.string.coin( - content, - data - ) - ); - } - - - /** - * @todo outsource - */ - export async function promise_row( - members : Array< - () => Promise - > - ) : Promise< - Array< - type_result - > - > - { - let results : Array = []; - for await (const member of members) { - results.push(await member()); - } - return Promise.resolve>(results); - } - - /** */ export function date_object_get_week_of_year( @@ -96,8 +33,8 @@ namespace _zeitbild.helpers ) ); } - - + + /** * @todo unite with type_datetimeobject? */ @@ -110,8 +47,8 @@ namespace _zeitbild.helpers type_time ); }; - - + + /** * @todo timezone_shift */ @@ -151,9 +88,8 @@ namespace _zeitbild.helpers }, }; } - - - + + /** * @todo negative shift? */ @@ -587,4 +523,67 @@ namespace _zeitbild.helpers }; } + + /** + */ + var _template_cache : Record = {}; + + + /** + * @todo caching + */ + export async function template_coin( + name : string, + data : Record + ) : Promise + { + let content : string; + if (! (name in _template_cache)) { + content = ( + ( + await lib_plankton.file.read( + lib_plankton.string.coin( + "templates/{{name}}.html.tpl", + { + "name": name, + } + ) + ) + ) + .toString() + ); + _template_cache[name] = content; + } + else { + content = _template_cache[name]; + } + return Promise.resolve( + lib_plankton.string.coin( + content, + data + ) + ); + } + + + /** + * @todo outsource + */ + export async function promise_row( + members : Array< + () => Promise + > + ) : Promise< + Array< + type_result + > + > + { + let results : Array = []; + for await (const member of members) { + results.push(await member()); + } + return Promise.resolve>(results); + } + } diff --git a/source/repositories/calendar.ts b/source/repositories/calendar.ts index ae2dd2d..9f93e8a 100644 --- a/source/repositories/calendar.ts +++ b/source/repositories/calendar.ts @@ -2,12 +2,28 @@ namespace _zeitbild.repository.calendar { + /** + */ + type type_dispersal = { + core_row : Record< + string, + any + >; + member_rows : Array< + Record< + string, + any + > + >; + }; + + /** */ var _core_store : ( null | - lib_plankton.storage.type_core_store< + lib_plankton.storage.type_store< _zeitbild.type.calendar_id, Record, {}, @@ -19,14 +35,14 @@ namespace _zeitbild.repository.calendar /** */ - var _event_store : ( + var _member_chest : ( null | - lib_plankton.storage.type_core_store< - _zeitbild.type.event_id, + lib_plankton.storage.type_chest< + Array, Record, - {}, - lib_plankton.storage.type_sql_table_autokey_search_term, + lib_plankton.database.type_description_create_table, + lib_plankton.storage.sql_table_common.type_sql_table_common_search_term, Record > ) = null; @@ -35,7 +51,7 @@ namespace _zeitbild.repository.calendar /** */ function get_core_store( - ) : lib_plankton.storage.type_core_store< + ) : lib_plankton.storage.type_store< _zeitbild.type.calendar_id, Record, {}, @@ -44,7 +60,7 @@ namespace _zeitbild.repository.calendar > { if (_core_store === null) { - _core_store = lib_plankton.storage.sql_table_autokey_core_store( + _core_store = lib_plankton.storage.sql_table_autokey_store( { "database_implementation": _zeitbild.database.get_implementation(), "table_name": "calendars", @@ -61,102 +77,78 @@ namespace _zeitbild.repository.calendar /** */ - function get_event_store( - ) : lib_plankton.storage.type_core_store< - _zeitbild.type.event_id, + function get_member_chest( + ) : lib_plankton.storage.type_chest< + Array, Record, - {}, - lib_plankton.storage.type_sql_table_autokey_search_term, + lib_plankton.database.type_description_create_table, + lib_plankton.storage.sql_table_common.type_sql_table_common_search_term, Record > { - if (_event_store === null) { - _event_store = lib_plankton.storage.sql_table_autokey_core_store( + if (_member_chest === null) { + _member_chest = lib_plankton.storage.sql_table_common.chest( { "database_implementation": _zeitbild.database.get_implementation(), - "table_name": "events", - "key_name": "id", + "table_name": "calendar_members", + "key_names": ["calendar_id","user_id"], } ); } else { // do nothing } - return _event_store; + return _member_chest; } /** - * @todo use events table */ function encode( object : _zeitbild.type.calendar_object - ) : Record - { - switch (object.kind) { - /* - case "concrete": { - const data_raw : any = lib_plankton.json.encode(object.data); - data_raw["users"] - return { - "name": object.name, - "private": object.private, - "kind": object.kind, - "data": { - "users": data_raw["users"], - "events": [] // TODO - }, - }; - } - */ - default: { - return { - "name": object.name, - "private": object.private, - "kind": object.kind, - "data": lib_plankton.json.encode(object.data), - }; - } - } - } - - - /** - */ - function decode( - row : Record - ) : _zeitbild.type.calendar_object + ) : type_dispersal { return { - "name": row["name"], - "private": row["private"], - "kind": row["kind"], - "data": lib_plankton.json.decode(row["data"]), + "core_row": { + "name": object.name, + "public": object.public, + "resource_id": object.resource_id, + }, + "member_rows": ( + object.members + .map( + (member) => ({ + // "calendar_id": calendar_id, + "user_id": member.user_id, + "role": member.role, + }) + ) + ), }; } /** */ - export async function dump( - ) : Promise< - Array< - { - id : _zeitbild.type.calendar_id; - object : _zeitbild.type.calendar_object; - } - > - > + function decode( + dispersal : type_dispersal + ) : _zeitbild.type.calendar_object { - return ( - (await get_core_store().search(null)) - .map( - ({"key": key, "preview": preview}) => ({ - "id": key, - "object": (preview as _zeitbild.type.calendar_object), - }) - ) - ); + return { + "name": dispersal.core_row["name"], + "public": dispersal.core_row["public"], + "members": ( + dispersal.member_rows + .map( + (member_row) => ({ + "calendar_id": member_row["calendar_id"], + "user_id": member_row["user_id"], + "role": member_row["role"], + }) + ) + ), + "resource_id": dispersal.core_row["resource_id"], + }; } @@ -177,7 +169,15 @@ namespace _zeitbild.repository.calendar > { return ( - (await get_core_store().search(null)) + ( + await get_core_store().search( + { + "expression": "(public = TRUE)", + "arguments": { + } + } + ) + ) .filter( ({"key": key, "preview": preview}) => ( ( @@ -205,49 +205,74 @@ namespace _zeitbild.repository.calendar /** */ - export async function read( + export function read( id : _zeitbild.type.calendar_id ) : Promise<_zeitbild.type.calendar_object> { - const row : Record = await get_core_store().read(id); - - return decode(row); + return ( + get_core_store().read(id) + .then( + (core_row) => ( + get_member_chest().search( + { + "expression": "(calendar_id = $calendar_id)", + "arguments": { + "calendar_id": id, + } + } + ) + .then( + (member_rows) => Promise.resolve( + { + "core_row": core_row, + "member_rows": member_rows, + } + ) + ) + .then( + (dispersal) => Promise.resolve<_zeitbild.type.calendar_object>( + decode(dispersal) + ) + ) + ) + ) + ); } /** */ - export async function create( - value : _zeitbild.type.calendar_object + export function create( + calendar_object : _zeitbild.type.calendar_object ) : Promise<_zeitbild.type.calendar_id> { - const row : Record = encode(value); - const id : _zeitbild.type.calendar_id = await get_core_store().create(row); - - return id; - } - - - /** - */ - export async function update( - id : _zeitbild.type.calendar_id, - value : _zeitbild.type.calendar_object - ) : Promise - { - const row : Record = encode(value); - - await get_core_store().update(id, row); - } - - - /** - */ - export async function delete_( - id : _zeitbild.type.calendar_id - ) : Promise - { - await get_core_store().delete(id); + return ( + Promise.resolve<_zeitbild.type.calendar_object>(calendar_object) + .then( + (calendar_object) => Promise.resolve(encode(calendar_object)) + ) + .then<_zeitbild.type.calendar_id>( + (dispersal) => ( + get_core_store().create(dispersal.core_row) + .then<_zeitbild.type.calendar_id>( + (calendar_id) => ( + Promise.all( + dispersal.member_rows + .map( + (member_row) => get_member_chest().write( + [calendar_id, member_row["user_id"]], + {"role": member_row["role"]} + ) + ) + ) + .then( + () => Promise.resolve<_zeitbild.type.calendar_id>(calendar_id) + ) + ) + ) + ) + ) + ); } } diff --git a/source/repositories/resource.ts b/source/repositories/resource.ts new file mode 100644 index 0000000..d36ec22 --- /dev/null +++ b/source/repositories/resource.ts @@ -0,0 +1,314 @@ + +namespace _zeitbild.repository.resource +{ + + /** + */ + var _local_resource_core_store : ( + null + | + lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + ) = null; + + + /** + */ + var _local_resource_event_store : ( + null + | + lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + ) = null; + + + /** + */ + var _caldav_resource_store : ( + null + | + lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + ) = null; + + + /** + */ + var _resource_core_store : ( + null + | + lib_plankton.storage.type_store< + _zeitbild.type.resource_id, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + ) = null; + + + /** + */ + function get_local_resource_core_store( + ) : lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + { + if (_local_resource_core_store === null) { + _local_resource_core_store = lib_plankton.storage.sql_table_autokey_store( + { + "database_implementation": _zeitbild.database.get_implementation(), + "table_name": "local_resources", + "key_name": "id", + } + ); + } + else { + // do nothing + } + return _local_resource_core_store; + } + + + /** + */ + function get_local_resource_event_store( + ) : lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + { + if (_local_resource_event_store === null) { + _local_resource_event_store = lib_plankton.storage.sql_table_autokey_store( + { + "database_implementation": _zeitbild.database.get_implementation(), + "table_name": "local_resource_events", + "key_name": "id", + } + ); + } + else { + // do nothing + } + return _local_resource_event_store; + } + + + /** + */ + function get_caldav_resource_store( + ) : lib_plankton.storage.type_store< + int, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + { + if (_caldav_resource_store === null) { + _caldav_resource_store = lib_plankton.storage.sql_table_autokey_store( + { + "database_implementation": _zeitbild.database.get_implementation(), + "table_name": "caldav_resources", + "key_name": "id", + } + ); + } + else { + // do nothing + } + return _caldav_resource_store; + } + + + /** + */ + function get_resource_core_store( + ) : lib_plankton.storage.type_store< + _zeitbild.type.resource_id, + Record, + {}, + lib_plankton.storage.type_sql_table_autokey_search_term, + Record + > + { + if (_resource_core_store === null) { + _resource_core_store = lib_plankton.storage.sql_table_autokey_store( + { + "database_implementation": _zeitbild.database.get_implementation(), + "table_name": "resources", + "key_name": "id", + } + ); + } + else { + // do nothing + } + return _resource_core_store; + } + + + /** + */ + /* + function encode_resource_core( + stuff : { + object : _zeitbild.type.resource_object; + sub_id : int; + } + ) : Record + { + return { + "kind": stuff.object.kind, + "sub_id": stuff.sub_id + }; + } + */ + + + /** + * @todo data + */ + /* + function decode_resource_core( + row : Record + ) : { + object : _zeitbild.type.resource_object; + sub_id : int; + } + { + return { + "object": { + "kind": row["kind"], + "data": null, + }, + "sub_id": row["sub_id"], + }; + } + */ + + + /** + */ + function decode_event( + row : Record + ) : _zeitbild.type.event_object + { + const decode_datetime : ((datetime_raw : string) => _zeitbild.helpers.type_datetime) = ((datetime_raw) => { + const parts : Array = datetime_raw.split("|"); + const timezone_shift : int = parseInt(parts[0]); + if (parts[1].length <= 10) { + return { + "timezone_shift": timezone_shift, + "date": { + "year": parseInt(parts[1].slice(0, 4)), + "month": parseInt(parts[1].slice(5, 7)), + "day": parseInt(parts[1].slice(8, 10)), + }, + "time": null + }; + } + else { + return { + "timezone_shift": timezone_shift, + "date": { + "year": parseInt(parts[1].slice(0, 4)), + "month": parseInt(parts[1].slice(5, 7)), + "day": parseInt(parts[1].slice(8, 10)), + }, + "time": { + "hour": parseInt(parts[1].slice(11, 13)), + "minute": parseInt(parts[1].slice(14, 16)), + "second": parseInt(parts[1].slice(17, 19)), + } + }; + } + }); + return { + "name": row["name"], + "begin": decode_datetime(row["begin"]), + "end": ( + (row["end"] === null) + ? + null + : + decode_datetime(row["end"]) + ), + "location": row["location"], + "description": row["description"], + }; + } + + + /** + */ + export async function read( + resource_id : _zeitbild.type.resource_id + ) : Promise<_zeitbild.type.resource_object> + { + const dataset_core : Record = await get_resource_core_store().read(resource_id); + switch (dataset_core.kind) { + case "local": { + const dataset_extra_local_core : Record = await get_local_resource_core_store().read(dataset_core.sub_id); + const datasets_extra_local_events : Array> = await get_local_resource_event_store().search( + { + "expression": "(local_resource_id = $local_resource_id)", + "arguments": { + "local_resource_id": dataset_core.sub_id, + } + } + ); + return Promise.resolve<_zeitbild.type.resource_object>( + { + "kind": "local", + "data": { + "events": datasets_extra_local_events.map(x => decode_event(x)), + } + } + ); + } + case "caldav": { + const dataset_extra_caldav : Record = await get_caldav_resource_store().read(dataset_core.sub_id); + return Promise.resolve<_zeitbild.type.resource_object>( + { + "kind": "caldav", + "data": { + "url": dataset_extra_caldav["url"], + "read_only": dataset_extra_caldav["read_only"], + } + } + ); + break; + } + default: { + return Promise.reject<_zeitbild.type.resource_object>( + new Error("invalid resource kind: " + dataset_core.kind) + ); + break; + } + } + } + +} diff --git a/source/services/calendar.ts b/source/services/calendar.ts index c843304..6fbab73 100644 --- a/source/services/calendar.ts +++ b/source/services/calendar.ts @@ -42,9 +42,8 @@ namespace _zeitbild.service.calendar /** - * @todo prevent loops */ - export async function gather_events( + async function gather_events( calendar_ids : Array<_zeitbild.type.calendar_id>, from_pit : _zeitbild.helpers.type_pit, to_pit : _zeitbild.helpers.type_pit @@ -52,151 +51,137 @@ namespace _zeitbild.service.calendar Array< { calendar_id : _zeitbild.type.calendar_id; - calendar_name : string; event : _zeitbild.type.event_object; } > > { - lib_plankton.log.info( - "calendar_gather_events", - { - "calendar_ids": calendar_ids, - } - ); let result : Array< { calendar_id : _zeitbild.type.calendar_id; - calendar_name : string; event : _zeitbild.type.event_object; } > = []; for await (const calendar_id of calendar_ids) { - const calendar_object : _zeitbild.type.calendar_object = await _zeitbild.repository.calendar.read( - calendar_id - ); - if (calendar_object.private) { - lib_plankton.log.info( - "calendar_gather_events_private_calendar_blocked", - { - "calendar_id": calendar_id, - } - ); - } - else { - switch (calendar_object.kind) { - case "concrete": { - result = ( - result - .concat( - calendar_object.data.events - .filter( - (event : _zeitbild.type.event_object) => _zeitbild.helpers.pit_is_between( - _zeitbild.helpers.pit_from_datetime(event.begin), - from_pit, - to_pit - ) - ) - .map( - (event : _zeitbild.type.event_object) => ({ - "calendar_id": calendar_id, - "calendar_name": calendar_object.name, - "event": event - }) + const calendar_object : _zeitbild.type.calendar_object = await _zeitbild.repository.calendar.read(calendar_id); + const resource_object : _zeitbild.type.resource_object = await _zeitbild.repository.resource.read(calendar_object.resource_id); + switch (resource_object.kind) { + case "local": { + result = ( + result + .concat( + resource_object.data.events + .filter( + (event : _zeitbild.type.event_object) => _zeitbild.helpers.pit_is_between( + _zeitbild.helpers.pit_from_datetime(event.begin), + from_pit, + to_pit ) ) - ); - break; - } - case "caldav": { - const url : lib_plankton.url.type_url = lib_plankton.url.decode( - calendar_object.data.source_url - ); - const http_request : lib_plankton.http.type_request = { - "version": "HTTP/2", - "scheme": ((url.scheme === "https") ? "https" : "http"), - "host": url.host, - "path": (url.path ?? "/"), - "query": url.query, - "method": lib_plankton.http.enum_method.get, - "headers": {}, - "body": null, - }; - // TODO: cache? - const http_response : lib_plankton.http.type_response = await lib_plankton.http.call( - http_request, - { - } - ); - const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode( - http_response.body.toString(), - { - } - ); - result = ( - result - .concat( - vcalendar.vevents - .map( - (vevent : lib_plankton.ical.type_vevent) => ( - (vevent.dtstart !== undefined) - ? - { - "name": ( - (vevent.summary !== undefined) - ? - vevent.summary - : - "???" - ), - "begin": _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtstart), - "end": ( - (vevent.dtend !== undefined) - ? - _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtend) - : - null - ), - "location": ( - (vevent.location !== undefined) - ? - vevent.location - : - null - ), - "description": ( - (vevent.description !== undefined) - ? - vevent.description - : - null - ), - } - : - null - ) - ) - .filter( - (event) => (event !== null) - ) - .filter( - (event) => _zeitbild.helpers.pit_is_between( - _zeitbild.helpers.pit_from_datetime(event.begin), - from_pit, - to_pit - ) - ) - .map( - (event) => ({ - "calendar_id": calendar_id, - "calendar_name": calendar_object.name, - "event": event, - }) + .map( + (event : _zeitbild.type.event_object) => ({ + "calendar_id": calendar_id, + "event": event, + }) + ) + ) + ); + break; + } + case "caldav": { + // TODO readonly + const url : lib_plankton.url.type_url = lib_plankton.url.decode( + calendar_object.data.url + ); + const http_request : lib_plankton.http.type_request = { + "version": "HTTP/2", + "scheme": ((url.scheme === "https") ? "https" : "http"), + "host": url.host, + "path": (url.path ?? "/"), + "query": url.query, + "method": lib_plankton.http.enum_method.get, + "headers": {}, + "body": null, + }; + // TODO: cache? + const http_response : lib_plankton.http.type_response = await lib_plankton.http.call( + http_request, + { + } + ); + const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode( + http_response.body.toString(), + { + } + ); + result = ( + result + .concat( + vcalendar.vevents + .map( + (vevent : lib_plankton.ical.type_vevent) => ( + (vevent.dtstart !== undefined) + ? + { + "name": ( + (vevent.summary !== undefined) + ? + vevent.summary + : + "???" + ), + "begin": _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtstart), + "end": ( + (vevent.dtend !== undefined) + ? + _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtend) + : + null + ), + "location": ( + (vevent.location !== undefined) + ? + vevent.location + : + null + ), + "description": ( + (vevent.description !== undefined) + ? + vevent.description + : + null + ), + } + : + null ) ) - ); - break; - } + .filter( + (event) => (event !== null) + ) + .filter( + (event) => _zeitbild.helpers.pit_is_between( + _zeitbild.helpers.pit_from_datetime(event.begin), + from_pit, + to_pit + ) + ) + .map( + (event) => ({ + "calendar_id": calendar_id, + "event": event, + }) + ) + ) + ); + break; + } + default: { + return Promise.reject( + new Error("invalid resource kind: " + resource_object["kind"]) + ); + break; } } } diff --git a/source/types.ts b/source/types.ts index 83fec00..320b81f 100644 --- a/source/types.ts +++ b/source/types.ts @@ -6,7 +6,9 @@ namespace _zeitbild.type /** */ - type role = ( + export type role = ( + "admin" + | "editor" | "viewer" @@ -15,21 +17,16 @@ namespace _zeitbild.type /** */ - type user_id = int; + export type user_id = int; /** */ - type user_object = { + export type user_object = { name : string; }; - /** - */ - export type event_id = int; - - /** */ export type event_object = { @@ -53,6 +50,33 @@ namespace _zeitbild.type }; + /** + */ + export type resource_id = int; + + + /** + */ + export type resource_object = ( + { + kind : "local"; + data : { + events : Array< + event_object + >; + }; + } + | + { + kind : "caldav"; + data : { + read_only : boolean; + url : string; + }; + } + ); + + /** */ export type calendar_id = int; @@ -60,34 +84,35 @@ namespace _zeitbild.type /** */ - export type calendar_object = ( - { - name : string; - private : boolean; - } - & - ( + export type calendar_object = { + name : string; + public : boolean; + members : Array< { - kind : "concrete"; - data : { - users : Array< - { - id : user_id; - role : role; - } - >; - events : Array; - }; + user_id : user_id; + role : role; } - | + >; + // resource : resource_object; + resource_id : resource_id; + }; + + + /** + */ + export type root = { + users : Array< { - kind : "caldav"; - data : { - read_only : boolean; - source_url : string; - } + id : user_id; + object : user_object; } - ) - ); + >; + calendars : Array< + { + id : calendar_id; + object : calendar_object; + } + >; + }; } diff --git a/tools/makefile b/tools/makefile index 8990357..b4fbd34 100644 --- a/tools/makefile +++ b/tools/makefile @@ -26,6 +26,7 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/conf.ts \ ${dir_source}/database.ts \ ${dir_source}/types.ts \ + ${dir_source}/repositories/resource.ts \ ${dir_source}/repositories/calendar.ts \ ${dir_source}/services/calendar.ts \ ${dir_source}/api/base.ts \