tracker_api 0.1.0

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +57 -0
  8. data/Rakefile +18 -0
  9. data/lib/tracker_api.rb +39 -0
  10. data/lib/tracker_api/client.rb +71 -0
  11. data/lib/tracker_api/endpoints/epic.rb +20 -0
  12. data/lib/tracker_api/endpoints/epics.rb +22 -0
  13. data/lib/tracker_api/endpoints/iterations.rb +22 -0
  14. data/lib/tracker_api/endpoints/project.rb +21 -0
  15. data/lib/tracker_api/endpoints/projects.rb +22 -0
  16. data/lib/tracker_api/endpoints/stories.rb +22 -0
  17. data/lib/tracker_api/endpoints/story.rb +20 -0
  18. data/lib/tracker_api/error.rb +18 -0
  19. data/lib/tracker_api/logger.rb +31 -0
  20. data/lib/tracker_api/resources/account.rb +18 -0
  21. data/lib/tracker_api/resources/epic.rb +19 -0
  22. data/lib/tracker_api/resources/iteration.rb +23 -0
  23. data/lib/tracker_api/resources/label.rb +14 -0
  24. data/lib/tracker_api/resources/project.rb +85 -0
  25. data/lib/tracker_api/resources/story.rb +33 -0
  26. data/lib/tracker_api/resources/time_zone.rb +13 -0
  27. data/lib/tracker_api/version.rb +3 -0
  28. data/test/client_test.rb +61 -0
  29. data/test/minitest_helper.rb +30 -0
  30. data/test/project_test.rb +81 -0
  31. data/test/vcr/cassettes/get_all_projects.json +1 -0
  32. data/test/vcr/cassettes/get_current_iteration.json +1 -0
  33. data/test/vcr/cassettes/get_done_iterations.json +1 -0
  34. data/test/vcr/cassettes/get_epics.json +1 -0
  35. data/test/vcr/cassettes/get_project.json +1 -0
  36. data/test/vcr/cassettes/get_project_with_epics.json +1 -0
  37. data/test/vcr/cassettes/get_unscheduled_stories.json +1 -0
  38. data/tracker_api.gemspec +36 -0
  39. metadata +273 -0
@@ -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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -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
@@ -0,0 +1,3 @@
1
+ 0.1.0
2
+ ---
3
+ - Initial release.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tracker_api.gemspec
4
+ gemspec
5
+
6
+ gem 'coveralls', group: :test, require: false
7
+
@@ -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.
@@ -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
@@ -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
@@ -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