Git Product home page Git Product logo

elixir-caldav-client's Introduction

Elixir CalDAV Client

Hex.pm API Docs

This library allows for managing calendars and events on a remote calendar server according to CalDAV specification (RFC 4791). Supports time zones, recurrence expansion and ETags. Internally uses Tesla HTTP client.

Please note that conversion between native Elixir structures and iCalendar format (RFC 5545) is beyond the scope of this library. The following packages are recommended:

Installation

CalDAV Client is published on Hex. Add it to your list of dependencies in mix.exs:

def deps do
  [
    {:caldav_client, "~> 2.0"},

    # time zone database
    {:tzdata, "~> 1.1"},

    # recommended Tesla adapter
    {:hackney, "~> 1.18"},
  ]
end

Then run mix deps.get to install the package and its dependencies.

It is also required to configure the time zone database and the default Tesla adapter in the config/config.exs of your project:

# config/config.exs

import Config

config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase

config :tesla, adapter: Tesla.Adapter.Hackney

The default Tesla adapter is Erlang's built-in httpc, but currently it does not support custom HTTP methods such as MKCALENDAR or REPORT.

Documentation

Available at HexDocs.

Examples

Client

The %CalDAVClient.Client{} struct aggregates the connection details such as the server address and user credentials.

client = %CalDAVClient.Client{
  server_url: "http://127.0.0.1:8800/cal.php",
  auth: %CalDAVClient.Auth.Basic{
    username: "username",
    password: "password"
  }
}

The library supports Basic, Digest and Bearer authentication:

%CalDAVClient.Auth.Basic{
  username: "username",
  password: "password"
}

%CalDAVClient.Auth.Digest{
  username: "username",
  password: "password"
}

%CalDAVClient.Auth.Bearer{
  token: "token"
}

Calendar

Each calendar user (or principal, according to CalDAV terminology) can have multiple calendars, which are identified by URLs.

calendar_url = CalDAVClient.URL.Builder.build_calendar_url("username", "example")
# "/calendars/username/example"

:ok =
  client
  |> CalDAVClient.Calendar.create(calendar_url,
    name: "Example calendar",
    description: "This is an example calendar."
  )

:ok = client |> CalDAVClient.Calendar.update(calendar_url, name: "Lorem ipsum")

:ok = client |> CalDAVClient.Calendar.delete(calendar_url)

In case of any failure, {:error, reason} tuple will be returned.

Event

event_url = CalDAVClient.URL.Builder.build_event_url(calendar_url, "event.ics")
# "/calendars/username/example/event.ics"

event_icalendar = """
BEGIN:VCALENDAR
PRODID:-//Elixir//CalDAV//EN
VERSION:2.0
BEGIN:VEVENT
UID:totally-random-uid
DTSTAMP:20210101T120000Z
DTSTART:20210101T140000Z
END:VEVENT
END:VCALENDAR
"""

{:ok, etag} = client |> CalDAVClient.Event.create(event_url, event_icalendar)

CalDAVClient.Event.create/3 returns {:error, :unsupported_media_type} on malformed payload or {:error, :already_exists} when the specified URL is already taken (see If-None-Match).

You may get a single event by its URL address:

{:ok, icalendar, etag} = client |> CalDAVClient.Event.get(event_url)

It is also possible to find the event with a specific UID property within the calendar:

{:ok, %CalDAVClient.Event{url: url, icalendar: icalendar, etag: etag}} =
  client |> CalDAVClient.Event.find_by_uid(calendar_url, event_uid)

Both CalDAVClient.Event.get/2 and CalDAVClient.Event.find_by_uid/3 return {:error, :not_found} when the event does not exist.

When modifying an event, you may optionally include the etag option in order to prevent simultaneous updates and ensure that the appropriate version of the event will be overwritten (see ETag).

{:ok, etag} = client |> CalDAVClient.Event.update(event_url, event_icalendar, etag: etag)
:ok = client |> CalDAVClient.Event.delete(event_url, etag: etag)

When ETag does not match, both CalDAVClient.Event.update/4 and CalDAVClient.Event.delete/3 return {:error, :bad_etag}.

Events

CalDAV specification defines a way to retrieve all events that meet certain criteria, which can be used to list all events within a specified time range.

from = DateTime.from_naive!(~N[2021-01-01 00:00:00], "Europe/Warsaw")
to = DateTime.from_naive!(~N[2021-02-01 00:00:00], "Europe/Warsaw")

{:ok, events} = client |> CalDAVClient.Event.get_events(calendar_url, from, to)

You may also pass expand: true option to enable recurrence expansion, which will force the calendar server to convert all events having the RRULE property into a series of occurrences within the specified time range with the RECURRENCE-ID property set.

{:ok, events} = client |> CalDAVClient.Event.get_events(calendar_url, from, to, expand: true)

It is also possible to retrieve only the events with an alarm (VALARM) within a specified time range:

{:ok, events} = client |> CalDAVClient.Event.get_events_by_alarm(calendar_url, from, to)

For custom event reports, pass the XML request body to CalDAVClient.Event.get_events_by_xml/3 function:

{:ok, events} = client |> CalDAVClient.Event.get_events_by_xml(calendar_url, request_xml)

In all cases above, events is a list of %CalDAVClient.Event{} structs with url, icalendar and etag fields.

Testing

By default, mix test will execute only the unit tests which check XML building and parsing as well as URL generation and iCalendar date-time serialization.

The full test suite requires a connection to a calendar server, e.g. Baïkal (Docker image available here). When installed and configured, create a test user account and provide credentials along with the server details in config/test.exs in this library.

Please note that the test suite operates directly on the calendar server and will automatically create and delete the test calendar during execution.

# config/test.exs

config :caldav_client, :test_server,
  server_url: "http://127.0.0.1:8800/cal.php",
  username: "username",
  password: "password"

When configured, the test suite including integration tests can be executed by running:

mix test --include integration

Copyright and License

Copyright 2022, Software Mansion

Software Mansion

The code located in this repository is licensed under the Apache License, Version 2.0.

elixir-caldav-client's People

Contributors

duderman avatar mspanc avatar tomekzaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

elixir-caldav-client's Issues

OAuth2 token support

Hello, are there any plans to extend the auth method to support Bearer + Token Authentication header for OAuth2? Seems that tesla already supports this so it look like a small change.

Timezone Problem at Full-Day Events

While Datetime events work without a problem for me, full-day events are one day longer in the answer with this library than with the macOS or Thunderbird calendar. I am not sure where the problem occurs, but I guess it's somewhere in the timezone conversion.

My calendar server (Baikal) works in UTC while my pc and my caldav client work in ETC.

The response of this library already contains the wrong end day, so I assume it's connected to this library. I'm happy to provide more information, I just don't know what's helpful.

Remove hard dependency on hackney

Currently the library has dependency on hackney and it might not be the preferred backend for tesla in the app using this package.

However, tests will fail without some backend as default httpc does not support custom HTTP methods.

Removing it and adding only: test fails with:

obraz

Somehow, hackney should be enabled only for testing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.