pivotal-api 0.3.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8eb153b9cf748c9d9d0f2ecbdeec3ef341963f1
4
+ data.tar.gz: 02383e644883c0bf292e1cfb5e58bfc223c2c58b
5
+ SHA512:
6
+ metadata.gz: 32dd22449130691138f408a00626ef3a0826903b23f2a5017583619a4f8d7f06adc831f3d30abd2db08e41b2d0fb553ae4d389bd720bd49e42ee3aed1b0fce9d
7
+ data.tar.gz: 03716a31c4bf552241b0c3940db729483843662efb31d33461a4d57d52b4dbef6f8b3e6501ec600c4046c2b6f064be5f7ef12be95b532992305c9a9e769d76f7
@@ -0,0 +1,51 @@
1
+ require_relative './pivotal_api/version.rb'
2
+
3
+ # dependencies
4
+ require 'virtus'
5
+ require 'faraday'
6
+ require 'faraday_middleware'
7
+
8
+ # stdlib
9
+ require 'addressable/uri'
10
+ require 'forwardable'
11
+ require 'logger'
12
+
13
+ module PivotalApi
14
+ autoload :Error, 'pivotal_api/error'
15
+ autoload :Client, 'pivotal_api/client'
16
+ autoload :Logger, 'pivotal_api/logger'
17
+
18
+ module Errors
19
+ class UnexpectedData < StandardError; end
20
+ end
21
+
22
+ module Endpoints
23
+ autoload :Comments, 'pivotal_api/endpoints/comments'
24
+ autoload :Epics, 'pivotal_api/endpoints/epics'
25
+ autoload :Iterations, 'pivotal_api/endpoints/iterations'
26
+ autoload :Labels, 'pivotal_api/endpoints/labels'
27
+ autoload :Me, 'pivotal_api/endpoints/me'
28
+ autoload :Memberships, 'pivotal_api/endpoints/memberships'
29
+ autoload :Projects, 'pivotal_api/endpoints/projects'
30
+ autoload :Stories, 'pivotal_api/endpoints/stories'
31
+ autoload :Tasks, 'pivotal_api/endpoints/tasks'
32
+ autoload :Webhooks, 'pivotal_api/endpoints/webhooks'
33
+ end
34
+
35
+ module Resources
36
+ autoload :Account, 'pivotal_api/resources/account'
37
+ autoload :Comment, 'pivotal_api/resources/comment'
38
+ autoload :Epic, 'pivotal_api/resources/epic'
39
+ autoload :Iteration, 'pivotal_api/resources/iteration'
40
+ autoload :Me, 'pivotal_api/resources/me'
41
+ autoload :MembershipSummary, 'pivotal_api/resources/membership_summary'
42
+ autoload :Label, 'pivotal_api/resources/label'
43
+ autoload :Person, 'pivotal_api/resources/person'
44
+ autoload :Project, 'pivotal_api/resources/project'
45
+ autoload :ProjectMembership, 'pivotal_api/resources/project_membership'
46
+ autoload :Story, 'pivotal_api/resources/story'
47
+ autoload :Task, 'pivotal_api/resources/task'
48
+ autoload :TimeZone, 'pivotal_api/resources/time_zone'
49
+ autoload :Webhook, 'pivotal_api/resources/webhook'
50
+ end
51
+ end
@@ -0,0 +1,218 @@
1
+ module PivotalApi
2
+ class Client
3
+ USER_AGENT = "Ruby/#{RUBY_VERSION} (#{RUBY_PLATFORM}; #{RUBY_ENGINE}) PivotalApi/#{PivotalApi::VERSION} Faraday/#{Faraday::VERSION}".freeze
4
+
5
+ # Header keys that can be passed in options hash to {#get},{#paginate}
6
+ CONVENIENCE_HEADERS = Set.new([:accept, :content_type])
7
+
8
+ attr_reader :url, :api_version, :token, :logger, :connection, :auto_paginate, :last_response
9
+
10
+ # Create Pivotal Tracker API client.
11
+ #
12
+ # @param [Hash] options the connection options
13
+ # @option options [String] :token API token to use for requests
14
+ # @option options [String] :url Main HTTP API root
15
+ # @option options [Boolean] :auto_paginate Client should perform pagination automatically. Default true.
16
+ # @option options [String] :api_version The API version URL path
17
+ # @option options [String] :logger Custom logger
18
+ # @option options [String] :adapter Custom http adapter to configure Faraday with
19
+ # @option options [String] :connection_options Connection options to pass to Faraday
20
+ #
21
+ # @example Creating a Client
22
+ # Client.new token: 'my-super-special-token'
23
+ def initialize(options={})
24
+ url = options.fetch(:url, 'https://www.pivotaltracker.com')
25
+ @url = Addressable::URI.parse(url).to_s
26
+ @api_version = options.fetch(:api_version, '/services/v5')
27
+ @logger = options.fetch(:logger, ::Logger.new(nil))
28
+ adapter = options.fetch(:adapter, :excon)
29
+ connection_options = options.fetch(:connection_options, { ssl: { verify: true } })
30
+ @auto_paginate = options.fetch(:auto_paginate, true)
31
+ @token = options[:token]
32
+ raise 'Missing required options: :token' unless @token
33
+
34
+ @connection = Faraday.new({ url: @url }.merge(connection_options)) do |builder|
35
+ # response
36
+ builder.use Faraday::Response::RaiseError
37
+ builder.response :json
38
+
39
+ # request
40
+ builder.request :multipart
41
+ builder.request :json
42
+
43
+ builder.use PivotalApi::Logger, @logger
44
+ builder.adapter adapter
45
+ end
46
+ end
47
+
48
+ # Make a HTTP GET request
49
+ #
50
+ # @param path [String] The path, relative to api endpoint
51
+ # @param options [Hash] Query and header params for request
52
+ # @return [Faraday::Response]
53
+ def get(path, options = {})
54
+ request(:get, parse_query_and_convenience_headers(path, options))
55
+ end
56
+
57
+ # Make a HTTP DELETE request
58
+ #
59
+ # @param path [String] The path, relative to api endpoint
60
+ # @param options [Hash] Query and header params for request
61
+ # @return [Faraday::Response]
62
+ def delete(path, options = {})
63
+ request(:delete, parse_query_and_convenience_headers(path, options))
64
+ end
65
+
66
+ # Make a HTTP POST request
67
+ #
68
+ # @param path [String] The path, relative to api endpoint
69
+ # @param options [Hash] Query and header params for request
70
+ # @return nil
71
+ def post(path, options = {})
72
+ request(:post, parse_query_and_convenience_headers(path, options))
73
+ end
74
+
75
+ # Make a HTTP PUT request
76
+ #
77
+ # @param path [String] The path, relative to api endpoint
78
+ # @param options [Hash] Query and header params for request
79
+ # @return nil
80
+ def put(path, options = {})
81
+ request(:put, parse_query_and_convenience_headers(path, options))
82
+ end
83
+
84
+ # Make one or more HTTP GET requests, optionally fetching
85
+ # the next page of results from information passed back in headers
86
+ # based on value in {#auto_paginate}.
87
+ #
88
+ # @param path [String] The path, relative to {#api_endpoint}
89
+ # @param options [Hash] Query and header params for request
90
+ # @param block [Block] Block to perform the data concatenation of the
91
+ # multiple requests. The block is called with two parameters, the first
92
+ # contains the contents of the requests so far and the second parameter
93
+ # contains the latest response.
94
+ # @return [Array]
95
+ def paginate(path, options = {}, &block)
96
+ opts = parse_query_and_convenience_headers path, options.dup
97
+ auto_paginate = opts[:params].delete(:auto_paginate) { |k| @auto_paginate }
98
+ @last_response = request :get, opts
99
+ data = @last_response.body
100
+ raise PivotalApi::Errors::UnexpectedData, 'Array expected' unless data.is_a? Array
101
+
102
+ if auto_paginate
103
+ pager = Pagination.new @last_response.headers
104
+
105
+ while pager.more?
106
+ opts[:params].update(pager.next_page_params)
107
+
108
+ @last_response = request :get, opts
109
+ pager = Pagination.new @last_response.headers
110
+ if block_given?
111
+ yield(data, @last_response)
112
+ else
113
+ data.concat(@last_response.body) if @last_response.body.is_a?(Array)
114
+ end
115
+ end
116
+ end
117
+
118
+ data
119
+ end
120
+
121
+ # Get projects
122
+ #
123
+ # @return [PivotalApi::Endpoints::Projects]
124
+ def projects
125
+ Endpoints::Projects.new(self)
126
+ end
127
+
128
+ # Get information about the authenticated user
129
+ #
130
+ # @return [PivotalApi::Resources::Me]
131
+ def me
132
+ data = get("/me").body
133
+
134
+ Resources::Me.new({ client: self }.merge(data))
135
+ end
136
+
137
+ # Get information about a client story without knowing what project the story belongs to
138
+ #
139
+ # @param [String] story_id
140
+ # @return [PivotalApi::Resources::Story]
141
+
142
+
143
+ private
144
+
145
+ def parse_query_and_convenience_headers(path, options)
146
+ raise 'Path can not be blank.' if path.to_s.empty?
147
+
148
+ opts = { body: options[:body] }
149
+
150
+ opts[:url] = options[:url] || File.join(@url, @api_version, path.to_s)
151
+ opts[:method] = options[:method] || :get
152
+ opts[:params] = options[:params] || {}
153
+ opts[:token] = options[:token] || @token
154
+ headers = { 'User-Agent' => USER_AGENT,
155
+ 'X-TrackerToken' => opts.fetch(:token) }.merge(options.fetch(:headers, {}))
156
+
157
+ CONVENIENCE_HEADERS.each do |h|
158
+ if header = options[h]
159
+ headers[h] = header
160
+ end
161
+ end
162
+ opts[:headers] = headers
163
+
164
+ opts
165
+ end
166
+
167
+ def request(method, options = {})
168
+ url = options.fetch(:url)
169
+ params = options[:params] || {}
170
+ body = options[:body]
171
+ headers = options[:headers]
172
+
173
+ @last_response = response = connection.send(method) do |req|
174
+ req.url(url)
175
+ req.headers.merge!(headers)
176
+ req.params.merge!(params)
177
+ req.body = body
178
+ end
179
+ response
180
+ rescue Faraday::Error::ClientError => e
181
+ raise PivotalApi::Error.new(e)
182
+ end
183
+
184
+ class Pagination
185
+ attr_accessor :headers, :total, :limit, :offset, :returned
186
+
187
+ def initialize(headers)
188
+ @headers = headers
189
+ @total = headers['x-tracker-pagination-total'].to_i
190
+ @limit = headers['x-tracker-pagination-limit'].to_i
191
+ @offset = headers['x-tracker-pagination-offset'].to_i
192
+ @returned = headers['x-tracker-pagination-returned'].to_i
193
+
194
+ # if offset is negative (e.g. Iterations Endpoint).
195
+ # For the 'Done' scope, negative numbers can be passed, which
196
+ # specifies the number of iterations preceding the 'Current' iteration.
197
+ # then need to adjust the negative offset to account for a smaller total,
198
+ # and set total to zero since we are paginating from -X to 0.
199
+ if @offset < 0
200
+ @offset = -@total if @offset.abs > @total
201
+ @total = 0
202
+ end
203
+ end
204
+
205
+ def more?
206
+ (offset + limit) < total
207
+ end
208
+
209
+ def next_offset
210
+ offset + limit
211
+ end
212
+
213
+ def next_page_params
214
+ { limit: limit, offset: next_offset }
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,42 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Comments
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/comments", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Comment.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, story_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{story_id}/comments/#{id}").body
21
+
22
+ Resources::Comment.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, story_id, params)
26
+ data = client.post("/projects/#{project_id}/stories/#{story_id}/comments", params: params).body
27
+
28
+ Resources::Comment.new({ project_id: project_id }.merge(data))
29
+ end
30
+
31
+ def update(project_id, story_id, id, params)
32
+ data = client.put("/projects/#{project_id}/stories/#{story_id}/comments/#{id}", params: params).body
33
+
34
+ Resources::Comment.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, story_id, id)
38
+ client.delete("/projects/#{project_id}/stories/#{story_id}/comments/#{id}").body
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Epics
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/epics", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of epics expected' unless data.is_a? Array
13
+
14
+ data.map do |epic|
15
+ Resources::Epic.new({ project_id: project_id }.merge(epic))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/epics/#{id}").body
21
+
22
+ Resources::Epic.new({ project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Iterations
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/iterations", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of iterations expected' unless data.is_a? Array
13
+
14
+ data.map do |iteration|
15
+ Resources::Iteration.new({ project_id: project_id }.merge(iteration))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Labels
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/labels", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Label.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def create(project_id, story_id, params)
20
+ data = client.post("/projects/#{project_id}/stories/#{story_id}/labels", params: params).body
21
+
22
+ Resources::Label.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def update(project_id, story_id, id, params)
26
+ data = client.put("/projects/#{project_id}/stories/#{story_id}/labels/#{id}", params: params).body
27
+
28
+ Resources::Label.new({ }.merge(data))
29
+ end
30
+
31
+ def delete(project_id, story_id, id)
32
+ client.delete("/projects/#{project_id}/stories/#{story_id}/labels/#{id}").body
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Memberships
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/memberships", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of memberships expected' unless data.is_a? Array
13
+
14
+ data.map do |membership|
15
+ Resources::ProjectMembership.new({ project_id: project_id }.merge(membership))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/memberships/#{id}").body
21
+
22
+ Resources::ProjectMembership.new({ project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Projects
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # @param [Hash] params
11
+ # @return [Array[PivotalApi::Resources::Project]]
12
+ def all(params={})
13
+ data = client.paginate('/projects', params: params)
14
+ raise PivotalApi::Errors::UnexpectedData, 'Array of projects expected' unless data.is_a? Array
15
+
16
+ data.map { |project| Resources::Project.new({ }.merge(project)) }
17
+ end
18
+
19
+
20
+ # @param [Hash] params
21
+ # @return [PivotalApi::Resources::Project]
22
+ def get(id, params={})
23
+ # Endpoints::Project.new(self).get(id, params)
24
+ data = client.get("/projects/#{id}", params: params).body
25
+
26
+ Resources::Project.new({ }.merge(data))
27
+ end
28
+
29
+ def epics
30
+ Endpoints::Epics.new(client)
31
+ end
32
+
33
+ def iterations
34
+ Endpoints::Iterations.new(client)
35
+ end
36
+
37
+ def stories
38
+ Endpoints::Stories.new(client)
39
+ end
40
+
41
+ def memberships
42
+ Endpoints::Memberships.new(client)
43
+ end
44
+
45
+ def webhooks
46
+ Endpoints::Webhooks.new(client)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Stories
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of stories expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Story.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{id}").body
21
+
22
+ Resources::Story.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, params)
26
+ data = client.post("/projects/#{project_id}/stories", params: params).body
27
+
28
+ Resources::Story.new({ }.merge(data))
29
+ end
30
+
31
+ def update(project_id, id, params)
32
+ data = client.put("/projects/#{project_id}/stories/#{id}", params: params).body
33
+
34
+ Resources::Story.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, id)
38
+ client.delete("/projects/#{project_id}/stories/#{id}").body
39
+ end
40
+
41
+ def tasks
42
+ Endpoints::Tasks.new(client)
43
+ end
44
+
45
+ def comments
46
+ Endpoints::Comments.new(client)
47
+ end
48
+
49
+ def labels
50
+ Endpoints::Labels.new(client)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Tasks
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, story_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/stories/#{story_id}/tasks", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of tasks expected' unless data.is_a? Array
13
+
14
+ data.map do |task|
15
+ Resources::Task.new({project_id: project_id }.merge(task))
16
+ end
17
+ end
18
+
19
+ def get(project_id, story_id, id)
20
+ data = client.get("/projects/#{project_id}/stories/#{story_id}/tasks/#{id}").body
21
+
22
+ Resources::Task.new({project_id: project_id }.merge(data))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module PivotalApi
2
+ module Endpoints
3
+ class Webhooks
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def all(project_id, params={})
11
+ data = client.paginate("/projects/#{project_id}/webhooks", params: params)
12
+ raise PivotalApi::Errors::UnexpectedData, 'Array of webhooks expected' unless data.is_a? Array
13
+
14
+ data.map do |story|
15
+ Resources::Webhook.new({ project_id: project_id }.merge(story))
16
+ end
17
+ end
18
+
19
+ def get(project_id, id)
20
+ data = client.get("/projects/#{project_id}/webhooks/#{id}").body
21
+
22
+ Resources::Webhook.new({ project_id: project_id }.merge(data))
23
+ end
24
+
25
+ def create(project_id, params)
26
+ data = client.post("/projects/#{project_id}/webhooks", params: params).body
27
+
28
+ Resources::Webhook.new({ }.merge(data))
29
+ end
30
+
31
+ def update(project_id, id, params)
32
+ data = client.put("/projects/#{project_id}/webhooks/#{id}", params: params).body
33
+
34
+ Resources::Webhook.new({ }.merge(data))
35
+ end
36
+
37
+ def delete(project_id, id)
38
+ client.delete("/projects/#{project_id}/webhooks/#{id}").body
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
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
@@ -0,0 +1,31 @@
1
+ module PivotalApi
2
+ class Logger < Faraday::Response::Middleware
3
+ extend Forwardable
4
+
5
+ def initialize(app, logger = nil)
6
+ super(app)
7
+ @logger = logger || ::Logger.new(STDOUT)
8
+ end
9
+
10
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
11
+
12
+ def call(env)
13
+ info("#{env[:method]} => #{env[:url].to_s}")
14
+ debug('request') { dump_headers env[:request_headers] }
15
+ debug('request.body') { env[:body] }
16
+ super
17
+ end
18
+
19
+ def on_complete(env)
20
+ info("#{env[:status]} <= #{env[:url].to_s}")
21
+ debug('response') { dump_headers env[:response_headers] }
22
+ debug('response.body') { env[:body] }
23
+ end
24
+
25
+ private
26
+
27
+ def dump_headers(headers)
28
+ headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Account
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :created_at, DateTime
8
+ attribute :status, String
9
+ attribute :kind, String
10
+ attribute :name, String
11
+ attribute :updated_at, DateTime
12
+ attribute :url, String
13
+ attribute :plan, String
14
+ attribute :days_left, Integer
15
+ attribute :over_the_limit, Boolean
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Comment
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :story_id, Integer
8
+ attribute :epic_id, Integer
9
+ attribute :text, String
10
+ attribute :person_id, Integer
11
+ attribute :created_at, DateTime
12
+ attribute :updated_at, DateTime
13
+ attribute :file_attachment_ids, Array[Integer]
14
+ attribute :google_attachment_ids, Array[Integer]
15
+ attribute :commit_identifier, String
16
+ attribute :commit_type, String
17
+ attribute :kind, String
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Epic
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :created_at, DateTime
10
+ attribute :description, String
11
+ attribute :kind, String
12
+ attribute :label, PivotalApi::Resources::Label
13
+ attribute :name, String
14
+ attribute :project_id, Integer
15
+ attribute :updated_at, DateTime
16
+ attribute :url, String
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Iteration
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :finish, DateTime
9
+ attribute :kind, String
10
+ attribute :length, Integer
11
+ attribute :number, Integer
12
+ attribute :planned, Boolean
13
+ attribute :project_id, Integer
14
+ attribute :start, DateTime
15
+ attribute :stories, [PivotalApi::Resources::Story]
16
+ attribute :story_ids, [Integer]
17
+ attribute :team_strength, Float
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Label
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :created_at, DateTime
8
+ attribute :kind, String
9
+ attribute :name, String
10
+ attribute :project_id, Integer
11
+ attribute :updated_at, DateTime
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Me
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :name, String
10
+ attribute :initials, String
11
+ attribute :username, String
12
+ attribute :time_zone, PivotalApi::Resources::TimeZone
13
+ attribute :api_token, String
14
+ attribute :has_google_identity, Boolean
15
+ attribute :project_ids, Array[Integer]
16
+ attribute :projects, [PivotalApi::Resources::MembershipSummary]
17
+ attribute :workspace_ids, Array[Integer]
18
+ attribute :email, String
19
+ attribute :receives_in_app_notifications, Boolean
20
+ attribute :kind, String
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class MembershipSummary
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :kind, String
10
+ attribute :last_viewed_at, DateTime
11
+ attribute :project_color, String
12
+ attribute :project_id, Integer
13
+ attribute :project_name, String
14
+ attribute :role, String
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Person
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :kind, String
9
+ attribute :id, Integer
10
+ attribute :name, String
11
+ attribute :email, String
12
+ attribute :initials, String
13
+ attribute :username, String
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,49 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Project
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :account, PivotalApi::Resources::Account
9
+ attribute :account_id, Integer
10
+ attribute :atom_enabled, Boolean
11
+ attribute :bugs_and_chores_are_estimatable, Boolean
12
+ attribute :created_at, DateTime
13
+ attribute :current_iteration_number, Integer
14
+ attribute :current_velocity, Integer
15
+ attribute :description, String
16
+ attribute :enable_following, Boolean
17
+ attribute :enable_incoming_emails, Boolean
18
+ attribute :enable_planned_mode, Boolean
19
+ attribute :enable_tasks, Boolean
20
+ attribute :epic_ids, Array[Integer]
21
+ attribute :epics, Array[PivotalApi::Resources::Epic]
22
+ attribute :has_google_domain, Boolean
23
+ attribute :id, Integer
24
+ attribute :initial_velocity, Integer
25
+ attribute :iteration_length, Integer
26
+ attribute :kind, String
27
+ attribute :label_ids, Array[Integer]
28
+ attribute :labels, Array[PivotalApi::Resources::Label]
29
+ attribute :name, String
30
+ attribute :number_of_done_iterations_to_show, Integer
31
+ attribute :point_scale, String
32
+ attribute :point_scale_is_custom, Boolean
33
+ attribute :profile_content, String
34
+ attribute :public, Boolean
35
+ attribute :start_date, DateTime
36
+ attribute :start_time, DateTime
37
+ attribute :time_zone, PivotalApi::Resources::TimeZone
38
+ attribute :updated_at, DateTime
39
+ attribute :velocity_averaged_over, Integer
40
+ attribute :version, Integer
41
+ attribute :week_start_day, String
42
+
43
+ # @return [String] comma separated list of labels
44
+ def label_list
45
+ @label_list ||= labels.collect(&:name).join(',')
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class ProjectMembership
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :person_id, Integer
10
+ attribute :project_id, Integer
11
+ attribute :role, String
12
+ attribute :project_color, String
13
+ attribute :wants_comment_notification_emails, Boolean
14
+ attribute :kind, String
15
+ attribute :person, PivotalApi::Resources::Person
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Story
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :accepted_at, DateTime
9
+ attribute :comment_ids, Array[Integer]
10
+ attribute :created_at, DateTime
11
+ attribute :current_state, String # (accepted, delivered, finished, started, rejected, planned, unstarted, unscheduled)
12
+ attribute :deadline, DateTime
13
+ attribute :description, String
14
+ attribute :estimate, Float
15
+ attribute :external_id, String
16
+ attribute :follower_ids, Array[Integer]
17
+ attribute :id, Integer
18
+ attribute :integration_id, Integer
19
+ attribute :kind, String
20
+ attribute :label_ids, Array[Integer]
21
+ attribute :labels, Array[PivotalApi::Resources::Label]
22
+ attribute :name, String
23
+ attribute :owned_by_id, Integer # deprecated!
24
+ attribute :owner_ids, Array[Integer]
25
+ attribute :owners, Array[PivotalApi::Resources::Person]
26
+ attribute :planned_iteration_number, Integer
27
+ attribute :project_id, Integer
28
+ attribute :requested_by_id, Integer
29
+ attribute :story_type, String # (feature, bug, chore, release)
30
+ attribute :task_ids, Array[Integer]
31
+ attribute :tasks, Array[PivotalApi::Resources::Task]
32
+ attribute :updated_at, DateTime
33
+ attribute :url, String
34
+
35
+ # @return [String] Comma separated list of labels.
36
+ def label_list
37
+ @label_list ||= labels.collect(&:name).join(',')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Task
4
+ include Virtus.model
5
+
6
+ # attribute :client
7
+
8
+ attribute :id, Integer
9
+ attribute :story_id, Integer
10
+ attribute :project_id, Integer
11
+ attribute :description, String
12
+ attribute :complete, Boolean
13
+ attribute :position, Integer
14
+ attribute :created_at, DateTime
15
+ attribute :updated_at, DateTime
16
+ attribute :kind, String
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class TimeZone
4
+ include Virtus.value_object
5
+
6
+ values do
7
+ attribute :offset, String
8
+ attribute :olson_name, String
9
+ attribute :kind, String
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module PivotalApi
2
+ module Resources
3
+ class Webhook
4
+ include Virtus.model
5
+
6
+ attribute :id, Integer
7
+ attribute :project_id, Integer
8
+ attribute :webhook_url, String
9
+ attribute :webhook_version, String, default: 'v5'
10
+ attribute :created_at, DateTime
11
+ attribute :updated_at, DateTime
12
+ attribute :kind, String
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module PivotalApi
2
+ VERSION = '0.3.1'
3
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Maciej Skierkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: addressable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: virtus
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: faraday_middleware
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: excon
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: This gem allows you to easily use the Pivotal Tracker v5 API.
112
+ email:
113
+ - maciej@factor.io
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - "./lib/pivotal-api.rb"
119
+ - "./lib/pivotal_api/client.rb"
120
+ - "./lib/pivotal_api/endpoints/comments.rb"
121
+ - "./lib/pivotal_api/endpoints/epics.rb"
122
+ - "./lib/pivotal_api/endpoints/iterations.rb"
123
+ - "./lib/pivotal_api/endpoints/labels.rb"
124
+ - "./lib/pivotal_api/endpoints/memberships.rb"
125
+ - "./lib/pivotal_api/endpoints/projects.rb"
126
+ - "./lib/pivotal_api/endpoints/stories.rb"
127
+ - "./lib/pivotal_api/endpoints/tasks.rb"
128
+ - "./lib/pivotal_api/endpoints/webhooks.rb"
129
+ - "./lib/pivotal_api/error.rb"
130
+ - "./lib/pivotal_api/logger.rb"
131
+ - "./lib/pivotal_api/resources/account.rb"
132
+ - "./lib/pivotal_api/resources/comment.rb"
133
+ - "./lib/pivotal_api/resources/epic.rb"
134
+ - "./lib/pivotal_api/resources/iteration.rb"
135
+ - "./lib/pivotal_api/resources/label.rb"
136
+ - "./lib/pivotal_api/resources/me.rb"
137
+ - "./lib/pivotal_api/resources/membership_summary.rb"
138
+ - "./lib/pivotal_api/resources/person.rb"
139
+ - "./lib/pivotal_api/resources/project.rb"
140
+ - "./lib/pivotal_api/resources/project_membership.rb"
141
+ - "./lib/pivotal_api/resources/story.rb"
142
+ - "./lib/pivotal_api/resources/task.rb"
143
+ - "./lib/pivotal_api/resources/time_zone.rb"
144
+ - "./lib/pivotal_api/resources/webhook.rb"
145
+ - "./lib/pivotal_api/version.rb"
146
+ homepage: https://github.com/factor-io/pivotal-api
147
+ licenses:
148
+ - MIT
149
+ metadata: {}
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 2.2.2
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: API client for the Pivotal Tracker v5 API
170
+ test_files: []
171
+ has_rdoc: