huginn_todoist_agent 0.2 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|