huginn_todoist_agent 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/huginn_todoist_agent/todoist_agent.rb +24 -18
- data/spec/todoist_agent_spec.rb +207 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fa7c7d07478fe20d4e6615c5dc1888717fbea36
|
4
|
+
data.tar.gz: 85253fc515dcf5b317956a39e56ee5a3016db05f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df7be486f40ee95d74a4db03a8c98cc8a8b762a6d20f624aa28b07fe1d0a58192300e26cf61b14cab93862b3ed8c19eb06b3b74fa47dd4e98c6e5c067e7a3f87
|
7
|
+
data.tar.gz: 7bb8c657d62a72379b4c6034cca488482260466c3cf146024df4bf116087ba2f5a7f88e7ede6da20ff08c231692550102c0d5bd0c46e3e71583f28f383a26b97
|
@@ -9,10 +9,16 @@ module Agents
|
|
9
9
|
|
10
10
|
description do
|
11
11
|
<<-MD
|
12
|
-
The Todoist Agent
|
12
|
+
The Todoist Agent creates items on your Todoist.
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
To authenticate you need to either set `api_token` or provide a credential named
|
15
|
+
`todoist_api_token` to your Todoist API token. You can find it within the
|
16
|
+
Todoist web frontend from "Gear Menu" > Todoist Settings > Account tab.
|
17
|
+
|
18
|
+
Change `content` to whatever the new Todoist item should tell. You can use liquid
|
19
|
+
templating to include parts from the incoming event in the new item.
|
20
|
+
Have a look at the [Wiki](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid)
|
21
|
+
to learn more about liquid templating.
|
16
22
|
|
17
23
|
In order to set a due date provide a `date_string` (which may contain all date string
|
18
24
|
features supported by Todoist).
|
@@ -28,16 +34,16 @@ module Agents
|
|
28
34
|
|
29
35
|
def default_options
|
30
36
|
{
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
"api_token" => "",
|
38
|
+
"content" => "{{ content }}",
|
39
|
+
"date_string" => "today",
|
40
|
+
"project_id" => "",
|
41
|
+
"labels" => "",
|
42
|
+
"priority" => "",
|
37
43
|
}
|
38
44
|
end
|
39
45
|
|
40
|
-
form_configurable :
|
46
|
+
form_configurable :api_token
|
41
47
|
form_configurable :content, type: :text
|
42
48
|
form_configurable :date_string
|
43
49
|
form_configurable :project_id
|
@@ -49,23 +55,23 @@ module Agents
|
|
49
55
|
end
|
50
56
|
|
51
57
|
def validate_options
|
52
|
-
errors.add(:base,
|
58
|
+
errors.add(:base, "you need to specify your Todoist API token or provide a credential named todoist_api_token") unless options["api_token"].present? || credential("todoist_api_token").present?
|
53
59
|
end
|
54
60
|
|
55
61
|
def receive(incoming_events)
|
56
62
|
incoming_events.each do |event|
|
57
63
|
interpolate_with(event) do
|
58
|
-
item = {
|
59
|
-
item[
|
60
|
-
item[
|
61
|
-
item[
|
64
|
+
item = { "content" => interpolated["content"] }
|
65
|
+
item["date_string"] = interpolated["date_string"] if interpolated["date_string"].present?
|
66
|
+
item["project_id"] = interpolated["project_id"].to_i if interpolated["project_id"].present?
|
67
|
+
item["priority"] = interpolated["priority"].to_i if interpolated["priority"].present?
|
62
68
|
|
63
|
-
if interpolated[
|
64
|
-
item[
|
69
|
+
if interpolated["labels"].present?
|
70
|
+
item["labels"] = interpolated["labels"].split(%r{,\s*}).map(&:to_i)
|
65
71
|
end
|
66
72
|
|
67
73
|
log "creating item: #{item}"
|
68
|
-
todoist = Todoist::Client.new(interpolated[
|
74
|
+
todoist = Todoist::Client.new(interpolated["api_token"].present? ? interpolated["api_token"] : credential("todoist_api_token"))
|
69
75
|
todoist.items.create(item)
|
70
76
|
todoist.process!
|
71
77
|
end
|
data/spec/todoist_agent_spec.rb
CHANGED
@@ -1,13 +1,216 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rails_helper"
|
2
|
+
require "huginn_agent/spec_helper"
|
3
|
+
require "uri"
|
3
4
|
|
4
5
|
describe Agents::TodoistAgent do
|
5
6
|
before(:each) do
|
6
|
-
@valid_options =
|
7
|
+
@valid_options = {
|
8
|
+
"api_token" => "some_token_here",
|
9
|
+
"content" => "foobar",
|
10
|
+
}
|
7
11
|
@checker = Agents::TodoistAgent.new(:name => "TodoistAgent", :options => @valid_options)
|
8
12
|
@checker.user = users(:bob)
|
9
13
|
@checker.save!
|
14
|
+
|
15
|
+
@event = Event.new
|
16
|
+
@event.agent = agents(:bob_weather_agent)
|
17
|
+
@event.payload = {
|
18
|
+
"somekey" => "somevalue",
|
19
|
+
"some_date" => "May 23",
|
20
|
+
"some_project_id" => "2342",
|
21
|
+
"some_priority" => "2",
|
22
|
+
"a_single_label" => "42",
|
23
|
+
"some_labels" => "23,42, 5",
|
24
|
+
}
|
25
|
+
|
26
|
+
@expected_token = "some_token_here"
|
27
|
+
@sent_requests = Array.new
|
28
|
+
stub_request(:post, "https://todoist.com/API/v6/sync").
|
29
|
+
to_return { |request|
|
30
|
+
expect(request.headers["Content-Type"]).to eq("application/x-www-form-urlencoded")
|
31
|
+
|
32
|
+
form_data = URI.decode_www_form(request.body)
|
33
|
+
expect(form_data.assoc("token").last).to eq(@expected_token)
|
34
|
+
|
35
|
+
json_data = ActiveSupport::JSON.decode(form_data.assoc("commands").last)
|
36
|
+
expect(json_data.length).to eq(1)
|
37
|
+
|
38
|
+
@sent_requests << req = json_data[0]
|
39
|
+
|
40
|
+
case json_data[0]["type"]
|
41
|
+
when "item_add"
|
42
|
+
json_response = {
|
43
|
+
"TempIdMapping" => {
|
44
|
+
json_data[0]["temp_id"] => 81662555
|
45
|
+
},
|
46
|
+
"seq_no_global" => 11248873939,
|
47
|
+
"seq_no" => 11248873939,
|
48
|
+
"UserId" => 9933517,
|
49
|
+
"SyncStatus" => {
|
50
|
+
json_data[0]["uuid"] => "oK"
|
51
|
+
},
|
52
|
+
}
|
53
|
+
else
|
54
|
+
raise "Unexpected type: #{json_data[0]["type"]}"
|
55
|
+
end
|
56
|
+
|
57
|
+
{ status: 200, body: ActiveSupport::JSON.encode(json_response), headers: { "Content-type" => "application/json" } }
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#validate_options" do
|
62
|
+
before do
|
63
|
+
expect(@checker).to be_valid
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should reject an empty token" do
|
67
|
+
@checker.options["api_token"] = nil
|
68
|
+
expect(@checker).not_to be_valid
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should also allow a credential" do
|
72
|
+
@checker.user.user_credentials.create :credential_name => "todoist_api_token", :credential_value => "some_credential_here"
|
73
|
+
@checker.options["api_token"] = nil
|
74
|
+
expect(@checker).to be_valid
|
75
|
+
end
|
10
76
|
end
|
11
77
|
|
12
|
-
|
78
|
+
describe "#receive" do
|
79
|
+
describe "with static content configuration" do
|
80
|
+
it "can create a new static item" do
|
81
|
+
@checker.receive([@event])
|
82
|
+
expect(@sent_requests.length).to eq(1)
|
83
|
+
expect(@sent_requests[0]["type"]).to eq("item_add")
|
84
|
+
expect(@sent_requests[0]["args"]["content"]).to eq("foobar")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "passes date_string to the new item" do
|
88
|
+
@checker.options["date_string"] = "today"
|
89
|
+
expect(@checker).to be_valid
|
90
|
+
|
91
|
+
@checker.receive([@event])
|
92
|
+
expect(@sent_requests[0]["args"]["date_string"]).to eq("today")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "passes project_id to the new item" do
|
96
|
+
@checker.options["project_id"] = "23"
|
97
|
+
expect(@checker).to be_valid
|
98
|
+
|
99
|
+
@checker.receive([@event])
|
100
|
+
expect(@sent_requests[0]["args"]["project_id"]).to eq(23)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "passes priority to the new item" do
|
104
|
+
@checker.options["priority"] = "3"
|
105
|
+
expect(@checker).to be_valid
|
106
|
+
|
107
|
+
@checker.receive([@event])
|
108
|
+
expect(@sent_requests[0]["args"]["priority"]).to eq(3)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "passes a single label to the new item" do
|
112
|
+
@checker.options["labels"] = "23"
|
113
|
+
expect(@checker).to be_valid
|
114
|
+
|
115
|
+
@checker.receive([@event])
|
116
|
+
expect(@sent_requests[0]["args"]["labels"]).to eq([23])
|
117
|
+
end
|
118
|
+
|
119
|
+
it "passes multiple labels to the new item" do
|
120
|
+
@checker.options["labels"] = "23, 42"
|
121
|
+
expect(@checker).to be_valid
|
122
|
+
|
123
|
+
@checker.receive([@event])
|
124
|
+
expect(@sent_requests[0]["args"]["labels"]).to eq([23, 42])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "with content interpolation" do
|
129
|
+
it "content can be interpolated" do
|
130
|
+
@checker.options["content"] = "Event Data: {{ somekey }}"
|
131
|
+
expect(@checker).to be_valid
|
132
|
+
|
133
|
+
@checker.receive([@event])
|
134
|
+
expect(@sent_requests[0]["args"]["content"]).to eq("Event Data: somevalue")
|
135
|
+
end
|
136
|
+
|
137
|
+
it "date_string can be interpolated" do
|
138
|
+
@checker.options["date_string"] = "{{ some_date }}"
|
139
|
+
expect(@checker).to be_valid
|
140
|
+
|
141
|
+
@checker.receive([@event])
|
142
|
+
expect(@sent_requests[0]["args"]["date_string"]).to eq("May 23")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "project_id can be interpolated" do
|
146
|
+
@checker.options["project_id"] = "{{ some_project_id }}"
|
147
|
+
expect(@checker).to be_valid
|
148
|
+
|
149
|
+
@checker.receive([@event])
|
150
|
+
expect(@sent_requests[0]["args"]["project_id"]).to eq(2342)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "priority can be interpolated" do
|
154
|
+
@checker.options["priority"] = "{{ some_priority }}"
|
155
|
+
expect(@checker).to be_valid
|
156
|
+
|
157
|
+
@checker.receive([@event])
|
158
|
+
expect(@sent_requests[0]["args"]["priority"]).to eq(2)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "single label can be interpolated" do
|
162
|
+
@checker.options["labels"] = "{{ a_single_label }}"
|
163
|
+
expect(@checker).to be_valid
|
164
|
+
|
165
|
+
@checker.receive([@event])
|
166
|
+
expect(@sent_requests[0]["args"]["labels"]).to eq([42])
|
167
|
+
end
|
168
|
+
|
169
|
+
it "multiple labels can be interpolated" do
|
170
|
+
@checker.options["labels"] = "{{ some_labels }}"
|
171
|
+
expect(@checker).to be_valid
|
172
|
+
|
173
|
+
@checker.receive([@event])
|
174
|
+
expect(@sent_requests[0]["args"]["labels"]).to eq([23, 42, 5])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it "creates two items for two events" do
|
179
|
+
@checker.receive([@event, @event])
|
180
|
+
expect(@sent_requests.length).to eq(2)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should use the credential token if no token is present" do
|
184
|
+
@checker.user.user_credentials.create :credential_name => "todoist_api_token", :credential_value => "some_credential_here"
|
185
|
+
@checker.options["api_token"] = nil
|
186
|
+
|
187
|
+
@expected_token = "some_credential_here"
|
188
|
+
@checker.receive([@event])
|
189
|
+
|
190
|
+
expect(@sent_requests.length).to eq(1)
|
191
|
+
expect(@sent_requests[0]["type"]).to eq("item_add")
|
192
|
+
expect(@sent_requests[0]["args"]["content"]).to eq("foobar")
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should use the credential token if an empty token is given" do
|
196
|
+
@checker.user.user_credentials.create :credential_name => "todoist_api_token", :credential_value => "some_credential_here"
|
197
|
+
@checker.options["api_token"] = ""
|
198
|
+
|
199
|
+
@expected_token = "some_credential_here"
|
200
|
+
@checker.receive([@event])
|
201
|
+
|
202
|
+
expect(@sent_requests.length).to eq(1)
|
203
|
+
expect(@sent_requests[0]["type"]).to eq("item_add")
|
204
|
+
expect(@sent_requests[0]["args"]["content"]).to eq("foobar")
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should use the provided token, if both a credential and immediate token are given" do
|
208
|
+
@checker.user.user_credentials.create :credential_name => "todoist_api_token", :credential_value => "some_credential_here"
|
209
|
+
@checker.receive([@event])
|
210
|
+
|
211
|
+
expect(@sent_requests.length).to eq(1)
|
212
|
+
expect(@sent_requests[0]["type"]).to eq("item_add")
|
213
|
+
expect(@sent_requests[0]["args"]["content"]).to eq("foobar")
|
214
|
+
end
|
215
|
+
end
|
13
216
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: huginn_todoist_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Siegl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|