tracker_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +57 -0
- data/Rakefile +18 -0
- data/lib/tracker_api.rb +39 -0
- data/lib/tracker_api/client.rb +71 -0
- data/lib/tracker_api/endpoints/epic.rb +20 -0
- data/lib/tracker_api/endpoints/epics.rb +22 -0
- data/lib/tracker_api/endpoints/iterations.rb +22 -0
- data/lib/tracker_api/endpoints/project.rb +21 -0
- data/lib/tracker_api/endpoints/projects.rb +22 -0
- data/lib/tracker_api/endpoints/stories.rb +22 -0
- data/lib/tracker_api/endpoints/story.rb +20 -0
- data/lib/tracker_api/error.rb +18 -0
- data/lib/tracker_api/logger.rb +31 -0
- data/lib/tracker_api/resources/account.rb +18 -0
- data/lib/tracker_api/resources/epic.rb +19 -0
- data/lib/tracker_api/resources/iteration.rb +23 -0
- data/lib/tracker_api/resources/label.rb +14 -0
- data/lib/tracker_api/resources/project.rb +85 -0
- data/lib/tracker_api/resources/story.rb +33 -0
- data/lib/tracker_api/resources/time_zone.rb +13 -0
- data/lib/tracker_api/version.rb +3 -0
- data/test/client_test.rb +61 -0
- data/test/minitest_helper.rb +30 -0
- data/test/project_test.rb +81 -0
- data/test/vcr/cassettes/get_all_projects.json +1 -0
- data/test/vcr/cassettes/get_current_iteration.json +1 -0
- data/test/vcr/cassettes/get_done_iterations.json +1 -0
- data/test/vcr/cassettes/get_epics.json +1 -0
- data/test/vcr/cassettes/get_project.json +1 -0
- data/test/vcr/cassettes/get_project_with_epics.json +1 -0
- data/test/vcr/cassettes/get_unscheduled_stories.json +1 -0
- data/tracker_api.gemspec +36 -0
- metadata +273 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8407c876c55351601c4fd2648ced15f1af1336b3
|
4
|
+
data.tar.gz: 983fcb13633fd259089a96eab64accc1ea49a777
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5cb62b0d1e08ea319e1b88281216a5874e13c8d431f391b43002d46571db29d210cd8294ed2cea1ff58608814145a4092ded83d527d84584a6fc0736b301f6c
|
7
|
+
data.tar.gz: 777c67421852258ef4a05e0165f4aa6de55c9ea207b03966fb383ba6f520c34cec821107f0dd2e9f14a3f5553fed7149cd57959940ea2a970c06f2fd9cefda9d
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- "1.9.3"
|
4
|
+
- "2.0.0"
|
5
|
+
- "2.1.0"
|
6
|
+
# Faraday::ConnectionFailed: Could not generate DH keypair (Java::JavaLang::RuntimeException)
|
7
|
+
# http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7044060
|
8
|
+
# - jruby-19mode # JRuby in 1.9 mode
|
9
|
+
- rbx
|
10
|
+
# - "1.8.7"
|
11
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
12
|
+
# script: bundle exec rspec spec
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Dash of Code
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# TrackerApi
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/tracker_api.png)](http://badge.fury.io/rb/tracker_api)
|
4
|
+
[![Build Status](https://travis-ci.org/dashofcode/tracker_api.png?branch=master)](https://travis-ci.org/dashofcode/tracker_api)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/dashofcode/tracker_api.png)](https://codeclimate.com/github/dashofcode/tracker_api)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/dashofcode/tracker_api/badge.png?branch=master)](https://coveralls.io/r/dashofcode/tracker_api?branch=master)
|
7
|
+
[![Dependency Status](https://gemnasium.com/dashofcode/tracker_api.png)](https://gemnasium.com/dashofcode/tracker_api)
|
8
|
+
|
9
|
+
This gem allows you to easily use the [Pivotal Tracker v5 API](https://www.pivotaltracker.com/help/api/rest/v5).
|
10
|
+
|
11
|
+
It’s powered by [Faraday](https://github.com/lostisland/faraday) and [Virtus](https://github.com/solnic/virtus).
|
12
|
+
|
13
|
+
|
14
|
+
## Basic Usage
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
client = TrackerApi::Client.new(token: 'my-api-token') # Create API client
|
18
|
+
|
19
|
+
projects = client.projects # Get all projects
|
20
|
+
project = client.project(123456) # Find project with given ID
|
21
|
+
|
22
|
+
project.stories # Get all stories for a project
|
23
|
+
project.stories(with_state: :unscheduled, limit: 10) # Get 10 unscheduled stories for a project
|
24
|
+
project.stories(filter: 'requester:OWK label:"jedi stuff"') # Get all stories that match the given filters
|
25
|
+
project.story(847762630) # Find a story with the given ID
|
26
|
+
|
27
|
+
epics = project.epics # Get all epics for a project
|
28
|
+
epic = epics.first
|
29
|
+
label = epic.label # Get an epic's label
|
30
|
+
```
|
31
|
+
|
32
|
+
## Eager Loading
|
33
|
+
|
34
|
+
See Pivotal Tracker API [documentation](https://www.pivotaltracker.com/help/api#Response_Controlling_Parameters) for how to use the `fields` parameter.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
client = TrackerApi::Client.new(token: 'my-api-token') # Create API client
|
38
|
+
|
39
|
+
client.project(project_id, fields: ':default,labels(name)') # Eagerly get labels with a project
|
40
|
+
client.project(project_id, fields: ':default,epics') # Eagerly get epics with a project
|
41
|
+
```
|
42
|
+
|
43
|
+
## TODO
|
44
|
+
|
45
|
+
- Pagination
|
46
|
+
- Create, Update, Delete of Resources
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
Currently this client supports read-only access to Pivotal Tracker.
|
51
|
+
We will be extending it as our use cases require, but are always happy to accept contributions.
|
52
|
+
|
53
|
+
1. Fork it
|
54
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
55
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
56
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
57
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'bundler/gem_tasks'
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.test_files = FileList['test/**/*_test.rb']
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :test
|
data/lib/tracker_api.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'tracker_api/version'
|
2
|
+
|
3
|
+
# dependencies
|
4
|
+
require 'virtus'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
|
8
|
+
# stdlib
|
9
|
+
require 'forwardable'
|
10
|
+
require 'logger'
|
11
|
+
|
12
|
+
module TrackerApi
|
13
|
+
autoload :Error, 'tracker_api/error'
|
14
|
+
autoload :Client, 'tracker_api/client'
|
15
|
+
autoload :Logger, 'tracker_api/logger'
|
16
|
+
|
17
|
+
module Errors
|
18
|
+
class UnexpectedData < StandardError; end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Endpoints
|
22
|
+
autoload :Epic, 'tracker_api/endpoints/epic'
|
23
|
+
autoload :Epics, 'tracker_api/endpoints/epics'
|
24
|
+
autoload :Iterations, 'tracker_api/endpoints/iterations'
|
25
|
+
autoload :Project, 'tracker_api/endpoints/project'
|
26
|
+
autoload :Projects, 'tracker_api/endpoints/projects'
|
27
|
+
autoload :Stories, 'tracker_api/endpoints/stories'
|
28
|
+
end
|
29
|
+
|
30
|
+
module Resources
|
31
|
+
autoload :Account, 'tracker_api/resources/account'
|
32
|
+
autoload :Epic, 'tracker_api/resources/epic'
|
33
|
+
autoload :Iteration, 'tracker_api/resources/iteration'
|
34
|
+
autoload :Label, 'tracker_api/resources/label'
|
35
|
+
autoload :Project, 'tracker_api/resources/project'
|
36
|
+
autoload :Story, 'tracker_api/resources/story'
|
37
|
+
autoload :TimeZone, 'tracker_api/resources/time_zone'
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
class Client
|
3
|
+
USER_AGENT = "Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}) TrackerApi/#{TrackerApi::VERSION} Faraday/#{Faraday::VERSION}".freeze
|
4
|
+
|
5
|
+
attr_accessor :url, :api_version, :token, :logger, :connection
|
6
|
+
|
7
|
+
# Create Pivotal Tracker API client.
|
8
|
+
#
|
9
|
+
# @param [Hash] options the connection options
|
10
|
+
# @option options [String] :token API token to use for requests
|
11
|
+
# @option options [String] :url Main HTTP API root
|
12
|
+
# @option options [String] :api_version The API version URL path
|
13
|
+
# @option options [String] :logger Custom logger
|
14
|
+
# @option options [String] :adapter Custom http adapter to configure Faraday with
|
15
|
+
# @option options [String] :connection_options Connection options to pass to Faraday
|
16
|
+
#
|
17
|
+
# @example Creating a Client
|
18
|
+
# Client.new token: 'my-super-special-token'
|
19
|
+
def initialize(options={})
|
20
|
+
url = options[:url] || 'https://www.pivotaltracker.com'
|
21
|
+
@url = URI.parse(url).to_s
|
22
|
+
|
23
|
+
@api_version = options[:api_version] || '/services/v5'
|
24
|
+
@logger = options[:logger] || Logger.new(nil)
|
25
|
+
adapter = options[:adapter] || :net_http
|
26
|
+
connection_options = options[:connection_options] || { ssl: { verify: true } }
|
27
|
+
@token = options[:token]
|
28
|
+
|
29
|
+
raise 'Missing required options: :token' unless @token
|
30
|
+
|
31
|
+
@connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
|
32
|
+
# response
|
33
|
+
builder.use Faraday::Response::RaiseError
|
34
|
+
builder.response :json
|
35
|
+
|
36
|
+
# request
|
37
|
+
builder.request :multipart
|
38
|
+
builder.request :json
|
39
|
+
|
40
|
+
builder.use TrackerApi::Logger, @logger
|
41
|
+
builder.adapter adapter
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def request(options={})
|
46
|
+
method = options[:method] || :get
|
47
|
+
url = options[:url] || File.join(@url, @api_version, options[:path])
|
48
|
+
token = options[:token] || @token
|
49
|
+
params = options[:params] || {}
|
50
|
+
body = options[:body]
|
51
|
+
headers = { 'User-Agent' => USER_AGENT, 'X-TrackerToken' => token }.merge(options[:headers] || {})
|
52
|
+
|
53
|
+
connection.send(method) do |req|
|
54
|
+
req.url url
|
55
|
+
req.headers.merge!(headers)
|
56
|
+
req.params.merge!(params)
|
57
|
+
req.body = body
|
58
|
+
end
|
59
|
+
rescue Faraday::Error::ClientError => e
|
60
|
+
raise TrackerApi::Error.new(e)
|
61
|
+
end
|
62
|
+
|
63
|
+
def projects(params={})
|
64
|
+
Endpoints::Projects.new(self).get(params)
|
65
|
+
end
|
66
|
+
|
67
|
+
def project(id, params={})
|
68
|
+
Endpoints::Project.new(self).get(id, params)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Epic
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(project_id, id)
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
:path => "/projects/#{project_id}/epics/#{id}"
|
14
|
+
).body
|
15
|
+
|
16
|
+
Resources::Epic.new({ client: client }.merge(data))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Epics
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(project_id, params={})
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
path: "/projects/#{project_id}/epics",
|
14
|
+
params: params
|
15
|
+
).body
|
16
|
+
raise TrackerApi::Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
|
17
|
+
|
18
|
+
data.map { |epic| Resources::Epic.new({ client: client }.merge(epic)) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Iterations
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(project_id, params={})
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
path: "/projects/#{project_id}/iterations",
|
14
|
+
params: params
|
15
|
+
).body
|
16
|
+
raise TrackerApi::Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
|
17
|
+
|
18
|
+
data.map { |iteration| Resources::Iteration.new({ client: client }.merge(iteration)) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Project
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(id, params={})
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
path: "/projects/#{id}",
|
14
|
+
params: params
|
15
|
+
).body
|
16
|
+
|
17
|
+
Resources::Project.new({ client: client }.merge(data))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Projects
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(params={})
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
path: '/projects',
|
14
|
+
params: params
|
15
|
+
).body
|
16
|
+
raise TrackerApi::Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
|
17
|
+
|
18
|
+
data.map { |project| Resources::Project.new({ client: client }.merge(project)) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Stories
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(project_id, params={})
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
path: "/projects/#{project_id}/stories",
|
14
|
+
params: params
|
15
|
+
).body
|
16
|
+
raise TrackerApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
|
17
|
+
|
18
|
+
data.map { |story| Resources::Story.new({ client: client }.merge(story)) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
module Endpoints
|
3
|
+
class Story
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(project_id, id)
|
11
|
+
data = client.request(
|
12
|
+
method: :get,
|
13
|
+
:path => "/projects/#{project_id}/stories/#{id}"
|
14
|
+
).body
|
15
|
+
|
16
|
+
Resources::Story.new({ client: client }.merge(data))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TrackerApi
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :wrapped_exception, :response
|
4
|
+
|
5
|
+
def initialize(wrapped_exception)
|
6
|
+
@wrapped_exception = wrapped_exception
|
7
|
+
@response = wrapped_exception.response
|
8
|
+
message = if wrapped_exception.is_a?(Faraday::Error::ParsingError)
|
9
|
+
wrapped_exception.message
|
10
|
+
elsif wrapped_exception.is_a?(Faraday::Error::ClientError)
|
11
|
+
wrapped_exception.response.inspect
|
12
|
+
else
|
13
|
+
wrapped_exception.instance_variable_get(:@wrapped_exception).inspect
|
14
|
+
end
|
15
|
+
super(message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|