rutrack 0.0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +11 -0
- data/features/support/env.rb +3 -0
- data/features/support/hooks.rb +1 -0
- data/features/support/vcr.rb +16 -0
- data/features/support/world.rb +0 -0
- data/lib/youtrack.rb +11 -0
- data/lib/youtrack/client.rb +86 -0
- data/lib/youtrack/resources/base.rb +54 -0
- data/lib/youtrack/resources/issue.rb +169 -0
- data/lib/youtrack/resources/project.rb +55 -0
- data/lib/youtrack/resources/tag.rb +52 -0
- data/lib/youtrack/resources/user.rb +78 -0
- data/lib/youtrack/version.rb +4 -0
- data/spec/cassettes/client/connect.yml +40 -0
- data/spec/cassettes/client/connect_credentials_error.yml +39 -0
- data/spec/cassettes/client/connect_url_error.yml +45 -0
- data/spec/cassettes/users/current.yml +75 -0
- data/spec/cassettes/users/get_by_login.yml +75 -0
- data/spec/cassettes/users/get_saved_searches_for.yml +76 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/youtrack/client_spec.rb +92 -0
- data/spec/youtrack/resources/user_spec.rb +83 -0
- data/youtrack.gemspec +35 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3b0ed1e3d05a9ba00d50cb1e56adc509f805750b
|
4
|
+
data.tar.gz: 7b325149a69f6e48150accc076f90b388f4ca918
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f5de0c8c780a5b202421e450ca8260b1f1881fa84b72b7fe411be33e6d15b705654fee80f25d5baf2809251863b6d9b6c3bcc3db6b0f349d52852a67cd1cc47
|
7
|
+
data.tar.gz: 6d478f473f77d029f2baee22a3234874d75ed8c7ed63e7f02f32282ab552d01ef800d2e5b6e2268fdd67efebd7e9af7fbcb239cce20528488f76c12b432a797e
|
data/.envrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export PATH=$PWD/binstubs:$PATH
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
youtrack
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- ruby-head
|
6
|
+
- jruby-19mode
|
7
|
+
- jruby-head
|
8
|
+
script:
|
9
|
+
- bundle exec rspec
|
10
|
+
- bundle exec cucumber
|
11
|
+
|
12
|
+
branches:
|
13
|
+
only:
|
14
|
+
- master
|
15
|
+
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: ruby-head
|
19
|
+
- rvm: jruby-head
|
20
|
+
|
21
|
+
notifications:
|
22
|
+
email:
|
23
|
+
- jwaterfaucett@gmail.com
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 John Faucett
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Youtrack
|
2
|
+
|
3
|
+
youTrack REST API Client
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/jwaterfaucett/youtrack.png)](https://travis-ci.org/jwaterfaucett/youtrack)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/youtrack.png)](http://badge.fury.io/rb/youtrack)
|
7
|
+
|
8
|
+
|
9
|
+
## New in this release
|
10
|
+
|
11
|
+
###### *(v. v0.0.11.1 - 13/12/16)*
|
12
|
+
|
13
|
+
Max issues option & Filter support
|
14
|
+
|
15
|
+
- Pass **max issues** & **filter** in `get_issues_for` request
|
16
|
+
|
17
|
+
Example:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
|
21
|
+
filter = "Username: -{No username}" # youtrack-style filter
|
22
|
+
max = 5000 # max issues to grab
|
23
|
+
client.projects.get_issues_for(project_name, max, filter)
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Add this line to your application's Gemfile:
|
31
|
+
|
32
|
+
gem 'youtrack', :git => "git://github.com/jesusinyourtown1/youtrack.git", :branch => "dev-new"
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
|
36
|
+
$ bundle
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
|
42
|
+
client = Youtrack::Client.new do |c|
|
43
|
+
c.url = "https://example.com"
|
44
|
+
c.login = "root"
|
45
|
+
c.password = "root"
|
46
|
+
|
47
|
+
# Hook into the Net::HTTP set_debug_output method for debugging HTTP Request/Response Cycles
|
48
|
+
c.debug = true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Now connect to the server
|
52
|
+
# It sets the Cookies and returns the Connection response status code
|
53
|
+
client.connect!
|
54
|
+
|
55
|
+
```
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
To get a feel for the API browse the [examples directory](https://github.com/jwaterfaucett/youtrack/tree/master/examples).
|
60
|
+
|
61
|
+
## Issues
|
62
|
+
|
63
|
+
Any issues or bugs should be well documented submit as many details as possible.
|
64
|
+
The best issues have the following characteristics:
|
65
|
+
|
66
|
+
1. Detail description with a title that conveys the problem well.
|
67
|
+
2. Gists and/or failing tests that show under what conditions the problem occurs.
|
68
|
+
3. Environment information, platform, gem version, etc.
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
1. Fork it
|
73
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
74
|
+
3. Write your feature in Cucumber.
|
75
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
76
|
+
5. Make sure your feature and (optional) Rspec test are green.
|
77
|
+
6. Rebase from master.
|
78
|
+
7. Push to the branch (`git push origin my-new-feature`)
|
79
|
+
8. Create new Pull Request.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "vcr"
|
2
|
+
|
3
|
+
|
4
|
+
VCR.config do |c|
|
5
|
+
|
6
|
+
# tell vcr where to store recorded responses
|
7
|
+
c.cassette_library_dir = "fixtures/cassette_library"
|
8
|
+
|
9
|
+
# use webmock for stubbing
|
10
|
+
c.hook_into :webmock
|
11
|
+
|
12
|
+
# allow local requests
|
13
|
+
c.ignore_localhost = true
|
14
|
+
|
15
|
+
c.default_cassette_options = { record: :none }
|
16
|
+
end
|
File without changes
|
data/lib/youtrack.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "httparty"
|
2
|
+
|
3
|
+
module Youtrack
|
4
|
+
autoload :Client, "youtrack/client"
|
5
|
+
autoload :VERSION, "youtrack/version"
|
6
|
+
autoload :Base, "youtrack/resources/base"
|
7
|
+
autoload :Issue, "youtrack/resources/issue"
|
8
|
+
autoload :Project, "youtrack/resources/project"
|
9
|
+
autoload :User, "youtrack/resources/user"
|
10
|
+
autoload :Tag, "youtrack/resources/tag"
|
11
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Youtrack
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# holds the youTrack Server url
|
5
|
+
# defaults to nil
|
6
|
+
attr_accessor :url
|
7
|
+
|
8
|
+
# stores the Server login credential
|
9
|
+
# defaults to nil
|
10
|
+
attr_accessor :login
|
11
|
+
|
12
|
+
# stores the Server password credential
|
13
|
+
# defaulst to nil
|
14
|
+
attr_accessor :password
|
15
|
+
|
16
|
+
# stores the response object
|
17
|
+
attr_accessor :connection
|
18
|
+
|
19
|
+
# stores the auth_headers
|
20
|
+
attr_accessor :cookies
|
21
|
+
|
22
|
+
# stores the scope of all subsequent api calls
|
23
|
+
attr_accessor :admin
|
24
|
+
|
25
|
+
def admin?
|
26
|
+
true == @admin
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets debugging mode
|
30
|
+
attr_accessor :debug
|
31
|
+
|
32
|
+
def initialize(options={}, &block)
|
33
|
+
@cookies = {}
|
34
|
+
@admin = false
|
35
|
+
@debug = false
|
36
|
+
|
37
|
+
yield(self) if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
# the server endpoint
|
41
|
+
def endpoint
|
42
|
+
@endpoint = File.join(url, 'rest')
|
43
|
+
end
|
44
|
+
|
45
|
+
def credentials_hash
|
46
|
+
{ login: login, password: password }
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Makes a login call and sets the Cookie headers
|
51
|
+
#
|
52
|
+
# Returns the status code of the connection call
|
53
|
+
def connect!
|
54
|
+
@connection = HTTParty.post(File.join(url, "rest/user/login"), body: credentials_hash )
|
55
|
+
#@connection = HTTParty.post(File.join(url, "hub/auth/login"), body: credentials_hash )
|
56
|
+
@cookies['Cookie'] = @connection.headers['set-cookie']
|
57
|
+
@connection.code
|
58
|
+
end
|
59
|
+
|
60
|
+
def connected?
|
61
|
+
!!(connection && connection.headers['set-cookie'] && connection.code == 200)
|
62
|
+
end
|
63
|
+
|
64
|
+
def users
|
65
|
+
resource(:user).new(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def projects
|
69
|
+
resource(:project).new(self)
|
70
|
+
end
|
71
|
+
|
72
|
+
def issues
|
73
|
+
resource(:issue).new(self)
|
74
|
+
end
|
75
|
+
|
76
|
+
def tags
|
77
|
+
resource(:tag).new(self)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def resource(resource_name)
|
83
|
+
Youtrack.const_get(resource_name.to_s.capitalize)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Youtrack
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
# The base route URL
|
6
|
+
attr_accessor :base_url
|
7
|
+
|
8
|
+
# The Server Endpoint
|
9
|
+
attr_accessor :service
|
10
|
+
|
11
|
+
# Stores the response of the previous request
|
12
|
+
attr_accessor :response
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(client)
|
16
|
+
@service = client
|
17
|
+
@base_url = @service.endpoint
|
18
|
+
self.class.debug_output($stderr) if client.debug
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def join(*args)
|
24
|
+
File.join(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare_options(options={})
|
28
|
+
options[:headers] ||= {}
|
29
|
+
options[:headers]['Cookie'] = service.cookies['Cookie']
|
30
|
+
options
|
31
|
+
end
|
32
|
+
|
33
|
+
def post(path, options={})
|
34
|
+
options = prepare_options(options)
|
35
|
+
@response = self.class.post( join(base_url, path), options )
|
36
|
+
end
|
37
|
+
|
38
|
+
def put(path, options={})
|
39
|
+
options = prepare_options(options)
|
40
|
+
@response = self.class.put( join(base_url, path), options )
|
41
|
+
end
|
42
|
+
|
43
|
+
def get(path, options={})
|
44
|
+
options = prepare_options(options)
|
45
|
+
@response = self.class.get( join(base_url, path), options )
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(path, options={})
|
49
|
+
options = prepare_options(options)
|
50
|
+
@response = self.class.delete( join(base_url, path), options )
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'net/http/post/multipart'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Youtrack
|
5
|
+
class Issue < Base
|
6
|
+
|
7
|
+
|
8
|
+
# ==================
|
9
|
+
# USER Methods
|
10
|
+
# ==================
|
11
|
+
|
12
|
+
|
13
|
+
# Create a New Issue
|
14
|
+
# project string ID of a project to add new issue to.
|
15
|
+
# summary string Short summary for the new issue.
|
16
|
+
# description string Description for the new issue.
|
17
|
+
# attachments file in "multipart/form-data" format One or several files in "multipart/form-data" format that should be attached to the new issue.
|
18
|
+
# permittedGroup string Set visibility for the new issue, that is: Specify a user group to which the issue will be visible.
|
19
|
+
#
|
20
|
+
# Hack: the body has to be set to empty so that HTTParty sends a Content-Length header, which Youtrack requires
|
21
|
+
#
|
22
|
+
# API-Success: Returns a 201 created wit hthe location header set
|
23
|
+
#
|
24
|
+
# Returns the response object
|
25
|
+
def create(attributes={})
|
26
|
+
put("issue", query: attributes, body: {})
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def find(issue_id)
|
31
|
+
get("issue/#{issue_id}")
|
32
|
+
response.parsed_response
|
33
|
+
end
|
34
|
+
|
35
|
+
def count(filter)
|
36
|
+
get("issue/count?filter=#{URI.encode(filter)}")
|
37
|
+
response.parsed_response
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_history_for(issue_id)
|
41
|
+
get("issue/#{issue_id}/history")
|
42
|
+
response.parsed_response
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_changes_for(issue_id)
|
46
|
+
get("issue/#{issue_id}/changes")
|
47
|
+
response.parsed_response
|
48
|
+
end
|
49
|
+
|
50
|
+
def exists?(issue_id)
|
51
|
+
get("issue/#{issue_id}/exists")
|
52
|
+
response.code == 200
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_attachments_for(issue_id)
|
56
|
+
get("issue/#{issue_id}/attachment")
|
57
|
+
response.parsed_response
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_comments_for(issue_id)
|
61
|
+
get("issue/#{issue_id}/comment")
|
62
|
+
response.parsed_response
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_links_for(issue_id)
|
66
|
+
get("issue/#{issue_id}/link")
|
67
|
+
response.parsed_response
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
# issueID string ID of an issue that should be updated.
|
73
|
+
# summary string New summary for the specified issue.
|
74
|
+
# description string Updated description for the specified issue.
|
75
|
+
def update(issue_id, attributes={})
|
76
|
+
post("issue/#{issue_id}", query: attributes)
|
77
|
+
response.parsed_response
|
78
|
+
end
|
79
|
+
|
80
|
+
def destroy(issue_id)
|
81
|
+
delete("issue/#{issue_id}")
|
82
|
+
response
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_attachment(issue_id, data, content_type, filename)
|
86
|
+
url = URI.parse(join(base_url, "issue/#{issue_id}/attachment"))
|
87
|
+
req = Net::HTTP::Post::Multipart.new( url.path, "file" => UploadIO.new(data, content_type, filename))
|
88
|
+
req['Cookie'] = service.cookies['Cookie']
|
89
|
+
http = Net::HTTP.new(url.host, url.port)
|
90
|
+
http.set_debug_output($stderr) if service.debug
|
91
|
+
|
92
|
+
if url.scheme == 'https'
|
93
|
+
http.use_ssl = true
|
94
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
95
|
+
end
|
96
|
+
|
97
|
+
response = http.request(req)
|
98
|
+
response
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def add_comment(issue_id, attributes={})
|
103
|
+
post("issue/#{issue_id}/execute", query: attributes)
|
104
|
+
response
|
105
|
+
end
|
106
|
+
|
107
|
+
# Get a list of issues for a search query.
|
108
|
+
#
|
109
|
+
# attributes
|
110
|
+
# filter string A query to search for issues.
|
111
|
+
# with string List of fields that should be included in the result.
|
112
|
+
# max integer Maximum number of issues to get. If not provided, only 10 issues will be returned by default.
|
113
|
+
# after integer A number of issues to skip before getting a list of issues.
|
114
|
+
#
|
115
|
+
def list attributes={}
|
116
|
+
attributes[:max] ||= 10
|
117
|
+
get("issue?#{URI.encode_www_form(attributes)}")
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get the list of available work items for a particular issue.
|
121
|
+
#
|
122
|
+
# issue_id string youtrack ticket id
|
123
|
+
#
|
124
|
+
def get_work_items_for(issue_id)
|
125
|
+
get("issue/#{issue_id}/timetracking/workitem/")
|
126
|
+
response.parsed_response
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add new work item to issue
|
130
|
+
#
|
131
|
+
# issue_id string youtrack ticket id
|
132
|
+
# attributes hash
|
133
|
+
# user string login name of the user who will be set as the author of the work item. (defaults to logged in user)
|
134
|
+
# date string date and time of the new work item in ISO8601 time format (defaults to current date)
|
135
|
+
# duration string Duration of the work item in minutes
|
136
|
+
# description string Activity description
|
137
|
+
#
|
138
|
+
def add_work_item_to(issue_id, attributes={})
|
139
|
+
attributes = attributes.to_hash
|
140
|
+
attributes.symbolize_keys!
|
141
|
+
attributes[:date] ||= Date.current.iso8601
|
142
|
+
epoc_date = Date.parse(attributes[:date]).to_time.to_i * 1000
|
143
|
+
attributes[:user] ||= self.service.login
|
144
|
+
work_items = REXML::Element.new('workItems')
|
145
|
+
work_item = work_items.add_element('workItem')
|
146
|
+
work_item.add_element('author').add_attribute('login', attributes[:user])
|
147
|
+
work_item.add_element('date').add_text(epoc_date.to_s)
|
148
|
+
work_item.add_element('duration').add_text(attributes[:duration].to_s)
|
149
|
+
work_item.add_element('description').add_text(attributes[:description])
|
150
|
+
put("import/issue/#{issue_id}/workitems", body: work_items.to_s, :headers => {'Content-type' => 'text/xml'} )
|
151
|
+
response
|
152
|
+
end
|
153
|
+
|
154
|
+
# apply command
|
155
|
+
#
|
156
|
+
# issue_id string youtrack ticket id
|
157
|
+
# attributes hash
|
158
|
+
# command string
|
159
|
+
# comment string
|
160
|
+
# group string
|
161
|
+
# disableNotifications boolean
|
162
|
+
# runAs string
|
163
|
+
#
|
164
|
+
def apply_command(issue_id, attributes={})
|
165
|
+
post("issue/#{issue_id}/execute", query: attributes)
|
166
|
+
response
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|