-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from jhuckabee/todo-support
Add support for tasks
- Loading branch information
Showing
10 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "../errors" | ||
require_relative "../todo" | ||
require_relative "../requests/list_todos" | ||
|
||
module Calendav | ||
module Clients | ||
class TodosClient | ||
def initialize(client, endpoint, credentials) | ||
@client = client | ||
@endpoint = endpoint | ||
@credentials = credentials | ||
end | ||
|
||
def create(calendar_url, todo_identifier, ics) | ||
todo_url = merged_url(calendar_url, todo_identifier) | ||
result = endpoint.put(ics, url: todo_url, content_type: :ics) | ||
|
||
Todo.new( | ||
url: result.headers["Location"] || todo_url, | ||
etag: result.headers["ETag"] | ||
) | ||
end | ||
|
||
def delete(todo_url, etag: nil) | ||
endpoint.delete(url: todo_url, etag: etag) | ||
rescue PreconditionError | ||
false | ||
end | ||
|
||
def find(todo_url) | ||
response = endpoint.get(url: todo_url) | ||
|
||
Todo.new( | ||
url: todo_url, | ||
calendar_data: response.body.to_s, | ||
etag: response.headers["ETag"] | ||
) | ||
end | ||
|
||
def list(calendar_url) | ||
request = Requests::ListTodos.call | ||
|
||
endpoint | ||
.report(request.to_xml, url: calendar_url, depth: 1) | ||
.reject { |node| node.xpath(".//caldav:calendar-data").text.empty? } | ||
.collect { |node| Todo.from_xml(calendar_url, node) } | ||
end | ||
|
||
def update(todo_url, ics, etag: nil) | ||
result = endpoint.put( | ||
ics, url: todo_url, content_type: :ics, etag: etag | ||
) | ||
|
||
Todo.new( | ||
url: todo_url, | ||
etag: result.headers["ETag"] | ||
) | ||
rescue PreconditionError | ||
nil | ||
end | ||
|
||
private | ||
|
||
attr_reader :client, :endpoint, :credentials | ||
|
||
def merged_url(calendar_url, todo_identifier) | ||
"#{calendar_url.delete_suffix('/')}/#{todo_identifier}" | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
module Calendav | ||
module Parsers | ||
class TodoXML | ||
def self.call(...) | ||
new(...).call | ||
end | ||
|
||
def initialize(element) | ||
@element = element | ||
end | ||
|
||
def call | ||
{ | ||
calendar_data: value(".//caldav:calendar-data"), | ||
etag: value(".//dav:getetag") | ||
} | ||
end | ||
|
||
private | ||
|
||
attr_reader :element | ||
|
||
def value(xpath) | ||
node = element.xpath(xpath) | ||
return nil if node.children.empty? | ||
|
||
if node.children.any?(&:element?) | ||
node.children.select(&:element?).collect(&:to_xml).join | ||
else | ||
node.children.text | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# frozen_string_literal: true | ||
|
||
require "nokogiri" | ||
|
||
require_relative "../namespaces" | ||
|
||
module Calendav | ||
module Requests | ||
class ListTodos | ||
def self.call(...) | ||
new.call | ||
end | ||
|
||
def call | ||
Nokogiri::XML::Builder.new do |xml| | ||
xml["caldav"].public_send("calendar-query", NAMESPACES) do | ||
xml["dav"].prop do | ||
xml["dav"].getetag | ||
xml["caldav"].public_send(:"calendar-data") | ||
end | ||
xml["caldav"].filter do | ||
xml["caldav"].public_send(:"comp-filter", name: "VCALENDAR") do | ||
xml["caldav"].public_send(:"comp-filter", name: "VTODO") | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
def from | ||
return nil if @from.nil? | ||
|
||
@from.utc.iso8601.delete(":-") | ||
end | ||
|
||
def to | ||
return nil if @to.nil? | ||
|
||
@to.utc.iso8601.delete(":-") | ||
end | ||
|
||
def range? | ||
to || from | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "contextual_url" | ||
require_relative "parsers/todo_xml" | ||
|
||
module Calendav | ||
class Todo | ||
ATTRIBUTES = %i[url calendar_data etag].freeze | ||
|
||
def self.from_xml(host, node) | ||
new( | ||
{ | ||
url: ContextualURL.call(host, node.xpath("./dav:href").text) | ||
}.merge( | ||
Parsers::TodoXML.call(node) | ||
) | ||
) | ||
end | ||
|
||
def initialize(attributes = {}) | ||
@attributes = attributes | ||
end | ||
|
||
ATTRIBUTES.each do |attribute| | ||
define_method(attribute) { attributes[attribute] } | ||
end | ||
|
||
def to_h | ||
attributes.dup | ||
end | ||
|
||
def summary | ||
inner_todo.summary | ||
end | ||
|
||
def due | ||
inner_todo.due | ||
end | ||
|
||
def status | ||
inner_todo.status | ||
end | ||
|
||
def unloaded? | ||
calendar_data.nil? | ||
end | ||
|
||
private | ||
|
||
attr_reader :attributes | ||
|
||
def inner_calendar | ||
Icalendar::Calendar.parse(calendar_data).first | ||
end | ||
|
||
def inner_todo | ||
@inner_todo = inner_calendar.todos.first | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.