ruby-todoist-api 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.kick +18 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +2 -0
- data/lib/todoist.rb +27 -0
- data/lib/todoist/client.rb +94 -0
- data/lib/todoist/command.rb +24 -0
- data/lib/todoist/error.rb +0 -0
- data/lib/todoist/filter.rb +16 -0
- data/lib/todoist/item.rb +54 -0
- data/lib/todoist/label.rb +15 -0
- data/lib/todoist/note.rb +19 -0
- data/lib/todoist/project.rb +32 -0
- data/lib/todoist/query.rb +47 -0
- data/lib/todoist/queue.rb +39 -0
- data/lib/todoist/reminder.rb +16 -0
- data/lib/todoist/request.rb +37 -0
- data/lib/todoist/resource.rb +73 -0
- data/lib/todoist/service/base_service.rb +77 -0
- data/lib/todoist/service/filter.rb +9 -0
- data/lib/todoist/service/item.rb +24 -0
- data/lib/todoist/service/label.rb +9 -0
- data/lib/todoist/service/note.rb +9 -0
- data/lib/todoist/service/project.rb +9 -0
- data/lib/todoist/service/reminder.rb +9 -0
- data/lib/todoist/user.rb +10 -0
- data/lib/todoist/version.rb +3 -0
- data/ruby-todoist-api.gemspec +22 -0
- data/spec/lib/todoist/client_spec.rb +58 -0
- data/spec/lib/todoist/filter_spec.rb +19 -0
- data/spec/lib/todoist/item_spec.rb +39 -0
- data/spec/lib/todoist/label_spec.rb +12 -0
- data/spec/lib/todoist/note_spec.rb +12 -0
- data/spec/lib/todoist/project_spec.rb +12 -0
- data/spec/lib/todoist/query_spec.rb +26 -0
- data/spec/lib/todoist/reminder_spec.rb +12 -0
- data/spec/lib/todoist/service/filter_spec.rb +33 -0
- data/spec/lib/todoist/service/item_spec.rb +46 -0
- data/spec/lib/todoist/service/label_spec.rb +33 -0
- data/spec/lib/todoist/service/note_spec.rb +33 -0
- data/spec/lib/todoist/service/project_spec.rb +33 -0
- data/spec/lib/todoist/service/reminder_spec.rb +33 -0
- data/spec/lib/todoist_spec.rb +7 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/json_response_helper.rb +17 -0
- data/spec/support/responses/filter.json +9 -0
- data/spec/support/responses/item.json +25 -0
- data/spec/support/responses/label.json +8 -0
- data/spec/support/responses/note.json +18 -0
- data/spec/support/responses/project.json +14 -0
- data/spec/support/responses/query.json +88 -0
- data/spec/support/responses/reminder.json +9 -0
- metadata +153 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Todoist
|
2
|
+
class Queue
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :@queue, :clear, :first, :last, :shift, :size, :length
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
def queue
|
12
|
+
@queue ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def path
|
16
|
+
"/API/v6/sync"
|
17
|
+
end
|
18
|
+
|
19
|
+
def length
|
20
|
+
queue.length
|
21
|
+
end
|
22
|
+
|
23
|
+
def process!
|
24
|
+
return if queue == []
|
25
|
+
commands = queue
|
26
|
+
clear_queue!
|
27
|
+
@client.post(path, { commands: commands.map(&:to_hash).to_json})
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_queue!
|
31
|
+
@queue = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def add(command)
|
35
|
+
@queue ||= []
|
36
|
+
@queue << command
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Todoist
|
4
|
+
class Request
|
5
|
+
SUCCES_STATUS_CODE = 200
|
6
|
+
|
7
|
+
attr_accessor :path, :net_http_method
|
8
|
+
|
9
|
+
def initialize(path, net_http_method)
|
10
|
+
self.path = path
|
11
|
+
self.net_http_method = net_http_method
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.post(path, form_data, request_class = Net::HTTP::Post)
|
15
|
+
request = request_class.new(path)
|
16
|
+
request.set_form_data(form_data)
|
17
|
+
|
18
|
+
new(path, request)
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(base_url)
|
22
|
+
uri = URI.parse("#{base_url}#{path}")
|
23
|
+
|
24
|
+
response = http(uri).request(net_http_method)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def http(uri)
|
29
|
+
net = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
net.use_ssl = true
|
31
|
+
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
net.read_timeout = 90
|
33
|
+
net.open_timeout = 30
|
34
|
+
net
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Todoist
|
3
|
+
module Resource
|
4
|
+
module ClassMethods
|
5
|
+
attr_reader :attributes, :read_only_attributes
|
6
|
+
|
7
|
+
def define_attributes(attributes)
|
8
|
+
attr_accessor(*@attributes = attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def define_read_only_attributes(attributes)
|
12
|
+
@read_only_attributes = attributes
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :temp_id
|
17
|
+
|
18
|
+
def initialize(attributes={})
|
19
|
+
self.attributes = attributes || {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def attributes
|
23
|
+
self.class.attributes.inject({}) do |attributes, attribute|
|
24
|
+
value = send(attribute)
|
25
|
+
if !value.nil?
|
26
|
+
attributes[attribute] = value
|
27
|
+
end; attributes
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes=(attributes)
|
32
|
+
attributes.each do |attribute, value|
|
33
|
+
if respond_to?(writer = attribute.to_s + '=')
|
34
|
+
send(writer, value)
|
35
|
+
else
|
36
|
+
logger.warn "#{self.class} does not have an `#{attribute}' attribute"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
def logger
|
46
|
+
@logger ||= Logger.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_command(name, arguments, tmp_id = nil)
|
50
|
+
command_class.new(name, arguments, tmp_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def command_class
|
54
|
+
@command_class ||= Command
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_submittable_hash
|
58
|
+
if self.class.read_only_attributes
|
59
|
+
attributes.delete_if { |key, value| self.class.read_only_attributes.include?(key)}
|
60
|
+
else
|
61
|
+
attributes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def resource_type
|
66
|
+
self.class.name.split('::').last.downcase
|
67
|
+
end
|
68
|
+
|
69
|
+
def persisted?
|
70
|
+
!id.nil?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Todoist
|
2
|
+
module Service
|
3
|
+
class BaseService
|
4
|
+
attr_writer :command_class
|
5
|
+
attr_reader :client
|
6
|
+
attr_accessor :data
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
@client.queue.process
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_command(name, arguments, tmp_id = nil)
|
17
|
+
command_class.new(name, arguments, tmp_id)
|
18
|
+
end
|
19
|
+
|
20
|
+
def command_class
|
21
|
+
@command_class ||= Command
|
22
|
+
end
|
23
|
+
|
24
|
+
def resource_type
|
25
|
+
self.class.name.split('::').last.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def resource_type_plural
|
29
|
+
"#{resource_type}s"
|
30
|
+
end
|
31
|
+
|
32
|
+
def path
|
33
|
+
'/API/v6/sync'
|
34
|
+
end
|
35
|
+
|
36
|
+
def seq_no
|
37
|
+
data.nil? ? (@seq_no || 0) : client.seq_no
|
38
|
+
end
|
39
|
+
|
40
|
+
def seq_no_global
|
41
|
+
data.nil? ? 0 : client.seq_no_global
|
42
|
+
end
|
43
|
+
|
44
|
+
def retrieve(types = ["all"])
|
45
|
+
response = client.post(path, { seq_no: seq_no, seq_no_global: seq_no_global, resource_types: types.to_json })
|
46
|
+
@seq_no = response['seq_no'] if response['seq_no']
|
47
|
+
response
|
48
|
+
end
|
49
|
+
|
50
|
+
def all
|
51
|
+
response = retrieve([resource_type_plural])
|
52
|
+
self.data = response[resource_type_plural.capitalize].map { |resource| collection_class.new(resource) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def create(params, temp_id = nil)
|
56
|
+
instance = build(params, temp_id)
|
57
|
+
save(instance)
|
58
|
+
end
|
59
|
+
|
60
|
+
def build(params, temp_id = nil)
|
61
|
+
instance = collection_class.new(params)
|
62
|
+
instance.temp_id ||= temp_id
|
63
|
+
instance
|
64
|
+
end
|
65
|
+
|
66
|
+
def save(instance)
|
67
|
+
if instance.persisted?
|
68
|
+
@client.queue.add(create_command("#{resource_type}_update", instance))
|
69
|
+
else
|
70
|
+
instance.temp_id ||= SecureRandom.uuid
|
71
|
+
@client.queue.add(create_command("#{resource_type}_add", instance))
|
72
|
+
end
|
73
|
+
instance
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Todoist
|
2
|
+
module Service
|
3
|
+
class Item < BaseService
|
4
|
+
def update(id, arguments)
|
5
|
+
@client.add_to_queue(create_command('item_update', arguments.merge({id: id})))
|
6
|
+
end
|
7
|
+
|
8
|
+
def delete(ids)
|
9
|
+
if ids.is_a?(FixNum)
|
10
|
+
ids = [ids]
|
11
|
+
end
|
12
|
+
@client.add_to_queue(create_command('item_delete', {ids: ids}))
|
13
|
+
end
|
14
|
+
|
15
|
+
def close(id)
|
16
|
+
@client.add_to_queue(create_command('item_close', {id: id}))
|
17
|
+
end
|
18
|
+
|
19
|
+
def collection_class
|
20
|
+
Todoist::Item
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/todoist/user.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'todoist/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ruby-todoist-api"
|
8
|
+
spec.version = Todoist::VERSION
|
9
|
+
spec.authors = ["Maarten van Vliet"]
|
10
|
+
spec.email = ["maartenvanvliet@gmail.com"]
|
11
|
+
spec.summary = %q{Wrapper for todoist api.}
|
12
|
+
spec.description = %q{Wrapper for todoist api v6.}
|
13
|
+
spec.homepage = "https://github.com/maartenvanvliet/ruby-todoist-api"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.test_files = spec.files.grep(%r{^(spec)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "rspec", "~> 3"
|
21
|
+
spec.add_development_dependency "webmock", "~> 1.21"
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Todoist::Client do
|
4
|
+
|
5
|
+
let(:client) { described_class.new("api_token") }
|
6
|
+
|
7
|
+
it "sets a api token when given one string argument" do
|
8
|
+
expect(client.token).to eq 'api_token'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "has a baseurl" do
|
12
|
+
expect(client.base_url).to_not be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "request handling" do
|
16
|
+
let!(:stub) {
|
17
|
+
stub_request(:post, "https://todoist.com/path").
|
18
|
+
with(:body => {"payload"=>"payload", "token"=>"api_token"}).
|
19
|
+
to_return(:status => 200, :body => '{}')
|
20
|
+
|
21
|
+
}
|
22
|
+
|
23
|
+
it "can post a request" do
|
24
|
+
client.post('/path', { payload: 'payload'})
|
25
|
+
|
26
|
+
expect(stub).to have_been_requested
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "queue handling" do
|
32
|
+
let(:command) { { 'type' => 'test_command'} }
|
33
|
+
before do
|
34
|
+
client.queue.add(command)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can add a command to the queue" do
|
38
|
+
|
39
|
+
expect(client.queue.first).to eq(command)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "sends the queue in a request" do
|
43
|
+
let!(:stub) {
|
44
|
+
stub_request(:post, "https://todoist.com/API/v6/sync").
|
45
|
+
with(:body => {"commands"=>"[{\"type\":\"test_command\"}]", "token"=>"api_token"}).
|
46
|
+
to_return(:status => 200, :body => '{}')
|
47
|
+
|
48
|
+
}
|
49
|
+
|
50
|
+
it "can post a request" do
|
51
|
+
client.process!
|
52
|
+
|
53
|
+
expect(client.queue.length).to eq 0
|
54
|
+
expect(stub).to have_been_requested
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Todoist::Filter do
|
4
|
+
|
5
|
+
it "can be built from json" do
|
6
|
+
instance = described_class.new(json_response('filter'))
|
7
|
+
|
8
|
+
expect(instance.attributes).to eq({
|
9
|
+
"id"=>4638878,
|
10
|
+
"user_id"=>1855589,
|
11
|
+
"name"=>"Priority 1",
|
12
|
+
"query"=>"priority 1",
|
13
|
+
"color"=>6,
|
14
|
+
"item_order"=>3,
|
15
|
+
"is_deleted"=>0
|
16
|
+
}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|