Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/calendar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ try do
description: "This is an example calendar."
)

{:ok, calendars} =
client
|> CalDAVClient.Calendar.list()

:ok =
client
|> CalDAVClient.Calendar.update(calendar_url,
Expand Down
34 changes: 34 additions & 0 deletions lib/caldav_client/calendar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@ defmodule CalDAVClient.Calendar do
import CalDAVClient.HTTP.Error
import CalDAVClient.Tesla

@type t :: %__MODULE__{
url: String.t(),
name: String.t(),
type: String.t(),
timezone: String.t()
}
@enforce_keys [:url, :name, :type, :timezone]
defstruct @enforce_keys

@xml_middlewares [
CalDAVClient.Tesla.ContentTypeXMLMiddleware,
CalDAVClient.Tesla.ContentLengthMiddleware
]

@doc """
Fetches the list of calendars (see [RFC 4791, section 4.2](https://tools.ietf.org/html/rfc4791#section-4.2)).
"""
@spec list(CalDAVClient.Client.t()) ::
:ok | {:error, any()}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:ok | {:error, any()}
{:ok, [t()]} | {:error, any()}

:)

def list(caldav_client) do
case caldav_client
|> make_tesla_client(@xml_middlewares)
|> Tesla.request(
method: :propfind,
url: "",
body: CalDAVClient.XML.Builder.build_list_calendar_xml()
) do
{:ok, %Tesla.Env{status: 207, body: response_xml}} ->
calendars = response_xml |> CalDAVClient.XML.Parser.parse_calendars()
{:ok, calendars}

{:ok, %Tesla.Env{status: code}} ->
{:error, reason_atom(code)}

{:error, _reason} = error ->
error
end
end

@doc """
Creates a calendar (see [RFC 4791, section 5.3.1.2](https://tools.ietf.org/html/rfc4791#section-5.3.1.2)).

Expand Down
24 changes: 24 additions & 0 deletions lib/caldav_client/xml/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ defmodule CalDAVClient.XML.Builder do
@default_event_from DateTime.from_naive!(~N[0000-01-01 00:00:00], "Etc/UTC")
@default_event_to DateTime.from_naive!(~N[9999-12-31 23:59:59], "Etc/UTC")

@doc """
Generates XML request body to fetch the list of calendars
(see [RFC 4791, section 4.2](https://tools.ietf.org/html/rfc4791#section-4.2)).
"""
@spec build_list_calendar_xml() :: String.t()
def build_list_calendar_xml() do
{"D:propfind",
[
"xmlns:D": "DAV:",
"xmlns:CS": "http://calendarserver.org/ns/",
"xmlns:C": "urn:ietf:params:xml:ns:caldav"
],
[
{"D:prop", nil,
[
{"D:resourcetype"},
{"D:displayname"},
{"C:calendar-timezone"},
{"C:supported-calendar-component-set"}
]}
]}
|> serialize()
end

@doc """
Generates XML request body to create a calendar
(see [RFC 4791, section 5.3.1.2](https://tools.ietf.org/html/rfc4791#section-5.3.1.2)).
Expand Down
19 changes: 19 additions & 0 deletions lib/caldav_client/xml/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ defmodule CalDAVClient.XML.Parser do
@icalendar_xpath ~x"./*[local-name()='propstat']/*[local-name()='prop']/*[local-name()='calendar-data']/text()"s
@etag_xpath ~x"./*[local-name()='propstat']/*[local-name()='prop']/*[local-name()='getetag']/text()"s

@cal_name_xpath ~x"./*[local-name()='propstat']/*[local-name()='prop']/*[local-name()='displayname']/text()"s
@cal_type_xpath ~x"./*[local-name()='propstat']/*[local-name()='prop']/*[local-name()='supported-calendar-component-set']/*[local-name()='comp']/@name"s
@cal_timezone_xpath ~x"./*[local-name()='propstat']/*[local-name()='prop']/*[local-name()='calendar-timezone']/text()"s

@doc """
Parses XML response body into a list of events.
"""
Expand All @@ -23,4 +27,19 @@ defmodule CalDAVClient.XML.Parser do
)
|> Enum.map(&struct(CalDAVClient.Event, &1))
end

@doc """
Parses XML response body into a list of calendars.
"""
@spec parse_calendars(response_xml :: String.t()) :: [CalDAVClient.Calendar.t()]
def parse_calendars(response_xml) do
response_xml
|> xpath(@event_xpath,
url: @url_xpath,
name: @cal_name_xpath,
type: @cal_type_xpath,
timezone: @cal_timezone_xpath
)
|> Enum.map(&struct(CalDAVClient.Calendar, &1))
end
end
20 changes: 20 additions & 0 deletions test/caldav_client/xml/builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ defmodule CalDAVClient.XML.BuilderTest do
use ExUnit.Case, async: true
doctest CalDAVClient.XML.Builder

test "generates list calendar XML request body" do
# https://tools.ietf.org/html/rfc4791#section-4.2

actual = CalDAVClient.XML.Builder.build_list_calendar_xml()

expected = """
<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:resourcetype/>
<D:displayname/>
<C:calendar-timezone/>
<C:supported-calendar-component-set/>
</D:prop>
</D:propfind>
"""

assert_xml_identical(actual, expected)
end

test "generates create calendar XML request body" do
# https://tools.ietf.org/html/rfc4791#section-5.3.1.2

Expand Down
Loading