[add] api-actions:calendar_get,calendar_change,calendar_remove

This commit is contained in:
Fenris Wolf 2024-09-30 19:52:24 +02:00
parent c3738ffbc0
commit 4dec750e72
14 changed files with 608 additions and 71 deletions

View file

@ -12,11 +12,11 @@ namespace _zeitbild.api
{
name : string;
access : {
default_level : ("none" | "view" | "edit" | "admin");
default_level : string;
attributed : Array<
{
user_id : int;
level : ("none" | "view" | "edit" | "admin");
level : string;
}
>;
};
@ -43,6 +43,10 @@ namespace _zeitbild.api
"/calendar",
{
"description": "erstellt einen Kalender",
"input_schema": () => ({
"nullable": true,
// TODO
}),
"output_schema": () => ({
"nullable": true,
"type": "integer",

View file

@ -0,0 +1,94 @@
namespace _zeitbild.api
{
/**
*/
export function register_calendar_change(
rest_subject : lib_plankton.rest.type_rest
) : void
{
register<
{
name : string;
access : {
default_level : ("none" | "view" | "edit" | "admin");
attributed : Array<
{
user_id : int;
level : ("none" | "view" | "edit" | "admin");
}
>;
};
},
null
>(
rest_subject,
lib_plankton.http.enum_method.put,
"/calendar/:calendar_id",
{
"description": "ändert einen Kalender (Resource ist fest)",
"input_schema": () => ({
"nullable": true,
// TODO
}),
"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 {
const calendar_id : _zeitbild.type_calendar_id = parseInt(stuff.path_parameters["calendar_id"]);
// TODO move logic to calendar service
const calendar_object_old : _zeitbild.type_calendar_object = await _zeitbild.service.calendar.get(
calendar_id,
user_id
);
const calendar_object_new : _zeitbild.type_calendar_object = {
"name": stuff.input.name,
"access": {
"default_level": _zeitbild.value_object.access_level.from_string(stuff.input.access.default_level),
"attributed": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
x => x.toFixed(0),
{
"pairs": (
stuff.input.access.attributed
.map(
(entry) => ({
"key": entry.user_id,
"value": _zeitbild.value_object.access_level.from_string(entry.level),
})
)
),
}
)
),
},
"resource_id": calendar_object_old.resource_id
};
await _zeitbild.service.calendar.change(
calendar_id,
calendar_object_new,
user_id
);
return Promise.resolve(
{
"status_code": 200,
"data": null,
}
);
}
}
}
);
}
}

View file

@ -0,0 +1,78 @@
namespace _zeitbild.api
{
/**
*/
export function register_calendar_get(
rest_subject : lib_plankton.rest.type_rest
) : void
{
register<
null,
{
name : string;
access : {
default_level : string;
attributed : Array<
{
user_id : int;
level : string;
}
>
};
resource_id : int;
}
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/calendar/:calendar_id",
{
"description": "liest die Daten eines Kalenders aus",
"output_schema": () => ({
"nullable": true, // TODO
}),
"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);
const calendar_id : _zeitbild.type_calendar_id = parseInt(stuff.path_parameters["calendar_id"]);
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.service.calendar.get(
calendar_id,
user_id
);
const result = {
"name": calendar_object.name,
"access": {
"default_level": _zeitbild.api.access_level_encode(calendar_object.access.default_level),
"attributed": lib_plankton.call.convey(
calendar_object.access.attributed,
[
lib_plankton.map.dump,
(pairs : Array<{key : _zeitbild.type_user_id; value : _zeitbild.enum_access_level;}>) => (
pairs
.map(
(pair : {key : _zeitbild.type_user_id; value : _zeitbild.enum_access_level;}) => ({
"user_id": pair.key,
"level": _zeitbild.api.access_level_encode(pair.value)
})
)
)
]
),
},
"resource_id": calendar_object.resource_id,
};
return Promise.resolve(
{
"status_code": 200,
"data": result,
}
);
}
}
);
}
}

View file

