teamforge 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +18 -0
- data/lib/teamforge/ctfsoap.rb +189 -0
- data/lib/teamforge/services/categorizationapp.rb +91 -0
- data/lib/teamforge/services/collabnet.rb +487 -0
- data/lib/teamforge/services/discussionapp.rb +102 -0
- data/lib/teamforge/services/documentapp.rb +120 -0
- data/lib/teamforge/services/frsapp.rb +110 -0
- data/lib/teamforge/services/integrationdataapp.rb +59 -0
- data/lib/teamforge/services/newsapp.rb +50 -0
- data/lib/teamforge/services/pageapp.rb +113 -0
- data/lib/teamforge/services/planningapp.rb +130 -0
- data/lib/teamforge/services/pluggableapp.rb +151 -0
- data/lib/teamforge/services/rbacapp.rb +233 -0
- data/lib/teamforge/services/scmapp.rb +122 -0
- data/lib/teamforge/services/simplefilestorageapp.rb +43 -0
- data/lib/teamforge/services/taskapp.rb +118 -0
- data/lib/teamforge/services/trackerapp.rb +241 -0
- data/lib/teamforge/services/wikiapp.rb +66 -0
- data/lib/teamforge/version.rb +5 -0
- data/lib/teamforge.rb +64 -0
- metadata +77 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 CollabNet, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= TeamForge Ruby SDK
|
2
|
+
|
3
|
+
* TeamForge Version 6.1.1
|
4
|
+
* SDK Version 0.0.2
|
5
|
+
|
6
|
+
== Description
|
7
|
+
|
8
|
+
This is a Ruby wrapper for interacting with the CollabNet TeamForge SOAP WebService.
|
9
|
+
|
10
|
+
|
11
|
+
== Install
|
12
|
+
|
13
|
+
* sudo gem install teamforge
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
Author:: Patrick Wolf (mailto:pwolf@collab.net)
|
17
|
+
Copyright:: 2013 CollabNet, Inc.,
|
18
|
+
License:: MIT license
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module TeamForge
|
2
|
+
|
3
|
+
# Every call sent to the TeamForge server goes through two methods: request and parse_response. This makes it easier to normalize how messages are sent and received
|
4
|
+
# and it also makes it easier to troubleshoot because there is one place to trap messages to and from the server.
|
5
|
+
# The request method can be called on its own with no other part of this SDK wrapper if the proper paramters are passed to it.
|
6
|
+
# Everything else in the gem package is provided as a placeholder for the CTF Server endpoint, a set of CTF Object types, and convenience methods to generate the proper parameters for each method.
|
7
|
+
# E.g. CTF.request(method, message, endpoint, response, proxy=nil)
|
8
|
+
# Each request to the CTF server expects the following information:
|
9
|
+
# method: This is the function call that the server is expected to complete. A symbol is expected. E.g. :login
|
10
|
+
# message: This is the body of the soap call to the server. The expected value is a hash. If a soap object is specified it should be converted to a hash.
|
11
|
+
# All objects provided in this SDK include a to_h function to convert all instance variables into a nested hash with the object type.
|
12
|
+
# E.g. {:userName=>username, :password=>password, {:order! => [:userName, :password]} }
|
13
|
+
# The order! key tells the message the parameter order expected by the server. CTF is picky about parameter order.
|
14
|
+
# endpoint: The URL of the specific soap endpoint for this method. Each of the different CTF Web services has its own endpoint. This is specified in each function call within this SDK wrapper.
|
15
|
+
# E.g. "http://localhost:8080/ce-soap60/services/TrackerApp"
|
16
|
+
# response: The response message name that is expected from a successful call to the CTF server. A symbol is expected
|
17
|
+
# E.g. :loginResponse
|
18
|
+
# proxy: If a proxy server is being used to connect to the the CTF server it should be specified. It can be left blank.
|
19
|
+
def self.request(req_msg, server=nil, proxy=nil)
|
20
|
+
return unless req_msg.kind_of? Struct
|
21
|
+
|
22
|
+
server ||= @server
|
23
|
+
|
24
|
+
if server.nil?
|
25
|
+
raise(TeamForgeError, "No TeamForge server specified")
|
26
|
+
else
|
27
|
+
@server = format_url(server)
|
28
|
+
end
|
29
|
+
|
30
|
+
proxy ||= @proxy
|
31
|
+
|
32
|
+
@endpoint, method = req_msg.class.to_s.split("::").pop(2)
|
33
|
+
@endpoint == "TeamForge" ? @endpoint = "CollabNet" : @endpoint
|
34
|
+
|
35
|
+
method = "#{method.slice!(0).downcase}#{method}".to_sym
|
36
|
+
|
37
|
+
response_msg = "#{method}Response".to_sym
|
38
|
+
return_msg = "#{method}Return".to_sym
|
39
|
+
|
40
|
+
|
41
|
+
fq_path = @server + SERVICE_PATH + @endpoint
|
42
|
+
|
43
|
+
|
44
|
+
# The client from Savon is instantiated with the proper namespace, the endpoint for the request, and SSL verification (from HTTPI) turned off
|
45
|
+
client = Savon::Client.new {wsdl.namespace = SERVICE_NAMESPACE; wsdl.endpoint = fq_path; http.auth.ssl.verify_mode= :none }
|
46
|
+
|
47
|
+
# proxy support in case CTF needs to be accessed through a proxy
|
48
|
+
if proxy
|
49
|
+
client.http.proxy = @proxy
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
unless req_msg.respond_to? :empty
|
54
|
+
message = Hash[req_msg.each_pair.to_a]
|
55
|
+
|
56
|
+
# if a TeamForge Struct was sent to the function it has to be converted to a hash first to be processed by Savon
|
57
|
+
# Luckily there is an easy method to convert a Struct to a Hash
|
58
|
+
message.each do |key, value|
|
59
|
+
message[key] = rep_blank_with_nil(value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
message[:order!] = req_msg.members
|
64
|
+
|
65
|
+
begin
|
66
|
+
# This is the only place that the Savon::Client is currently used. It creates a SOAP requests with the required namespaces, headers, and the SOAP message that was sent to the request method.
|
67
|
+
# The response message from the CTF server is a SOAP method.
|
68
|
+
ctfresponse = client.request(method) do
|
69
|
+
soap.namespaces["xmlns:tns1"] = TYPE_NAMESPACE
|
70
|
+
soap.namespaces["xmlns:impl"] = SERVICE_NAMESPACE
|
71
|
+
soap.namespaces["xmlns:soapenc"] = SOAP_NAMESPACE
|
72
|
+
soap.body = message
|
73
|
+
end
|
74
|
+
# Return any error messages in a return hash. This error might be SOAP, HTTPI, or generic error. Each method checks for presense of fault but does not raise runtime error.
|
75
|
+
# Raising runtime exceptions is left to the calling program
|
76
|
+
rescue Exception => e
|
77
|
+
raise(TeamForgeError, e.message)
|
78
|
+
end
|
79
|
+
|
80
|
+
# if there are no errors our call to CTF was successful and we need to parse the response
|
81
|
+
# Savon does not handle MultiRef responses so we get a Nokogiri XML document and send that to our custom parser
|
82
|
+
# The parser must also be told what CTF return message that is expected
|
83
|
+
@response_doc = ctfresponse.doc
|
84
|
+
|
85
|
+
parse_response(response_msg)[return_msg]
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# A very basic Nokogiri parser. This function requires two parameters:
|
90
|
+
# property: This is the return message that the specific CTF method is expected to return
|
91
|
+
# E.g. :loginResponse
|
92
|
+
# response: This parameter is expected to be a Nokogiri document.
|
93
|
+
# This method returns a hash. The key for hash will be the element name of the return and the value will be the element value.
|
94
|
+
# If the element has an "href" attribute the return value is a complex type and must be parsed as a complex type.
|
95
|
+
# If DataRows are present then the response is an array of complexTypes and an array must be created to hold these datarows.
|
96
|
+
# If it is a complexType this function will return an object of the type specified.
|
97
|
+
# E.g. UserSoapDO
|
98
|
+
def self.parse_response(doc_node)
|
99
|
+
return_msg = {}
|
100
|
+
@response_doc.xpath("//#{doc_node.to_s}").children.each do |child|
|
101
|
+
|
102
|
+
href = child.attribute("href").to_s.gsub("#","")
|
103
|
+
|
104
|
+
unless href.empty?
|
105
|
+
multiref = @response_doc.xpath("//multiRef[@id='#{href}']")
|
106
|
+
|
107
|
+
if multiref.children.first.name == "dataRows"
|
108
|
+
datarows = []
|
109
|
+
multiref.children.children.each do |datarow|
|
110
|
+
data_href = datarow.attribute("href").to_s.gsub("#","")
|
111
|
+
datarows.push(create_struct(@response_doc.xpath("//multiRef[@id='#{data_href}']")))
|
112
|
+
end
|
113
|
+
return_msg[child.name.to_sym] = datarows
|
114
|
+
else
|
115
|
+
return_msg[child.name.to_sym] = create_struct(multiref)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
return_msg[child.name.to_sym] = child.text
|
119
|
+
end
|
120
|
+
end
|
121
|
+
return return_msg
|
122
|
+
end
|
123
|
+
|
124
|
+
# Takes a Nokogiri node and parses it into the object type specified
|
125
|
+
# The return type is a Ruby object
|
126
|
+
def self.create_struct(node)
|
127
|
+
|
128
|
+
@endpoint == "CollabNet" ? ctfapp="TeamForge" : ctfapp="TeamForge::#{@endpoint}"
|
129
|
+
attributes = {}
|
130
|
+
|
131
|
+
type = node.attribute("type").to_s.split(":")[1]
|
132
|
+
|
133
|
+
new_struct = eval("#{ctfapp}::#{type}.new")
|
134
|
+
|
135
|
+
node.children.each do |element|
|
136
|
+
|
137
|
+
el_type = element.attribute("type").to_s.split(":")[1].to_s.downcase.to_sym
|
138
|
+
node_href = element.attribute("href").to_s.gsub("#","")
|
139
|
+
|
140
|
+
element.name = element.name.snakecase
|
141
|
+
|
142
|
+
unless node_href.empty?
|
143
|
+
new_struct[element.name] = create_struct(@response_doc.xpath("//multiRef[@id='#{node_href}']"))
|
144
|
+
else
|
145
|
+
if el_type == :array
|
146
|
+
unless element.child.nil?
|
147
|
+
new_struct[element.name.to_sym] = {element.name.to_sym => element.element_children.map {|chld| chld.text.to_s }, :attributes! => { element.child.name.to_sym => {'xsi:type' => element.child.attribute("type").to_s}}}
|
148
|
+
end
|
149
|
+
attributes[element.name.to_sym] = {'soapenc:arrayType'=> element.attribute("arrayType").to_s, 'xsi:type' => element.attribute("type").to_s}
|
150
|
+
else
|
151
|
+
new_struct[element.name] = element.text.to_s
|
152
|
+
attributes[element.name.to_sym] = {'xsi:type' => element.attribute("type").to_s}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
new_struct[:attributes!] = attributes
|
159
|
+
|
160
|
+
return new_struct
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.rep_blank_with_nil(value)
|
164
|
+
|
165
|
+
if value.kind_of? Struct
|
166
|
+
struct_param = Hash[value.each_pair.to_a]
|
167
|
+
|
168
|
+
struct_param.each do |k, v|
|
169
|
+
struct_param[k] = rep_blank_with_nil(v)
|
170
|
+
end
|
171
|
+
|
172
|
+
value = struct_param
|
173
|
+
|
174
|
+
else
|
175
|
+
unless value.nil?
|
176
|
+
if value.blank?
|
177
|
+
value = nil
|
178
|
+
end #if
|
179
|
+
end #unless
|
180
|
+
end #if
|
181
|
+
return value
|
182
|
+
end #def
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
#end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module TeamForge
|
3
|
+
module CategorizationApp
|
4
|
+
|
5
|
+
######################
|
6
|
+
# CategorizationApp Messages
|
7
|
+
######################
|
8
|
+
AddProjectToCategory = Struct.new(:session_id, :project_id, :category_id) do
|
9
|
+
def send (server=nil, proxy=nil)
|
10
|
+
TeamForge.request(self, server, proxy)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
CreateCategory = Struct.new(:session_id, :parent_id, :title, :description) do
|
14
|
+
def send (server=nil, proxy=nil)
|
15
|
+
TeamForge.request(self, server, proxy)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
DeleteCategory = Struct.new(:session_id, :category_id) do
|
19
|
+
def send (server=nil, proxy=nil)
|
20
|
+
TeamForge.request(self, server, proxy)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
GetAllCategories = Struct.new(:session_id) do
|
24
|
+
def send (server=nil, proxy=nil)
|
25
|
+
TeamForge.request(self, server, proxy)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
GetCategoryData = Struct.new(:session_id, :category_id) do
|
29
|
+
def send (server=nil, proxy=nil)
|
30
|
+
TeamForge.request(self, server, proxy)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
GetCategoryProjects = Struct.new(:session_id, :category_id, :include_subcategories) do
|
34
|
+
def send (server=nil, proxy=nil)
|
35
|
+
TeamForge.request(self, server, proxy)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
GetProjectCategories = Struct.new(:session_id, :project_id) do
|
39
|
+
def send (server=nil, proxy=nil)
|
40
|
+
TeamForge.request(self, server, proxy)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
GetRootCategoryData = Struct.new(:session_id) do
|
44
|
+
def send (server=nil, proxy=nil)
|
45
|
+
TeamForge.request(self, server, proxy)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
GetSubcategories = Struct.new(:session_id, :category_id, :recursive) do
|
49
|
+
def send (server=nil, proxy=nil)
|
50
|
+
TeamForge.request(self, server, proxy)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
GetUncategorizedProjects = Struct.new(:session_id) do
|
54
|
+
def send (server=nil, proxy=nil)
|
55
|
+
TeamForge.request(self, server, proxy)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
IsCategorizationEnabled = Struct.new(:session_id) do
|
59
|
+
def send (server=nil, proxy=nil)
|
60
|
+
TeamForge.request(self, server, proxy)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
MoveCategory = Struct.new(:session_id, :category_id, :dst_category_id) do
|
64
|
+
def send (server=nil, proxy=nil)
|
65
|
+
TeamForge.request(self, server, proxy)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
RemoveProjectFromCategory = Struct.new(:session_id, :project_id, :category_id) do
|
69
|
+
def send (server=nil, proxy=nil)
|
70
|
+
TeamForge.request(self, server, proxy)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
SetCategoryData = Struct.new(:session_id, :category_data) do
|
74
|
+
def send (server=nil, proxy=nil)
|
75
|
+
TeamForge.request(self, server, proxy)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
######################
|
81
|
+
# CategorizationApp Types
|
82
|
+
######################
|
83
|
+
|
84
|
+
CategorySoapDO = Struct.new(:created_by,:created_date,:description,:id,:last_modified_by,:last_modified_date,:parent_folder_id,:path,:project_id,:title,:version, :attributes!)
|
85
|
+
CategorySoapRow = Struct.new(:created_by,:created_on,:description,:id,:last_modified_by,:last_modified_on,:parent_folder_id,:path,:project_id,:title, :attributes!)
|
86
|
+
ProjectSoapRow = Struct.new(:date_created,:description,:hierarchy_path,:id,:locked,:parent_project_id,:path,:title, :attributes!)
|
87
|
+
|
88
|
+
end # module CategorizationApp
|
89
|
+
|
90
|
+
|
91
|
+
end # module TeamForge
|