From c3738ffbc07f8009f331b107044d9da422ddfc25 Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Mon, 30 Sep 2024 12:01:40 +0200 Subject: [PATCH] [add] api-action:calendar_event_get [add] api-action:calendar_event_change --- source/api/actions/calendar_event_change.ts | 99 +++++++++++++++++ source/api/actions/calendar_event_get.ts | 88 ++++++++++++++++ source/api/actions/events.ts | 23 ++-- source/api/functions.ts | 2 + source/repositories/resource.ts | 26 +++++ source/services/calendar.ts | 111 +++++++++++++++++--- source/services/resource.ts | 71 +++++++++++++ tools/makefile | 2 + 8 files changed, 400 insertions(+), 22 deletions(-) create mode 100644 source/api/actions/calendar_event_change.ts create mode 100644 source/api/actions/calendar_event_get.ts diff --git a/source/api/actions/calendar_event_change.ts b/source/api/actions/calendar_event_change.ts new file mode 100644 index 0000000..4529021 --- /dev/null +++ b/source/api/actions/calendar_event_change.ts @@ -0,0 +1,99 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_calendar_event_change( + rest_subject : lib_plankton.rest.type_rest + ) : void + { + register< + _zeitbild.type_event_object, // TODO aufdröseln + ( + null + | + string + ) + >( + rest_subject, + lib_plankton.http.enum_method.put, + "/calendar/:calendar_id/event/:event_id", + { + "description": "ändert einen Termin", + "input_schema": () => ({ + "type": "object", + "properties": { + "name": { + "nullable": false, + "type": "string" + }, + "begin": _zeitbild.api.datetime_type( + { + "nullable": false, + } + ), + "end": _zeitbild.api.datetime_type( + { + "nullable": true, + } + ), + "location": { + "type": "string", + "nullable": true, + }, + "description": { + "type": "string", + "nullable": true, + }, + }, + "required": [ + "name", + "begin", + "end", + "location", + "description", + ], + "additionalProperties": false + }), + "output_schema": () => ({ + "nullable": true, + "type": "null" + }), + "restriction": restriction_logged_in, + "execution": async (stuff) => { + const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff); + const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name); + + if (stuff.input === null) { + return Promise.reject(new Error("impossible")); + } + else { + return ( + _zeitbild.service.calendar.event_change( + parseInt(stuff.path_parameters["calendar_id"]), + parseInt(stuff.path_parameters["event_id"]), + stuff.input, + user_id + ) + .then( + () => Promise.resolve({ + "status_code": 200, + "data": null, + }) + ) + // TODO distinguish + .catch( + (reason) => Promise.resolve({ + "status_code": 403, + "data": String(reason), + }) + ) + ); + } + } + } + ); + } + +} diff --git a/source/api/actions/calendar_event_get.ts b/source/api/actions/calendar_event_get.ts new file mode 100644 index 0000000..0cfbe94 --- /dev/null +++ b/source/api/actions/calendar_event_get.ts @@ -0,0 +1,88 @@ + +namespace _zeitbild.api +{ + + /** + */ + export function register_calendar_event_get( + rest_subject : lib_plankton.rest.type_rest + ) : void + { + register< + null, + ( + _zeitbild.type_event_object + | + string + ) + >( + rest_subject, + lib_plankton.http.enum_method.get, + "/calendar/:calendar_id/event/:event_id", + { + "description": "gibt die Daten eines Termins aus", + "output_schema": () => ({ + "type": "object", + "properties": { + "name": { + "nullable": false, + "type": "string" + }, + "begin": _zeitbild.api.datetime_type( + { + "nullable": false, + } + ), + "end": _zeitbild.api.datetime_type( + { + "nullable": true, + } + ), + "location": { + "type": "string", + "nullable": true, + }, + "description": { + "type": "string", + "nullable": true, + }, + }, + "required": [ + "name", + "begin", + "end", + "location", + "description", + ], + "additionalProperties": false + }), + "restriction": restriction_logged_in, + "execution": async (stuff) => { + const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff); + const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name); + + return ( + _zeitbild.service.calendar.event_get( + parseInt(stuff.path_parameters["calendar_id"]), + parseInt(stuff.path_parameters["event_id"]), + user_id + ) + .then( + (event_object) => Promise.resolve({ + "status_code": 200, + "data": event_object, + }) + ) + .catch( + (reason) => Promise.resolve({ + "status_code": 403, + "data": String(reason), + }) + ) + ); + } + } + ); + } + +} diff --git a/source/api/actions/events.ts b/source/api/actions/events.ts index e85f2c8..9e32c7b 100644 --- a/source/api/actions/events.ts +++ b/source/api/actions/events.ts @@ -23,7 +23,8 @@ namespace _zeitbild.api { calendar_id : int; calendar_name : string; - event : _zeitbild.type_event_object; + event_id : (null | int); + event_object : _zeitbild.type_event_object; } > | @@ -55,15 +56,24 @@ namespace _zeitbild.api "output_schema": () => ({ "type": "array", "items": { - "type": "object", "nullable": false, + "type": "object", "additionalProperties": false, "properties": { "calendar_id": { - "type": "number", "nullable": false, + "type": "number", }, - "event": { + "calendar_name": { + "nullable": false, + "type": "string", + }, + "event_id": { + "nullable": true, + "type": "number", + }, + "event_object": { + "nullable": false, "type": "object", "additionalProperties": false, "properties": { @@ -100,9 +110,10 @@ namespace _zeitbild.api } }, "required": [ - "calendar_ids", + "calendar_id", + "event_id", "calendar_name", - "event", + "event_object", ], } }), diff --git a/source/api/functions.ts b/source/api/functions.ts index ffb4220..93e16a7 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -37,7 +37,9 @@ namespace _zeitbild.api _zeitbild.api.register_calendar_add(rest_subject); // event { + _zeitbild.api.register_calendar_event_get(rest_subject); _zeitbild.api.register_calendar_event_add(rest_subject); + _zeitbild.api.register_calendar_event_change(rest_subject); _zeitbild.api.register_calendar_event_remove(rest_subject); } } diff --git a/source/repositories/resource.ts b/source/repositories/resource.ts index 8b4a45b..ad68e7f 100644 --- a/source/repositories/resource.ts +++ b/source/repositories/resource.ts @@ -558,6 +558,32 @@ namespace _zeitbild.repository.resource } + /** + */ + export async function local_resource_event_update( + resource_id : _zeitbild.type_resource_id, + event_id : _zeitbild.type_local_resource_event_id, + event_object : _zeitbild.type_event_object + ) : Promise + { + const dataset_core : Record = await get_resource_core_store().read(resource_id); + if (! (dataset_core.kind === "local")) { + throw (new Error("not a local resource")); + } + else { + return get_local_resource_event_store().update( + event_id, + encode_local_resource_event( + { + "local_resource_id": dataset_core["sub_id"], + "event": event_object, + } + ) + ); + } + } + + /** */ export async function local_resource_event_delete( diff --git a/source/services/calendar.ts b/source/services/calendar.ts index a8c49c2..f1c25eb 100644 --- a/source/services/calendar.ts +++ b/source/services/calendar.ts @@ -108,6 +108,32 @@ namespace _zeitbild.service.calendar } + /** + */ + export async function event_get( + calendar_id : _zeitbild.type_calendar_id, + local_resource_event_id : _zeitbild.type_local_resource_event_id, + user_id : _zeitbild.type_user_id + ) : Promise<_zeitbild.type_event_object> + { + const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read( + calendar_id + ); + return wrap_check_access_level<_zeitbild.type_event_object>( + calendar_object, + user_id, + _zeitbild.enum_access_level.view, + async () => { + const event_object : _zeitbild.type_event_object = await _zeitbild.service.resource.event_get( + calendar_object.resource_id, + local_resource_event_id + ); + return Promise.resolve<_zeitbild.type_event_object>(event_object); + } + ); + } + + /** */ export async function event_add( @@ -135,7 +161,34 @@ namespace _zeitbild.service.calendar /** - * @todo check access level + */ + export async function event_change( + calendar_id : _zeitbild.type_calendar_id, + local_resource_event_id : _zeitbild.type_local_resource_event_id, + event_object : _zeitbild.type_event_object, + user_id : _zeitbild.type_user_id + ) : Promise + { + const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read( + calendar_id + ); + return wrap_check_access_level( + calendar_object, + user_id, + _zeitbild.enum_access_level.edit, + async () => { + await _zeitbild.service.resource.event_change( + calendar_object.resource_id, + local_resource_event_id, + event_object + ); + return Promise.resolve(undefined); + } + ); + } + + + /** */ export async function event_remove( calendar_id : _zeitbild.type_calendar_id, @@ -170,12 +223,15 @@ namespace _zeitbild.service.calendar user_id : _zeitbild.type_user_id ) : Promise< Array< - _zeitbild.type_event_object + { + id : (null | _zeitbild.type_local_resource_event_id); + object : _zeitbild.type_event_object; + } > > { const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id); - return wrap_check_access_level>( + return wrap_check_access_level>( calendar_object, user_id, _zeitbild.enum_access_level.view, @@ -187,18 +243,28 @@ namespace _zeitbild.service.calendar Promise.all( resource_object.data.event_ids .map( - (event_id) => _zeitbild.repository.resource.local_resource_event_read( - calendar_object.resource_id, - event_id + (event_id) => ( + _zeitbild.repository.resource.local_resource_event_read( + calendar_object.resource_id, + event_id + ) + .then( + (event_object) => Promise.resolve( + { + "id": event_id, + "object": event_object, + } + ) + ) ) ) ) .then( - (events) => Promise.resolve( - events + (event_entries) => Promise.resolve( + event_entries .filter( - (event : _zeitbild.type_event_object) => lib_plankton.pit.is_between( - lib_plankton.pit.from_datetime(event.begin), + (event_entry : {id : (null | _zeitbild.type_local_resource_event_id); object : _zeitbild.type_event_object;}) => lib_plankton.pit.is_between( + lib_plankton.pit.from_datetime(event_entry.object.begin), from_pit, to_pit ) @@ -278,9 +344,15 @@ namespace _zeitbild.service.calendar .filter( (event) => (event !== null) ) + .map( + (event) => ({ + "id": null, + "object": event, + }) + ) .filter( - (event) => lib_plankton.pit.is_between( - lib_plankton.pit.from_datetime(event.begin), + (event_entry) => lib_plankton.pit.is_between( + lib_plankton.pit.from_datetime(event_entry.object.begin), from_pit, to_pit ) @@ -313,7 +385,8 @@ namespace _zeitbild.service.calendar { calendar_id : _zeitbild.type_calendar_id; calendar_name : string; - event : _zeitbild.type_event_object; + event_id : (null | _zeitbild.type_local_resource_event_id); + event_object : _zeitbild.type_event_object; } > > @@ -346,7 +419,12 @@ namespace _zeitbild.service.calendar const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read( calendar_id ); - const events : Array<_zeitbild.type_event_object> = await get_events( + const events : Array< + { + id : (null | _zeitbild.type_local_resource_event_id); + object : _zeitbild.type_event_object; + } + > = await get_events( calendar_id, from_pit, to_pit, @@ -355,10 +433,11 @@ namespace _zeitbild.service.calendar return Promise.resolve( events .map( - (event) => ({ + (event_entry) => ({ "calendar_id": calendar_id, "calendar_name": calendar_object.name, - "event": event, + "event_id": event_entry.id, + "event_object": event_entry.object, }) ) ); diff --git a/source/services/resource.ts b/source/services/resource.ts index a401191..ba5b53e 100644 --- a/source/services/resource.ts +++ b/source/services/resource.ts @@ -12,6 +12,38 @@ namespace _zeitbild.service.resource } + /** + */ + export async function event_get( + resource_id : _zeitbild.type_resource_id, + local_resource_event_id : _zeitbild.type_local_resource_event_id + ) : Promise<_zeitbild.type_event_object> + { + const resource_object : _zeitbild.type_resource_object = await _zeitbild.repository.resource.read( + resource_id + ); + switch (resource_object.kind) { + case "local": { + const event_object : _zeitbild.type_event_object = await _zeitbild.repository.resource.local_resource_event_read( + resource_id, + local_resource_event_id + ); + return Promise.resolve<_zeitbild.type_event_object>(event_object); + break; + } + case "caldav": { + // TODO + return Promise.reject(new Error("not implemented")); + break; + } + default: { + // @ts-ignore + throw (new Error("unhandled resource kind: " + resource_object.kind)); + } + } + } + + /** */ export async function event_add( @@ -49,6 +81,45 @@ namespace _zeitbild.service.resource } + /** + */ + export async function event_change( + resource_id : _zeitbild.type_resource_id, + local_resource_event_id : _zeitbild.type_local_resource_event_id, + event_object : _zeitbild.type_event_object + ) : Promise + { + const resource_object : _zeitbild.type_resource_object = await _zeitbild.repository.resource.read( + resource_id + ); + switch (resource_object.kind) { + case "local": { + await _zeitbild.repository.resource.local_resource_event_update( + resource_id, + local_resource_event_id, + event_object + ); + return Promise.resolve(undefined); + break; + } + case "caldav": { + if (resource_object.data.read_only) { + return Promise.reject(new Error("can not change event of read only caldav resource")); + } + else { + // TODO + return Promise.reject(new Error("not implemented")); + } + break; + } + default: { + // @ts-ignore + throw (new Error("unhandled resource kind: " + resource_object.kind)); + } + } + } + + /** */ export async function event_remove( diff --git a/tools/makefile b/tools/makefile index 97b480f..1486827 100644 --- a/tools/makefile +++ b/tools/makefile @@ -59,7 +59,9 @@ ${dir_temp}/zeitbild-unlinked.js: \ ${dir_source}/api/actions/session_end.ts \ ${dir_source}/api/actions/calendar_list.ts \ ${dir_source}/api/actions/calendar_add.ts \ + ${dir_source}/api/actions/calendar_event_get.ts \ ${dir_source}/api/actions/calendar_event_add.ts \ + ${dir_source}/api/actions/calendar_event_change.ts \ ${dir_source}/api/actions/calendar_event_remove.ts \ ${dir_source}/api/actions/events.ts \ ${dir_source}/api/functions.ts \