@ -0,0 +1,44 @@
namespace _zeitbild.api
{
/**
*/
export function register_calendar_remove(
rest_subject : lib_plankton.rest.type_rest
) : void
{
register<
null,
null
>(
rest_subject,
lib_plankton.http.enum_method.delete,
"/calendar/:calendar_id",
{
"description": "löscht einen Kalender",
"output_schema": () => ({
"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);
const calendar_id : _zeitbild.type_calendar_id = parseInt(stuff.path_parameters["calendar_id"]);
await _zeitbild.service.calendar.remove(
calendar_id,
user_id
);
return Promise.resolve(
{
"status_code": 200,
"data": null,
}
);
}
}
);
}
}

View file

@ -23,6 +23,7 @@ namespace _zeitbild.api
{
calendar_id : int;
calendar_name : string;
access_level : string;
event_id : (null | int);
event_object : _zeitbild.type_event_object;
}
@ -68,6 +69,11 @@ namespace _zeitbild.api
"nullable": false,
"type": "string",
},
"access_level": {
"nullable": false,
"type": "string",
"enum": ["none","view","edit","admin"],
},
"event_id": {
"nullable": true,
"type": "number",
@ -152,7 +158,18 @@ namespace _zeitbild.api
.then(
(data) => Promise.resolve({
"status_code": 200,
"data": data,
"data": (
data
.map(
(entry) => ({
"calendar_id": entry.calendar_id,
"calendar_name": entry.calendar_name,
"access_level": _zeitbild.api.access_level_encode(entry.access_level),
"event_id": entry.event_id,
"event_object": entry.event_object,
})
)
),
})
)
.catch(

View file

@ -0,0 +1,74 @@
namespace _zeitbild.api
{
/**
*/
export function register_users(
rest_subject : lib_plankton.rest.type_rest
) : void
{
register<
null,
Array<
{
id : int;
name : string;
}
>
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/users",
{
"description": "listet alle Nutzer auf",
"query_parameters": () => ([
{
"name": "term",
"required": false,
"description": "search term",
},
]),
"output_schema": () => ({
"type": "array",
"items": {
"nullable": false,
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"nullable": false,
"type": "number",
},
"name": {
"nullable": false,
"type": "string",
},
},
"required": [
"id",
"name",
],
}
}),
"restriction": restriction_logged_in,
"execution": async (stuff) => {
const result : Array<
{
id : _zeitbild.type_user_id;
name : string;
}
> = await _zeitbild.service.user.list(
);
return Promise.resolve(
{
"status_code": 200,
"data": result,
}
);
}
}
);
}
}

View file

@ -34,7 +34,10 @@ namespace _zeitbild.api
// calendar
{
_zeitbild.api.register_calendar_list(rest_subject);
_zeitbild.api.register_calendar_get(rest_subject);
_zeitbild.api.register_calendar_add(rest_subject);
_zeitbild.api.register_calendar_change(rest_subject);
_zeitbild.api.register_calendar_remove(rest_subject);
// event
{
_zeitbild.api.register_calendar_event_get(rest_subject);
@ -45,6 +48,7 @@ namespace _zeitbild.api
}
// misc
{
_zeitbild.api.register_users(rest_subject);
_zeitbild.api.register_events(rest_subject);
}

View file

@ -115,5 +115,38 @@ namespace _zeitbild.api
};
}
/**
*/
export function access_level_encode(
access_level : _zeitbild.enum_access_level
) : string
{
switch (access_level) {
case _zeitbild.enum_access_level.none: {return "none";}
case _zeitbild.enum_access_level.view: {return "view";}
case _zeitbild.enum_access_level.edit: {return "edit";}
case _zeitbild.enum_access_level.admin: {return "admin";}
default: {throw (new Error("invalid access level: " + String(access_level)));}
}
}
/**
*/
export function access_level_decode(
access_level_ : string
) : _zeitbild.enum_access_level
{
switch (access_level_) {
case "none": {return _zeitbild.enum_access_level.none;}
case "view": {return _zeitbild.enum_access_level.view;}
case "edit": {return _zeitbild.enum_access_level.edit;}
case "admin": {return _zeitbild.enum_access_level.admin;}
default: {throw (new Error("invalid encoded access level: " + String(access_level_)));}
}
}
}

View file

