alm-rest-api 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitattributes +22 -0
- data/.gitignore +163 -0
- data/Rakefile +8 -0
- data/alm-rest-api-0.0.1.gem +0 -0
- data/alm-rest-api.gemspec +14 -0
- data/lib/alm-rest-api.rb +154 -0
- data/lib/alm-rest-api/constants.rb +12 -0
- data/lib/alm-rest-api/defect-fields.rb +33 -0
- data/lib/alm-rest-api/entity.rb +30 -0
- data/lib/alm-rest-api/response.rb +11 -0
- data/lib/alm-rest-api/rest-connector.rb +147 -0
- data/lib/alm-rest-api/value-lists.rb +43 -0
- data/test/test_alm_rest_api.rb +118 -0
- metadata +77 -0
data/.gitattributes
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Auto detect text files and perform LF normalization
|
2
|
+
* text=auto
|
3
|
+
|
4
|
+
# Custom for Visual Studio
|
5
|
+
*.cs diff=csharp
|
6
|
+
*.sln merge=union
|
7
|
+
*.csproj merge=union
|
8
|
+
*.vbproj merge=union
|
9
|
+
*.fsproj merge=union
|
10
|
+
*.dbproj merge=union
|
11
|
+
|
12
|
+
# Standard to msysgit
|
13
|
+
*.doc diff=astextplain
|
14
|
+
*.DOC diff=astextplain
|
15
|
+
*.docx diff=astextplain
|
16
|
+
*.DOCX diff=astextplain
|
17
|
+
*.dot diff=astextplain
|
18
|
+
*.DOT diff=astextplain
|
19
|
+
*.pdf diff=astextplain
|
20
|
+
*.PDF diff=astextplain
|
21
|
+
*.rtf diff=astextplain
|
22
|
+
*.RTF diff=astextplain
|
data/.gitignore
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
#################
|
2
|
+
## Eclipse
|
3
|
+
#################
|
4
|
+
|
5
|
+
*.pydevproject
|
6
|
+
.project
|
7
|
+
.metadata
|
8
|
+
bin/
|
9
|
+
tmp/
|
10
|
+
*.tmp
|
11
|
+
*.bak
|
12
|
+
*.swp
|
13
|
+
*~.nib
|
14
|
+
local.properties
|
15
|
+
.classpath
|
16
|
+
.settings/
|
17
|
+
.loadpath
|
18
|
+
|
19
|
+
# External tool builders
|
20
|
+
.externalToolBuilders/
|
21
|
+
|
22
|
+
# Locally stored "Eclipse launch configurations"
|
23
|
+
*.launch
|
24
|
+
|
25
|
+
# CDT-specific
|
26
|
+
.cproject
|
27
|
+
|
28
|
+
# PDT-specific
|
29
|
+
.buildpath
|
30
|
+
|
31
|
+
|
32
|
+
#################
|
33
|
+
## Visual Studio
|
34
|
+
#################
|
35
|
+
|
36
|
+
## Ignore Visual Studio temporary files, build results, and
|
37
|
+
## files generated by popular Visual Studio add-ons.
|
38
|
+
|
39
|
+
# User-specific files
|
40
|
+
*.suo
|
41
|
+
*.user
|
42
|
+
*.sln.docstates
|
43
|
+
|
44
|
+
# Build results
|
45
|
+
[Dd]ebug/
|
46
|
+
[Rr]elease/
|
47
|
+
*_i.c
|
48
|
+
*_p.c
|
49
|
+
*.ilk
|
50
|
+
*.meta
|
51
|
+
*.obj
|
52
|
+
*.pch
|
53
|
+
*.pdb
|
54
|
+
*.pgc
|
55
|
+
*.pgd
|
56
|
+
*.rsp
|
57
|
+
*.sbr
|
58
|
+
*.tlb
|
59
|
+
*.tli
|
60
|
+
*.tlh
|
61
|
+
*.tmp
|
62
|
+
*.vspscc
|
63
|
+
.builds
|
64
|
+
*.dotCover
|
65
|
+
|
66
|
+
## TODO: If you have NuGet Package Restore enabled, uncomment this
|
67
|
+
#packages/
|
68
|
+
|
69
|
+
# Visual C++ cache files
|
70
|
+
ipch/
|
71
|
+
*.aps
|
72
|
+
*.ncb
|
73
|
+
*.opensdf
|
74
|
+
*.sdf
|
75
|
+
|
76
|
+
# Visual Studio profiler
|
77
|
+
*.psess
|
78
|
+
*.vsp
|
79
|
+
|
80
|
+
# ReSharper is a .NET coding add-in
|
81
|
+
_ReSharper*
|
82
|
+
|
83
|
+
# Installshield output folder
|
84
|
+
[Ee]xpress
|
85
|
+
|
86
|
+
# DocProject is a documentation generator add-in
|
87
|
+
DocProject/buildhelp/
|
88
|
+
DocProject/Help/*.HxT
|
89
|
+
DocProject/Help/*.HxC
|
90
|
+
DocProject/Help/*.hhc
|
91
|
+
DocProject/Help/*.hhk
|
92
|
+
DocProject/Help/*.hhp
|
93
|
+
DocProject/Help/Html2
|
94
|
+
DocProject/Help/html
|
95
|
+
|
96
|
+
# Click-Once directory
|
97
|
+
publish
|
98
|
+
|
99
|
+
# Others
|
100
|
+
[Bb]in
|
101
|
+
[Oo]bj
|
102
|
+
sql
|
103
|
+
TestResults
|
104
|
+
*.Cache
|
105
|
+
ClientBin
|
106
|
+
stylecop.*
|
107
|
+
~$*
|
108
|
+
*.dbmdl
|
109
|
+
Generated_Code #added for RIA/Silverlight projects
|
110
|
+
|
111
|
+
# Backup & report files from converting an old project file to a newer
|
112
|
+
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
113
|
+
_UpgradeReport_Files/
|
114
|
+
Backup*/
|
115
|
+
UpgradeLog*.XML
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
############
|
120
|
+
## Windows
|
121
|
+
############
|
122
|
+
|
123
|
+
# Windows image file caches
|
124
|
+
Thumbs.db
|
125
|
+
|
126
|
+
# Folder config file
|
127
|
+
Desktop.ini
|
128
|
+
|
129
|
+
|
130
|
+
#############
|
131
|
+
## Python
|
132
|
+
#############
|
133
|
+
|
134
|
+
*.py[co]
|
135
|
+
|
136
|
+
# Packages
|
137
|
+
*.egg
|
138
|
+
*.egg-info
|
139
|
+
dist
|
140
|
+
build
|
141
|
+
eggs
|
142
|
+
parts
|
143
|
+
bin
|
144
|
+
var
|
145
|
+
sdist
|
146
|
+
develop-eggs
|
147
|
+
.installed.cfg
|
148
|
+
|
149
|
+
# Installer logs
|
150
|
+
pip-log.txt
|
151
|
+
|
152
|
+
# Unit test / coverage reports
|
153
|
+
.coverage
|
154
|
+
.tox
|
155
|
+
|
156
|
+
#Translations
|
157
|
+
*.mo
|
158
|
+
|
159
|
+
#Mr Developer
|
160
|
+
.mr.developer.cfg
|
161
|
+
|
162
|
+
# Mac crap
|
163
|
+
.DS_Store
|
data/Rakefile
ADDED
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'alm-rest-api'
|
3
|
+
s.version = '0.0.2'
|
4
|
+
s.date = '2012-12-14'
|
5
|
+
s.summary = "ALM!"
|
6
|
+
s.description = "ALM REST API Integration"
|
7
|
+
s.authors = ["Simon Zheng"]
|
8
|
+
s.email = 'xiaomengzheng@gmail.com'
|
9
|
+
s.require_paths = ["lib"]
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
#s.files = ["lib/alm-rest-api.rb"]
|
12
|
+
s.homepage =
|
13
|
+
'http://rubygems.org/gems/alm-rest-api'
|
14
|
+
end
|
data/lib/alm-rest-api.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# alm-rest-api.rb
|
2
|
+
|
3
|
+
module ALM
|
4
|
+
|
5
|
+
# Logging in to our system is standard http login (basic authentication),
|
6
|
+
# where one must store the returned cookies for further use.
|
7
|
+
def self.login(loginUrl, username, password)
|
8
|
+
response = RestConnector.instance.httpBasicAuth(loginUrl, username, password)
|
9
|
+
|
10
|
+
return response.statusCode == '200'
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.logout()
|
14
|
+
# note the get operation logs us out by setting authentication cookies to:
|
15
|
+
# LWSSO_COOKIE_KEY="" via server response header Set-Cookie
|
16
|
+
logoutUrl = RestConnector.instance.buildUrl("qcbin/authentication-point/logout")
|
17
|
+
response = RestConnector.instance.httpGet(logoutUrl, nil, nil)
|
18
|
+
|
19
|
+
return response.statusCode == '200'
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.isAuthenticated()
|
23
|
+
isAuthenticateUrl = RestConnector.instance.buildUrl("qcbin/rest/is-authenticated")
|
24
|
+
response = RestConnector.instance.httpGet(isAuthenticateUrl, nil, nil)
|
25
|
+
responseCode = response.statusCode
|
26
|
+
|
27
|
+
# if already authenticated
|
28
|
+
# if not authenticated - get the address where to authenticate
|
29
|
+
# via WWW-Authenticate
|
30
|
+
if responseCode == "200"
|
31
|
+
ret = nil
|
32
|
+
elsif responseCode == "401"
|
33
|
+
authenticationHeader = response.responseHeaders["WWW-Authenticate"]
|
34
|
+
newUrl = authenticationHeader.split("=").at(1)
|
35
|
+
newUrl = newUrl.delete("\"")
|
36
|
+
newUrl = newUrl + "/authenticate"
|
37
|
+
ret = newUrl
|
38
|
+
end
|
39
|
+
|
40
|
+
return ret
|
41
|
+
end
|
42
|
+
|
43
|
+
# convenience method to do user login
|
44
|
+
def self.isLoggedIn(username, password)
|
45
|
+
authenticationPoint = isAuthenticated();
|
46
|
+
if (authenticationPoint != nil)
|
47
|
+
return login(authenticationPoint, username, password)
|
48
|
+
end
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
# read all defects fields
|
53
|
+
def self.getDefectFields(required = false)
|
54
|
+
defectFieldsUrl = RestConnector.instance.buildEntityCollectionUrl("customization/entities/defect/field")
|
55
|
+
queryString = nil
|
56
|
+
if required
|
57
|
+
queryString = "required=true"
|
58
|
+
end
|
59
|
+
requestHeaders = Hash.new
|
60
|
+
requestHeaders["Accept"] = "application/xml"
|
61
|
+
response = RestConnector.instance.httpGet(defectFieldsUrl, queryString, requestHeaders)
|
62
|
+
|
63
|
+
defectFields = nil
|
64
|
+
if response.statusCode == '200'
|
65
|
+
defectFields = DefectFields::Fields.parse(response.toString())
|
66
|
+
end
|
67
|
+
|
68
|
+
return defectFields
|
69
|
+
end
|
70
|
+
|
71
|
+
# read pre-defined defects fields values
|
72
|
+
def self.getValueLists(defectFields = nil)
|
73
|
+
# ALM 11.0 the url is "customization/list"
|
74
|
+
valueListsUrl = RestConnector.instance.buildEntityCollectionUrl("customization/list")
|
75
|
+
queryString = nil
|
76
|
+
if defectFields
|
77
|
+
defectFields.fields.each do |field|
|
78
|
+
if field.list_id
|
79
|
+
if queryString == nil
|
80
|
+
queryString = "id=" + field.list_id.to_s
|
81
|
+
else
|
82
|
+
queryString = queryString + "," + field.list_id.to_s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
requestHeaders = Hash.new
|
89
|
+
requestHeaders["Accept"] = "application/xml"
|
90
|
+
response = RestConnector.instance.httpGet(valueListsUrl, queryString, requestHeaders)
|
91
|
+
|
92
|
+
valueLists = nil
|
93
|
+
if response.statusCode == '200'
|
94
|
+
valueLists = ValueLists::Lists.parse(response.toString())
|
95
|
+
end
|
96
|
+
|
97
|
+
return valueLists
|
98
|
+
end
|
99
|
+
|
100
|
+
# create new defect
|
101
|
+
def self.createDefect(defect)
|
102
|
+
defectsUrl = RestConnector.instance.buildEntityCollectionUrl("defect")
|
103
|
+
requestHeaders = Hash.new
|
104
|
+
requestHeaders["Content-Type"] = "application/xml"
|
105
|
+
requestHeaders["Accept"] = "application/xml"
|
106
|
+
|
107
|
+
response = RestConnector.instance.httpPost(defectsUrl, defect.to_xml, requestHeaders)
|
108
|
+
|
109
|
+
defectId = nil
|
110
|
+
if response.statusCode == '201'
|
111
|
+
defectUrl = response.responseHeaders["Location"]
|
112
|
+
defectId = defectUrl.split('/').last
|
113
|
+
end
|
114
|
+
|
115
|
+
return defectId
|
116
|
+
end
|
117
|
+
|
118
|
+
# delete a defect
|
119
|
+
def self.deleteDefect(defectId)
|
120
|
+
defectUrl = RestConnector.instance.buildDefectUrl(defectId)
|
121
|
+
requestHeaders = Hash.new
|
122
|
+
requestHeaders["Accept"] = "application/xml"
|
123
|
+
|
124
|
+
response = RestConnector.instance.httpDelete(defectUrl, requestHeaders)
|
125
|
+
|
126
|
+
return response.statusCode == '200'
|
127
|
+
end
|
128
|
+
|
129
|
+
# attach a file
|
130
|
+
def self.attachWithMultipart(defectId, filePath)
|
131
|
+
attachmentUrl = RestConnector.instance.buildEntityCollectionUrl("attachment")
|
132
|
+
boundary = "AaB03x"
|
133
|
+
requestHeaders = Hash.new
|
134
|
+
requestHeaders["Content-Type"] = "multipart/form-data, boundary=#{boundary}"
|
135
|
+
|
136
|
+
#post_body = []
|
137
|
+
#post_body < < "--#{boundary}rn"
|
138
|
+
#post_body < < "Content-Disposition: form-data; name="datafile"; filename="#{File.basename(file)}"rn"
|
139
|
+
#post_body < < "Content-Type: text/plainrn"
|
140
|
+
#post_body < < "rn"
|
141
|
+
#post_body < < File.read(file)
|
142
|
+
#post_body < < "rn--#{boundary}--rn"
|
143
|
+
|
144
|
+
#Response response = RestConnector.instance.httpPost(attachmentUrl, post_body, requestHeaders)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
require 'alm-rest-api/entity'
|
150
|
+
require 'alm-rest-api/defect-fields'
|
151
|
+
require 'alm-rest-api/value-lists'
|
152
|
+
require 'alm-rest-api/constants'
|
153
|
+
require 'alm-rest-api/response'
|
154
|
+
require 'alm-rest-api/rest-connector'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# These constants are used throughout the code to set the server to work with.
|
2
|
+
# To execute this code, change these settings to fit
|
3
|
+
# those of your server.
|
4
|
+
class ALM::Constants
|
5
|
+
HOST = "localhost"
|
6
|
+
PORT = "8080"
|
7
|
+
USERNAME = "sa"
|
8
|
+
PASSWORD = "C71a04t23"
|
9
|
+
DOMAIN = "DEFAULT"
|
10
|
+
PROJECT = "Simon"
|
11
|
+
VERSIONED = true
|
12
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'happymapper'
|
2
|
+
|
3
|
+
module DefectFields
|
4
|
+
|
5
|
+
class Field
|
6
|
+
include HappyMapper
|
7
|
+
|
8
|
+
tag 'Field'
|
9
|
+
attribute :physical_name, String, :tag => 'PhysicalName'
|
10
|
+
attribute :name, String, :tag => 'Name'
|
11
|
+
attribute :label, String, :tag => 'Label'
|
12
|
+
element :active, Boolean, :tag => 'Active'
|
13
|
+
element :editable, Boolean, :tag => 'Editable'
|
14
|
+
element :size, Integer, :tag => 'Size'
|
15
|
+
element :filterable, Boolean, :tag => 'Filterable'
|
16
|
+
element :groupable, Boolean, :tag => 'Groupable'
|
17
|
+
element :history, Boolean, :tag => 'History'
|
18
|
+
element :list_id, Integer, :tag => 'List-Id'
|
19
|
+
element :type, String, :tag => 'Type'
|
20
|
+
element :supports_multivalue, Boolean, :tag => 'SupportsMultivalue'
|
21
|
+
element :required, Boolean, :tag => 'Required'
|
22
|
+
element :system, Boolean, :tag => 'System'
|
23
|
+
element :verify, Boolean, :tag => 'Verify'
|
24
|
+
element :virtual, Boolean, :tag => 'Virtual'
|
25
|
+
end
|
26
|
+
|
27
|
+
class Fields
|
28
|
+
include HappyMapper
|
29
|
+
|
30
|
+
tag 'Fields'
|
31
|
+
has_many :fields, Field
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'happymapper'
|
2
|
+
|
3
|
+
class Field
|
4
|
+
include HappyMapper
|
5
|
+
|
6
|
+
tag 'Field'
|
7
|
+
attribute :name, String, :tag => 'Name'
|
8
|
+
element :value, String, :tag => 'Value'
|
9
|
+
end
|
10
|
+
|
11
|
+
class Fields
|
12
|
+
include HappyMapper
|
13
|
+
|
14
|
+
tag 'Fields'
|
15
|
+
has_many :fields, Field
|
16
|
+
end
|
17
|
+
|
18
|
+
class Entity
|
19
|
+
include HappyMapper
|
20
|
+
|
21
|
+
def initialize(type)
|
22
|
+
@type = type
|
23
|
+
@fields = Fields.new
|
24
|
+
@fields.fields = Array.new
|
25
|
+
end
|
26
|
+
|
27
|
+
tag 'Entity'
|
28
|
+
attribute :type, String, :tag => 'Type'
|
29
|
+
element :fields, Fields
|
30
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# rest-connector.rb
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'stringio'
|
6
|
+
require 'singleton'
|
7
|
+
|
8
|
+
class ALM::RestConnector
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
attr_accessor :cookies
|
12
|
+
attr_accessor :host
|
13
|
+
attr_accessor :port
|
14
|
+
attr_accessor :domain
|
15
|
+
attr_accessor :project
|
16
|
+
|
17
|
+
def init(cookies, host, port, domain, project)
|
18
|
+
@cookies = cookies
|
19
|
+
@host = host
|
20
|
+
@port = port
|
21
|
+
@domain = domain
|
22
|
+
@project = project
|
23
|
+
end
|
24
|
+
|
25
|
+
def buildEntityCollectionUrl(entityType)
|
26
|
+
return buildUrl('qcbin/rest/domains/' + domain + '/projects/' + project + '/' + entityType + 's')
|
27
|
+
end
|
28
|
+
|
29
|
+
def buildDefectUrl(defectId)
|
30
|
+
return buildUrl('qcbin/rest/domains/' + domain + '/projects/' + project + '/defects/' + defectId)
|
31
|
+
end
|
32
|
+
|
33
|
+
def buildUrl(path)
|
34
|
+
return "http://#{host}:#{port}/#{path}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def httpPut(url, data, headers)
|
38
|
+
return doHttp('PUT', url, nil, data, headers, cookies)
|
39
|
+
end
|
40
|
+
|
41
|
+
def httpPost(url, data, headers)
|
42
|
+
return doHttp('POST', url, nil, data, headers, cookies)
|
43
|
+
end
|
44
|
+
|
45
|
+
def httpDelete(url, headers)
|
46
|
+
return doHttp('DELETE', url, nil, nil, headers, cookies)
|
47
|
+
end
|
48
|
+
|
49
|
+
def httpGet(url, queryString, headers)
|
50
|
+
return doHttp('GET', url, queryString, nil, headers, cookies)
|
51
|
+
end
|
52
|
+
|
53
|
+
def httpBasicAuth(url, username, password)
|
54
|
+
headers = {"username" => username, "password" => password}
|
55
|
+
return doHttp('AUTH', url, nil, nil, headers, cookies)
|
56
|
+
end
|
57
|
+
|
58
|
+
def getCookieString
|
59
|
+
s = StringIO.new
|
60
|
+
if (!cookies.empty?)
|
61
|
+
cookies.each{|key,value|
|
62
|
+
s << key << '=' << value << ';'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
return s.string
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def doHttp(type, url, queryString, data, headers, cookies)
|
72
|
+
if (queryString != nil && !queryString.empty?)
|
73
|
+
url.concat('?' + queryString)
|
74
|
+
end
|
75
|
+
|
76
|
+
uri = URI.parse(url)
|
77
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
78
|
+
|
79
|
+
case type
|
80
|
+
when "POST"
|
81
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
82
|
+
when "GET"
|
83
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
84
|
+
when "PUT"
|
85
|
+
request = Net::HTTP::Put.new(uri.request_uri)
|
86
|
+
when "DELETE"
|
87
|
+
request = Net::HTTP::Delete.new(uri.request_uri)
|
88
|
+
when "AUTH"
|
89
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
90
|
+
request.basic_auth(headers["username"], headers["password"])
|
91
|
+
end
|
92
|
+
|
93
|
+
cookieString = getCookieString()
|
94
|
+
prepareHttpRequest(request, headers, data, cookieString)
|
95
|
+
|
96
|
+
response = http.request(request)
|
97
|
+
|
98
|
+
res = retrieveHtmlResponse(response)
|
99
|
+
updateCookies(res)
|
100
|
+
|
101
|
+
return res
|
102
|
+
end
|
103
|
+
|
104
|
+
def prepareHttpRequest(request, headers, data, cookieString)
|
105
|
+
contentType = nil
|
106
|
+
if (cookieString != nil && !cookieString.empty?)
|
107
|
+
request["Cookie"] = cookieString
|
108
|
+
end
|
109
|
+
|
110
|
+
if (headers != nil)
|
111
|
+
contentType = headers.delete("Content-Type")
|
112
|
+
headers.each{|key, value|
|
113
|
+
request[key] = value
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
if (data != nil)
|
118
|
+
if (contentType != nil)
|
119
|
+
request["Content-Type"] = contentType
|
120
|
+
request.body = data
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def retrieveHtmlResponse(response)
|
126
|
+
res = ALM::Response.new
|
127
|
+
res.statusCode = response.code
|
128
|
+
res.responseHeaders = response
|
129
|
+
res.responseData = response.body
|
130
|
+
|
131
|
+
return res
|
132
|
+
end
|
133
|
+
|
134
|
+
def updateCookies(response)
|
135
|
+
newCookies = response.responseHeaders.get_fields('Set-Cookie')
|
136
|
+
if (newCookies != nil)
|
137
|
+
newCookies.each{|cookie|
|
138
|
+
c1 = cookie.split(';')[0]
|
139
|
+
c2 = c1.split('=')
|
140
|
+
key = c2[0]
|
141
|
+
value = c2[1]
|
142
|
+
cookies[key] = value
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'happymapper'
|
2
|
+
|
3
|
+
module ValueLists
|
4
|
+
|
5
|
+
class Item
|
6
|
+
include HappyMapper
|
7
|
+
|
8
|
+
tag 'Item'
|
9
|
+
attribute :value, String, :tag => 'value'
|
10
|
+
end
|
11
|
+
|
12
|
+
class Items
|
13
|
+
include HappyMapper
|
14
|
+
|
15
|
+
tag 'Items'
|
16
|
+
has_many :items, Item
|
17
|
+
end
|
18
|
+
|
19
|
+
class List
|
20
|
+
include HappyMapper
|
21
|
+
|
22
|
+
tag 'List'
|
23
|
+
element :name, String, :tag => 'Name'
|
24
|
+
element :id, Integer, :tag => 'Id'
|
25
|
+
has_one :items, Items
|
26
|
+
end
|
27
|
+
|
28
|
+
class Lists
|
29
|
+
include HappyMapper
|
30
|
+
|
31
|
+
tag 'Lists'
|
32
|
+
has_many :lists, List
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.getValuesById(id, valueLists)
|
36
|
+
valueLists.lists.each do |list|
|
37
|
+
if (list.id == id)
|
38
|
+
return list.items.items
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'alm-rest-api'
|
4
|
+
|
5
|
+
class TestALMRestAPI < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
ALM::RestConnector.instance.init(Hash.new,
|
9
|
+
ALM::Constants::HOST,
|
10
|
+
ALM::Constants::PORT,
|
11
|
+
ALM::Constants::DOMAIN,
|
12
|
+
ALM::Constants::PROJECT)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_AuthenticateLoginLogout
|
16
|
+
if false # change to true if you want run the test case
|
17
|
+
# Returns nil if authenticated. If not authenticated, returns
|
18
|
+
# a URL indicating where to login.
|
19
|
+
# We are not logged in, so call returns a URL
|
20
|
+
authenticationPoint = ALM.isAuthenticated()
|
21
|
+
assert_not_nil(authenticationPoint, "response from isAuthenticated means we're authenticated. that can't be.")
|
22
|
+
|
23
|
+
# now we login to previously returned URL.
|
24
|
+
loginResponse = ALM.login(authenticationPoint, ALM::Constants::USERNAME, ALM::Constants::PASSWORD)
|
25
|
+
assert(loginResponse, "failed to login.")
|
26
|
+
assert((ALM::RestConnector.instance.getCookieString.include? "LWSSO_COOKIE_KEY"), "login did not cause creation of Light Weight Single Sign On(LWSSO) cookie.")
|
27
|
+
|
28
|
+
# proof that we are indeed logged in
|
29
|
+
assert_nil(ALM.isAuthenticated(), "isAuthenticated returned not authenticated after login.")
|
30
|
+
|
31
|
+
# and now we logout
|
32
|
+
ALM.logout()
|
33
|
+
|
34
|
+
# And now we can see that we are indeed logged out
|
35
|
+
# because isAuthenticated once again returns a url, and not null.
|
36
|
+
assert_not_nil(ALM.isAuthenticated(), "isAuthenticated returned authenticated after logout.")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_GetDefectFields
|
41
|
+
if false # change to true if you want run the test case
|
42
|
+
loginResponse = ALM.isLoggedIn(ALM::Constants::USERNAME, ALM::Constants::PASSWORD)
|
43
|
+
assert(loginResponse, "failed to login.")
|
44
|
+
|
45
|
+
defectFields = ALM.getDefectFields(true)
|
46
|
+
valueLists = ALM.getValueLists(defectFields)
|
47
|
+
if defectFields
|
48
|
+
defectFields.fields.each do |field|
|
49
|
+
puts "Name = #{field.name}"
|
50
|
+
puts "Label = #{field.label}"
|
51
|
+
puts "Size = #{field.size}"
|
52
|
+
puts "Type = #{field.type}"
|
53
|
+
puts "Required = #{field.required}"
|
54
|
+
if (field.list_id && valueLists)
|
55
|
+
items = ValueLists.getValuesById(field.list_id, valueLists)
|
56
|
+
items.each do |item|
|
57
|
+
puts "Value = #{item.value}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
puts "--------------------------"
|
61
|
+
end
|
62
|
+
|
63
|
+
puts defectFields.to_xml
|
64
|
+
if valueLists
|
65
|
+
puts valueLists.to_xml
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ALM.logout()
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_CreateDeleteDefect
|
74
|
+
if true # change to true if you want run the test case
|
75
|
+
loginResponse = ALM.isLoggedIn(ALM::Constants::USERNAME, ALM::Constants::PASSWORD)
|
76
|
+
assert(loginResponse, "failed to login.")
|
77
|
+
|
78
|
+
defect = Entity.new("defect")
|
79
|
+
|
80
|
+
defectFields = ALM.getDefectFields(true)
|
81
|
+
valueLists = ALM.getValueLists(defectFields)
|
82
|
+
if defectFields
|
83
|
+
defectFields.fields.each do |field|
|
84
|
+
defectField = Field.new
|
85
|
+
defectField.name = field.name
|
86
|
+
type = field.type
|
87
|
+
case type
|
88
|
+
when "LookupList"
|
89
|
+
if valueLists
|
90
|
+
items = ValueLists.getValuesById(field.list_id, valueLists)
|
91
|
+
defectField.value = items.first.value
|
92
|
+
end
|
93
|
+
when "Date"
|
94
|
+
defectField.value = Time.now.strftime("%Y-%m-%d")
|
95
|
+
when "UsersList"
|
96
|
+
defectField.value = ALM::Constants::USERNAME
|
97
|
+
else
|
98
|
+
defectField.value = "0"
|
99
|
+
end
|
100
|
+
defect.fields.fields<<defectField
|
101
|
+
end
|
102
|
+
|
103
|
+
puts defect.to_xml
|
104
|
+
|
105
|
+
defectId = ALM.createDefect(defect)
|
106
|
+
assert_not_nil(defectId, "fail to create defect.")
|
107
|
+
|
108
|
+
if defectId
|
109
|
+
deleteResponse = ALM.deleteDefect(defectId)
|
110
|
+
assert(deleteResponse, "failed to delete defect.")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
ALM.logout()
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alm-rest-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Simon Zheng
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-12-14 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: ALM REST API Integration
|
22
|
+
email: xiaomengzheng@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- .gitattributes
|
31
|
+
- .gitignore
|
32
|
+
- Rakefile
|
33
|
+
- alm-rest-api-0.0.1.gem
|
34
|
+
- alm-rest-api.gemspec
|
35
|
+
- lib/alm-rest-api.rb
|
36
|
+
- lib/alm-rest-api/constants.rb
|
37
|
+
- lib/alm-rest-api/defect-fields.rb
|
38
|
+
- lib/alm-rest-api/entity.rb
|
39
|
+
- lib/alm-rest-api/response.rb
|
40
|
+
- lib/alm-rest-api/rest-connector.rb
|
41
|
+
- lib/alm-rest-api/value-lists.rb
|
42
|
+
- test/test_alm_rest_api.rb
|
43
|
+
homepage: http://rubygems.org/gems/alm-rest-api
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.10
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: ALM!
|
76
|
+
test_files: []
|
77
|
+
|