namespace _zeitbild.service.caldav { /** * @todo use pod for output * @todo get api paths in props from config * @todo consider to outsorce to plankton */ export function probe( input : (null | lib_plankton.xml.type_node_data), { "force_props": force_props = null, } : { force_props ?: (null | Array); } = { } ) : (null | lib_plankton.xml.type_node_data) { const http_protocol : string = "HTTP/1.1"; let props : (null | Array); if (force_props) { props = force_props; } else { if ( (input !== null) && (input.kind === "complex") && ( (input.data.tag.toLowerCase() === "d:propfind") || (input.data.tag.toLowerCase() === "propfind") ) && (input.data.children.length === 1) && (input.data.children[0].kind === "complex") && ( (input.data.children[0].data.tag.toLowerCase() === "d:prop") || (input.data.children[0].data.tag.toLowerCase() === "prop") ) ) { props = input.data.children[0].data.children.map( node => { switch (node.kind) { case "complex": { return node.data.tag; break; } default: { throw (new Error("unexpected node type for prop")); break; } } } ); props.sort(); } else { props = null; } } if (props === null) { lib_plankton.log.notice( "service.caldav.probe.unexpected_input", { "input": input, } ); return null; } else { const answers : Record< string, lib_plankton.webdav.type_data_prop_value > = { // webdav 1 /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.2 */ "displayname": { "kind": "primitive", "data": "projects" }, /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.4 */ /* "getcontentlength": { "kind": "none", "data": null }, */ /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.5 */ /* "getcontenttype": { "kind": "none", "data": null }, */ /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.7 */ /* "getlastmodified": { "kind": "none", "data": null }, */ /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.9 */ "resourcetype": { "kind": "resourcetype", "data": { "kind": "collection", "type": "calendar", } }, // webdav 2 /** * @see https://datatracker.ietf.org/doc/html/rfc3744#section-4.2 */ "principal-url": { "kind": "href", "data": "/caldav" }, /** * @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.1 */ "owner": { "kind": "primitive", "data": "" }, /** * @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.4 */ "current-user-privilege-set": { "kind": "privileges", "data": [ "read" ] }, // caldav /** * @see https://datatracker.ietf.org/doc/html/rfc4791#section-6.2.1 */ "calendar-home-set": { "kind": "href", "data": "/caldav/project" }, // WebDAV Current Principal Extension /** * @see https://datatracker.ietf.org/doc/html/rfc5397#section-3 */ "current-user-principal": { "kind": "href", "data": "/caldav" }, // unknown /* "calendar-color": { "kind": "none", "data": null }, "executable": { "kind": "none", "data": null }, "checked-in": { "kind": "none", "data": null }, "checked-out": { "kind": "none", "data": null }, "calendar-user-address-set": { "kind": "none", "data": null }, */ }; return { "kind": "root", "data": { "version": "1.0", "encoding": "utf-8", "content": lib_plankton.webdav.data_multistatus_encode_xml( { "responses": [ { "href": "/caldav/project", "body": { /** * @todo maybe propstats needs to be filled with props (.map …) */ "propstats": ( false ? [ { "prop": (props ?? []).map( (prop) => { const prop_parts : Array = prop.toLowerCase().split(":"); const prop_normalized : string = ((prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":")); if (! (prop_normalized in answers)) { lib_plankton.log.error( "api.caldav_probe.unhandled_prop", prop_normalized ); throw (new Error("unhandled prop: " + prop_normalized)); } else { return { "name": prop, "value": ( answers[prop_normalized] ?? { "kind": "none", "data": null, } ), }; } } ), "status": (http_protocol + " 200 OK"), "description": null, }, ] : props.map( (prop) => { const prop_parts : Array = prop.toLowerCase().split(":"); const prop_normalized : string = ( (prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":") ); if (! (prop_normalized in answers)) { lib_plankton.log.notice( "api.caldav_probe.unhandled_prop", prop_normalized ); /* throw (new Error("unhandled prop: " + prop_normalized)); */ return { "prop": [ { "name": prop, "value": { "kind": "none", "data": null, } }, ], "status": (http_protocol + " 404 Not Found"), "description": null, }; } else { return { "prop": [ { "name": prop, "value": answers[prop_normalized], }, ], "status": (http_protocol + " 200 OK"), "description": null, }; }; } ) ) }, "description": null, } ], "description": null, } ) } }; } } /** */ export function projects( user_id : type_user_id ) : Promise { return ( _zeitbild.service.calendar.overview(user_id) .then( (data_raw) => Promise.resolve( data_raw .map( (entry) => ({ "id": entry.id, "name": entry.name, "access_level": _zeitbild.value_object.access_level.to_string(entry.access_level), }) ) ) ) .then( (data) => Promise.resolve( { "kind": "root", "data": { "version": "1.0", "encoding": "utf-8", "content": lib_plankton.webdav.data_multistatus_encode_xml( { "responses": data.map( (entry) => ({ "href": lib_plankton.string.coin( "/caldav/project/{{id}}", { "id": entry.id.toFixed(0), } ), "body": { "propstats": [ { "prop": [ { "name": "D:displayname", "value": { "kind": "primitive", "data": entry.name, }, }, { "name": "D:resourcetype", "value": { "kind": "resourcetype", "data": { "kind": "collection", "type": "calendar", } } }, ], "status": "HTTP/1.1 200 OK", "description": null, } ], }, "description": null, }) ), "description": null, } ) } } ) ) ); } }