@ -1,4 +1,5 @@
/**
*/
type type_data = {

View file

@ -285,38 +285,118 @@ namespace _zeitbild.repository.calendar
/**
*/
export function create(
export async function create(
calendar_object : _zeitbild.type_calendar_object
) : Promise<_zeitbild.type_calendar_id>
{
return (
Promise.resolve<_zeitbild.type_calendar_object>(calendar_object)
.then<type_dispersal>(
(calendar_object) => Promise.resolve<type_dispersal>(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.access_attributed_rows
.map(
(access_attributed_row) => get_access_attributed_chest().write(
const dispersal : type_dispersal = encode(calendar_object);
const core_store = get_core_store();
const calendar_id : _zeitbild.type_calendar_id = await core_store.create(
dispersal.core_row
);
for await (const access_attributed_row of dispersal.access_attributed_rows) {
get_access_attributed_chest().write(
[calendar_id, access_attributed_row["user_id"]],
{"level": access_attributed_row["level"]}
)
)
)
.then(
() => Promise.resolve<_zeitbild.type_calendar_id>(calendar_id)
)
)
)
)
)
);
}
return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id);
}
/**
*/
export async function update(
calendar_id : _zeitbild.type_calendar_id,
calendar_object : _zeitbild.type_calendar_object
) : Promise<void>
{
const dispersal : type_dispersal = encode(calendar_object);
// core
{
const core_store = get_core_store();
await core_store.update(
calendar_id,
dispersal.core_row
);
}
// attributed access
{
const access_attributed_chest = get_access_attributed_chest();
const hits : Array<Record<string, any>> = await access_attributed_chest.search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": calendar_id,
}
}
);
const contrast = lib_plankton.list.contrast<
Record<string, any>,
Record<string, any>
>(
hits,
hit => hit["user_id"],
dispersal.access_attributed_rows,
row => row["user_id"]
);
// delete
for await (const entry of contrast.only_left) {
await access_attributed_chest.delete(
[calendar_id, entry.left["user_id"]]
);
}
// update
for await (const entry of contrast.both) {
await access_attributed_chest.write(
[calendar_id, entry.right["user_id"]],
{"level": entry.right["level"]}
);
}
// create
for await (const entry of contrast.only_right) {
await access_attributed_chest.write(
[calendar_id, entry.right["user_id"]],
{"level": entry.right["level"]}
);
}
}
return Promise.resolve<void>(undefined);
}
/**
*/
export async function delete_(
calendar_id : _zeitbild.type_calendar_id
) : Promise<void>
{
const core_store = get_core_store();
const access_attributed_chest = get_access_attributed_chest();
// attributed access
{
const hits : Array<Record<string, any>> = await access_attributed_chest.search(
{
"expression": "(calendar_id = {{calendar_id}})",
"arguments": {
"calendar_id": calendar_id,
}
}
);
for await (const hit of hits) {
await access_attributed_chest.delete(
[calendar_id, hit["user_id"]]
);
}
}
// core
{
await core_store.delete(
calendar_id
);
}
return Promise.resolve<void>(undefined);
}
/**

View file

@ -70,6 +70,31 @@ namespace _zeitbild.repository.user
}
/**
*/
export async function list(
) : Promise<
Array<
{
id : _zeitbild.type_user_id;
name : string;
}
>
>
{
const hits : Array<{key : int; preview : Record<string, any>;}> = await get_store().search({"expression": "TRUE", "arguments": {}});
return Promise.resolve(
hits
.map(
(hit) => ({
"id": hit.key,
"name": hit.preview.name,
})
)
);
}
/**
*/
export async function read(

View file

@ -3,12 +3,56 @@ namespace _zeitbild.service.calendar
{
/**
* checks if a user has a sufficient access level
*/
export function add(
calendar_object : _zeitbild.type_calendar_object
) : Promise<_zeitbild.type_calendar_id>
function get_access_level(
calendar_object : _zeitbild.type_calendar_object,
user_id : _zeitbild.type_user_id
) : _zeitbild.enum_access_level
{
return _zeitbild.repository.calendar.create(calendar_object);
return calendar_object.access.attributed.get(
user_id,
lib_plankton.pod.make_filled<_zeitbild.enum_access_level>(
calendar_object.access.default_level
)
);
}
/**
* checks if a user has a sufficient access level
*/
function wrap_check_access_level<type_result>(
calendar_object : _zeitbild.type_calendar_object,
user_id : _zeitbild.type_user_id,
threshold : _zeitbild.enum_access_level,
success_handler : (
(access_level : _zeitbild.enum_access_level)
=>
Promise<type_result>
)
) : Promise<type_result>
{
const access_level : _zeitbild.enum_access_level = get_access_level(
calendar_object,
user_id
);
if (! _zeitbild.value_object.access_level.order(threshold, access_level)) {
return Promise.reject<type_result>(
new Error(
lib_plankton.string.coin(
"insufficient access level; at least required: {{threshold}}, actual: {{actual}}",
{
"threshold": _zeitbild.value_object.access_level.to_string(threshold),
"actual": _zeitbild.value_object.access_level.to_string(access_level),
}
)
)
);
}
else {
return success_handler(access_level);
}
}
@ -62,50 +106,63 @@ namespace _zeitbild.service.calendar
/**
*/
export async function get(
calendar_id : _zeitbild.type_calendar_id
calendar_id : _zeitbild.type_calendar_id,
user_id : _zeitbild.type_user_id
) : Promise<_zeitbild.type_calendar_object>
{
return _zeitbild.repository.calendar.read(calendar_id);
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id);
return wrap_check_access_level<_zeitbild.type_calendar_object>(
calendar_object,
user_id,
_zeitbild.enum_access_level.view,
() => Promise.resolve(calendar_object)
);
}
/**
* checks if a user has a sufficient access level
*/
function wrap_check_access_level<type_result>(
export function add(
calendar_object : _zeitbild.type_calendar_object
) : Promise<_zeitbild.type_calendar_id>
{
return _zeitbild.repository.calendar.create(calendar_object);
}
/**
*/
export async function change(
calendar_id : _zeitbild.type_calendar_id,
calendar_object : _zeitbild.type_calendar_object,
user_id : _zeitbild.type_user_id,
threshold : _zeitbild.enum_access_level,
success_handler : (
(access_level : _zeitbild.enum_access_level)
=>
Promise<type_result>
)
) : Promise<type_result>
user_id : _zeitbild.type_user_id
) : Promise<void>
{
const access_level : _zeitbild.enum_access_level = calendar_object.access.attributed.get(
const calendar_object_current : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id);
return wrap_check_access_level<void>(
calendar_object_current,
user_id,
lib_plankton.pod.make_filled<_zeitbild.enum_access_level>(
calendar_object.access.default_level
)
_zeitbild.enum_access_level.admin,
() => _zeitbild.repository.calendar.update(calendar_id, calendar_object)
);
if (! _zeitbild.value_object.access_level.order(threshold, access_level)) {
return Promise.reject<type_result>(
new Error(
lib_plankton.string.coin(
"insufficient access level; at least required: {{threshold}}, actual: {{actual}}",
}
/**
*/
export async function remove(
calendar_id : _zeitbild.type_calendar_id,
user_id : _zeitbild.type_user_id
) : Promise<void>
{
"threshold": _zeitbild.value_object.access_level.to_string(threshold),
"actual": _zeitbild.value_object.access_level.to_string(access_level),
}
)
)
const calendar_object_current : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id);
return wrap_check_access_level<void>(
calendar_object_current,
user_id,
_zeitbild.enum_access_level.admin,
() => _zeitbild.repository.calendar.delete_(calendar_id)
);
}
else {
return success_handler(access_level);
}
}
/**
@ -385,6 +442,7 @@ namespace _zeitbild.service.calendar
{
calendar_id : _zeitbild.type_calendar_id;
calendar_name : string;
access_level : _zeitbild.enum_access_level;
event_id : (null | _zeitbild.type_local_resource_event_id);
event_object : _zeitbild.type_event_object;
}
@ -419,6 +477,10 @@ namespace _zeitbild.service.calendar
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
calendar_id
);
const access_level : _zeitbild.enum_access_level = get_access_level(
calendar_object,
user_id
);
const events : Array<
{
id : (null | _zeitbild.type_local_resource_event_id);
@ -436,6 +498,7 @@ namespace _zeitbild.service.calendar
(event_entry) => ({
"calendar_id": calendar_id,
"calendar_name": calendar_object.name,
"access_level": access_level,
"event_id": event_entry.id,
"event_object": event_entry.object,
})

View file

@ -4,11 +4,17 @@ namespace _zeitbild.service.user
/**
*/
export function add(
user_object : _zeitbild.type_user_object
) : Promise<_zeitbild.type_user_id>
export function list(
) : Promise<
Array<
{
return _zeitbild.repository.user.create(user_object);
id : _zeitbild.type_user_id;
name : string;
}
>
>
{
return _zeitbild.repository.user.list();
}
@ -21,4 +27,14 @@ namespace _zeitbild.service.user
return _zeitbild.repository.user.identify(name);
}
/**
*/
export function add(
user_object : _zeitbild.type_user_object
) : Promise<_zeitbild.type_user_id>
{
return _zeitbild.repository.user.create(user_object);
}
}

View file

@ -57,8 +57,12 @@ ${dir_temp}/zeitbild-unlinked.js: \
${dir_source}/api/actions/session_begin.ts \
${dir_source}/api/actions/session_oidc.ts \
${dir_source}/api/actions/session_end.ts \
${dir_source}/api/actions/users.ts \
${dir_source}/api/actions/calendar_list.ts \
${dir_source}/api/actions/calendar_get.ts \
${dir_source}/api/actions/calendar_add.ts \
${dir_source}/api/actions/calendar_change.ts \
${dir_source}/api/actions/calendar_remove.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 \