jiraSOAP 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +2 -3
- data/ChangeLog +15 -0
- data/LICENSE.txt +1 -1
- data/README.markdown +51 -41
- data/Rakefile +57 -0
- data/docs/GettingStarted.markdown +36 -0
- data/lib/jiraSOAP.rb +7 -4
- data/lib/jiraSOAP/api.rb +59 -53
- data/lib/jiraSOAP/api/additions.rb +4 -6
- data/lib/jiraSOAP/api/attachments.rb +18 -8
- data/lib/jiraSOAP/api/avatars.rb +30 -17
- data/lib/jiraSOAP/api/comments.rb +16 -11
- data/lib/jiraSOAP/api/filters.rb +13 -10
- data/lib/jiraSOAP/api/issue_data_types.rb +29 -21
- data/lib/jiraSOAP/api/issues.rb +46 -31
- data/lib/jiraSOAP/api/project_roles.rb +22 -15
- data/lib/jiraSOAP/api/projects.rb +33 -20
- data/lib/jiraSOAP/api/schemes.rb +9 -9
- data/lib/jiraSOAP/api/server_info.rb +11 -9
- data/lib/jiraSOAP/api/users.rb +58 -10
- data/lib/jiraSOAP/api/versions.rb +15 -11
- data/lib/jiraSOAP/api/worklog.rb +14 -0
- data/lib/jiraSOAP/core_extensions.rb +8 -0
- data/lib/jiraSOAP/entities.rb +3 -0
- data/lib/jiraSOAP/entities/attachment_metadata.rb +28 -9
- data/lib/jiraSOAP/entities/avatar.rb +29 -10
- data/lib/jiraSOAP/entities/comment.rb +37 -11
- data/lib/jiraSOAP/entities/component.rb +1 -1
- data/lib/jiraSOAP/entities/custom_field_value.rb +17 -12
- data/lib/jiraSOAP/entities/described_entity.rb +9 -5
- data/lib/jiraSOAP/entities/dynamic_entity.rb +12 -5
- data/lib/jiraSOAP/entities/entity.rb +33 -27
- data/lib/jiraSOAP/entities/field.rb +1 -1
- data/lib/jiraSOAP/entities/field_value.rb +16 -6
- data/lib/jiraSOAP/entities/filter.rb +18 -5
- data/lib/jiraSOAP/entities/issue.rb +74 -29
- data/lib/jiraSOAP/entities/issue_property.rb +9 -6
- data/lib/jiraSOAP/entities/issue_security_scheme.rb +1 -1
- data/lib/jiraSOAP/entities/issue_type.rb +9 -4
- data/lib/jiraSOAP/entities/named_entity.rb +8 -4
- data/lib/jiraSOAP/entities/notification_scheme.rb +1 -1
- data/lib/jiraSOAP/entities/permission.rb +14 -6
- data/lib/jiraSOAP/entities/permission_mapping.rb +9 -5
- data/lib/jiraSOAP/entities/permission_scheme.rb +7 -4
- data/lib/jiraSOAP/entities/priority.rb +7 -4
- data/lib/jiraSOAP/entities/project.rb +29 -13
- data/lib/jiraSOAP/entities/project_role.rb +3 -2
- data/lib/jiraSOAP/entities/resolution.rb +1 -1
- data/lib/jiraSOAP/entities/scheme.rb +5 -1
- data/lib/jiraSOAP/entities/server_configuration.rb +39 -14
- data/lib/jiraSOAP/entities/server_info.rb +20 -8
- data/lib/jiraSOAP/entities/status.rb +1 -1
- data/lib/jiraSOAP/entities/time_info.rb +12 -5
- data/lib/jiraSOAP/entities/user.rb +8 -4
- data/lib/jiraSOAP/entities/usergroup.rb +15 -0
- data/lib/jiraSOAP/entities/username.rb +7 -1
- data/lib/jiraSOAP/entities/version.rb +20 -12
- data/lib/jiraSOAP/entities/worklog.rb +28 -0
- data/lib/jiraSOAP/handsoap_extensions.rb +31 -80
- data/lib/jiraSOAP/{JIRAservice.rb → jira_service.rb} +19 -14
- data/lib/jiraSOAP/{macruby_bonuses.rb → macruby_extensions.rb} +5 -14
- data/lib/jiraSOAP/nokogiri_extensions.rb +68 -0
- data/lib/jiraSOAP/url.rb +10 -2
- data/lib/jiraSOAP/version.rb +3 -0
- metadata +29 -55
- data/yard-jiraSOAP.rb +0 -65
@@ -1,49 +1,56 @@
|
|
1
|
-
module JIRA
|
2
|
-
|
3
|
-
# @group
|
1
|
+
module JIRA::RemoteAPI
|
2
|
+
|
3
|
+
# @group Project Roles
|
4
4
|
|
5
5
|
# @return [Array<JIRA::ProjectRole>]
|
6
|
-
def
|
7
|
-
|
6
|
+
def project_roles
|
7
|
+
array_jira_call JIRA::ProjectRole, 'getProjectRoles'
|
8
8
|
end
|
9
|
+
alias_method :get_project_roles, :project_roles
|
9
10
|
|
10
11
|
# @param [String] role_id
|
11
12
|
# @return [JIRA::ProjectRole]
|
12
|
-
def
|
13
|
-
JIRA::ProjectRole.new_with_xml
|
13
|
+
def project_role_with_id role_id
|
14
|
+
JIRA::ProjectRole.new_with_xml jira_call( 'getProjectRole', role_id )
|
14
15
|
end
|
16
|
+
alias_method :get_project_role_with_id, :project_role_with_id
|
15
17
|
|
16
18
|
# @param [JIRA::ProjectRole] project_role
|
17
19
|
# @return [JIRA::ProjectRole] the role that was created
|
18
20
|
def create_project_role_with_role project_role
|
19
|
-
JIRA::ProjectRole.new_with_xml
|
21
|
+
JIRA::ProjectRole.new_with_xml jira_call( 'createProjectRole', project_role )
|
20
22
|
end
|
21
23
|
|
24
|
+
##
|
22
25
|
# @note JIRA 4.0 and 4.2 returns an exception if the name already exists
|
26
|
+
#
|
23
27
|
# Returns true if the name does not exist.
|
28
|
+
#
|
24
29
|
# @param [String] project_role_name
|
25
30
|
# @return [Boolean] true if successful
|
26
31
|
def project_role_name_unique? project_role_name
|
27
|
-
|
32
|
+
jira_call( 'isProjectRoleNameUnique', project_role_name ).to_boolean
|
28
33
|
end
|
29
34
|
|
30
|
-
|
35
|
+
##
|
36
|
+
# @note The confirm argument appears to do nothing (at least on JIRA 4.0)
|
37
|
+
#
|
31
38
|
# @param [JIRA::ProjectRole] project_role
|
32
39
|
# @param [Boolean] confirm
|
33
40
|
# @return [Boolean] true if successful
|
34
41
|
def delete_project_role project_role, confirm = true
|
35
|
-
|
42
|
+
jira_call 'deleteProjectRole', project_role, confirm
|
36
43
|
true
|
37
44
|
end
|
38
45
|
|
46
|
+
##
|
39
47
|
# @note JIRA 4.0 will not update project roles, it will instead throw
|
40
|
-
#
|
48
|
+
# an exception telling you that the project role already exists
|
49
|
+
#
|
41
50
|
# @param [JIRA::ProjectRole] project_role
|
42
51
|
# @return [JIRA::ProjectRole] the role after the update
|
43
52
|
def update_project_role_with_role project_role
|
44
|
-
JIRA::ProjectRole.new_with_xml
|
53
|
+
JIRA::ProjectRole.new_with_xml jira_call( 'updateProjectRole', project_role )
|
45
54
|
end
|
46
55
|
|
47
|
-
# @endgroup
|
48
|
-
end
|
49
56
|
end
|
@@ -1,67 +1,80 @@
|
|
1
|
-
module JIRA
|
2
|
-
module RemoteAPI
|
3
|
-
# @group Working with Projects
|
1
|
+
module JIRA::RemoteAPI
|
4
2
|
|
3
|
+
# @group Projects
|
4
|
+
|
5
|
+
##
|
5
6
|
# You need to explicitly ask for schemes in order to get them. By
|
6
7
|
# default, most project fetching methods purposely leave out all
|
7
8
|
# the scheme information as permission schemes can be very large.
|
9
|
+
#
|
8
10
|
# @param [String] project_key
|
9
11
|
# @return [JIRA::Project]
|
10
|
-
def
|
11
|
-
JIRA::Project.new_with_xml
|
12
|
+
def project_with_key project_key
|
13
|
+
JIRA::Project.new_with_xml jira_call( 'getProjectByKey', project_key )
|
12
14
|
end
|
15
|
+
alias_method :get_project_with_key, :project_with_key
|
13
16
|
|
17
|
+
##
|
14
18
|
# Requires you to set at least a project name, key, and lead.
|
15
19
|
# However, it is also a good idea to set other project properties, such as
|
16
20
|
# the permission scheme as the default permission scheme can be too
|
17
21
|
# restrictive in most cases.
|
22
|
+
#
|
18
23
|
# @param [JIRA::Project] project
|
19
24
|
# @return [JIRA::Project]
|
20
25
|
def create_project_with_project project
|
21
|
-
JIRA::Project.new_with_xml
|
26
|
+
JIRA::Project.new_with_xml jira_call( 'createProjectFromObject', project )
|
22
27
|
end
|
23
28
|
|
29
|
+
##
|
24
30
|
# The id of the project is the only field that you cannot update. Or, at
|
25
31
|
# least the only field I know that you cannot update.
|
32
|
+
#
|
26
33
|
# @param [JIRA::Project] project
|
27
34
|
# @return [JIRA::Project]
|
28
35
|
def update_project_with_project project
|
29
|
-
JIRA::Project.new_with_xml
|
36
|
+
JIRA::Project.new_with_xml jira_call( 'updateProject', project )
|
30
37
|
end
|
31
38
|
|
32
39
|
# @param [String] project_id
|
33
40
|
# @return [JIRA::Project]
|
34
|
-
def
|
35
|
-
JIRA::Project.new_with_xml
|
41
|
+
def project_with_id project_id
|
42
|
+
JIRA::Project.new_with_xml jira_call( 'getProjectById', project_id )
|
36
43
|
end
|
44
|
+
alias_method :get_project_with_id, :project_with_id
|
37
45
|
|
38
|
-
|
46
|
+
##
|
47
|
+
# @todo Parse the permission scheme
|
39
48
|
# @note This method does not yet include the permission scheme.
|
49
|
+
#
|
40
50
|
# @param [String] project_id
|
41
51
|
# @return [JIRA::Project]
|
42
|
-
def
|
43
|
-
JIRA::Project.new_with_xml
|
52
|
+
def project_including_schemes_with_id project_id
|
53
|
+
JIRA::Project.new_with_xml jira_call( 'getProjectWithSchemesById', project_id )
|
44
54
|
end
|
55
|
+
alias_method :get_project_including_schemes_with_id, :project_including_schemes_with_id
|
45
56
|
|
46
57
|
# @param [String] project_name
|
47
58
|
# @return [Array<JIRA::IssueType>]
|
48
|
-
def
|
49
|
-
|
59
|
+
def issue_types_for_project_with_id project_id
|
60
|
+
array_jira_call JIRA::IssueType, 'getIssueTypesForProject', project_id
|
50
61
|
end
|
62
|
+
alias_method :get_issue_types_for_project_with_id, :issue_types_for_project_with_id
|
51
63
|
|
52
|
-
|
64
|
+
##
|
65
|
+
# @note This will not fill in {JIRA::Scheme} data for the projects.
|
66
|
+
#
|
53
67
|
# @return [Array<JIRA::Project>]
|
54
|
-
def
|
55
|
-
|
68
|
+
def projects_without_schemes
|
69
|
+
array_jira_call JIRA::Project, 'getProjectsNoSchemes'
|
56
70
|
end
|
71
|
+
alias_method :get_projects_without_schemes, :projects_without_schemes
|
57
72
|
|
58
73
|
# @param [String] project_key
|
59
74
|
# @return [Boolean] true if successful
|
60
75
|
def delete_project_with_key project_key
|
61
|
-
|
76
|
+
jira_call 'deleteProject', project_key
|
62
77
|
true
|
63
78
|
end
|
64
79
|
|
65
|
-
# @endgroup
|
66
|
-
end
|
67
80
|
end
|
data/lib/jiraSOAP/api/schemes.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
module JIRA
|
2
|
-
|
3
|
-
# @group
|
1
|
+
module JIRA::RemoteAPI
|
2
|
+
|
3
|
+
# @group Schemes
|
4
4
|
|
5
5
|
# @return [Array<JIRA::NotificationScheme>]
|
6
|
-
def
|
7
|
-
|
6
|
+
def notification_schemes
|
7
|
+
array_jira_call JIRA::NotificationScheme, 'getNotificationSchemes'
|
8
8
|
end
|
9
|
+
alias_method :get_notification_schemes, :notification_schemes
|
9
10
|
|
10
11
|
# @return [Array<JIRA::PermissionScheme>]
|
11
|
-
def
|
12
|
-
|
12
|
+
def permission_schemes
|
13
|
+
array_jira_call JIRA::PermissionScheme, 'getPermissionSchemes'
|
13
14
|
end
|
15
|
+
alias_method :get_permission_schemes, :permission_schemes
|
14
16
|
|
15
|
-
# @endgroup
|
16
|
-
end
|
17
17
|
end
|
@@ -1,18 +1,20 @@
|
|
1
|
-
module JIRA
|
2
|
-
module RemoteAPI
|
3
|
-
# @group Getting information about the server
|
1
|
+
module JIRA::RemoteAPI
|
4
2
|
|
3
|
+
# @group Server Information
|
4
|
+
|
5
|
+
##
|
5
6
|
# The @build_date attribute is a Time value, but does not include a time.
|
7
|
+
#
|
6
8
|
# @return [JIRA::ServerInfo]
|
7
|
-
def
|
8
|
-
JIRA::ServerInfo.new_with_xml
|
9
|
+
def server_info
|
10
|
+
JIRA::ServerInfo.new_with_xml jira_call( 'getServerInfo' )
|
9
11
|
end
|
12
|
+
alias_method :get_server_info, :server_info
|
10
13
|
|
11
14
|
# @return [JIRA::ServerConfiguration]
|
12
|
-
def
|
13
|
-
JIRA::ServerConfiguration.new_with_xml
|
15
|
+
def server_configuration
|
16
|
+
JIRA::ServerConfiguration.new_with_xml jira_call( 'getConfiguration' )
|
14
17
|
end
|
18
|
+
alias_method :get_server_configuration, :server_configuration
|
15
19
|
|
16
|
-
# @endgroup
|
17
|
-
end
|
18
20
|
end
|
data/lib/jiraSOAP/api/users.rb
CHANGED
@@ -1,35 +1,83 @@
|
|
1
|
-
module JIRA
|
2
|
-
|
3
|
-
# @group
|
1
|
+
module JIRA::RemoteAPI
|
2
|
+
|
3
|
+
# @group Users
|
4
4
|
|
5
5
|
# @param [String] user_name
|
6
6
|
# @return [JIRA::User]
|
7
|
-
def
|
8
|
-
JIRA::User.new_with_xml
|
7
|
+
def user_with_name user_name
|
8
|
+
JIRA::User.new_with_xml jira_call( 'getUser', user_name )
|
9
9
|
end
|
10
|
+
alias_method :get_user_with_name, :user_with_name
|
10
11
|
|
12
|
+
##
|
11
13
|
# It seems that creating a user without any permission groups will trigger
|
12
14
|
# an exception on some versions of JIRA. The irony is that this method provides
|
13
15
|
# no way to add groups. The good news though, is that the creation will still
|
14
16
|
# happen; but the user will have no permissions.
|
17
|
+
#
|
15
18
|
# @param [String] username
|
16
19
|
# @param [String] password
|
17
20
|
# @param [String] full_name
|
18
21
|
# @param [String] email
|
19
22
|
# @return [JIRA::User,nil] depending on your JIRA version, this method may
|
20
|
-
#
|
23
|
+
# always raise an exception instead of actually returning anything
|
21
24
|
def create_user username, password, full_name, email
|
22
|
-
fragment =
|
25
|
+
fragment = jira_call( 'createUser', username, password, full_name, email )
|
23
26
|
JIRA::User.new_with_xml fragment
|
24
27
|
end
|
25
28
|
|
26
29
|
# @param [String] username
|
27
30
|
# @return [Boolean] true if successful
|
28
31
|
def delete_user_with_name username
|
29
|
-
|
32
|
+
jira_call 'deleteUser', username
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [String] group_name
|
37
|
+
# @return [JIRA::UserGroup]
|
38
|
+
def group_with_name group_name
|
39
|
+
frag = jira_call 'getGroup', group_name
|
40
|
+
JIRA::UserGroup.new_with_xml frag
|
41
|
+
end
|
42
|
+
alias_method :get_group_with_name, :group_with_name
|
43
|
+
|
44
|
+
# @param [JIRA::UserGroup] group
|
45
|
+
# @param [JIRA::User] user
|
46
|
+
# @return [Boolean] true if successful
|
47
|
+
def add_user_to_group group, user
|
48
|
+
jira_call 'addUserToGroup', group, user
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [JIRA::UserGroup] group
|
53
|
+
# @param [JIRA::User] user
|
54
|
+
# @return [Boolean]
|
55
|
+
def remove_user_from_group group, user
|
56
|
+
jira_call 'removeUserFromGroup', group, user
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Create a new user group. You can initialize the group
|
62
|
+
# with a user if you wish.
|
63
|
+
#
|
64
|
+
# @param [String] group_name
|
65
|
+
# @param [JIRA::User] user
|
66
|
+
# @return [JIRA::UserGroup]
|
67
|
+
def create_user_group group_name, user = nil
|
68
|
+
frag = jira_call 'createGroup', group_name, user
|
69
|
+
JIRA::UserGroup.new_with_xml frag
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# @todo Find out the semantics of swap_group
|
74
|
+
#
|
75
|
+
# @param [String] group_name
|
76
|
+
# @param [String] swap_group
|
77
|
+
# @return [Boolean] true if successful
|
78
|
+
def delete_user_group group_name, swap_group
|
79
|
+
jira_call 'deleteGroup', group_name, swap_group
|
30
80
|
true
|
31
81
|
end
|
32
82
|
|
33
|
-
# @endgroup
|
34
|
-
end
|
35
83
|
end
|
@@ -1,13 +1,15 @@
|
|
1
|
-
module JIRA
|
2
|
-
|
3
|
-
# @group
|
1
|
+
module JIRA::RemoteAPI
|
2
|
+
|
3
|
+
# @group Versions
|
4
4
|
|
5
5
|
# @param [String] project_key
|
6
6
|
# @return [Array<JIRA::Version>]
|
7
|
-
def
|
8
|
-
|
7
|
+
def versions_for_project project_key
|
8
|
+
array_jira_call JIRA::Version, 'getVersions', project_key
|
9
9
|
end
|
10
|
+
alias_method :get_versions_for_project, :versions_for_project
|
10
11
|
|
12
|
+
##
|
11
13
|
# New versions cannot have the archived bit set and the release date
|
12
14
|
# field will ignore the time of day you give it and instead insert
|
13
15
|
# the time zone offset as the time of day.
|
@@ -17,34 +19,36 @@ module RemoteAPI
|
|
17
19
|
#
|
18
20
|
# Descriptions do not appear to be included with JIRA::Version objects
|
19
21
|
# that SOAP API provides.
|
22
|
+
#
|
20
23
|
# @param [String] project_key
|
21
24
|
# @param [JIRA::Version] version
|
22
25
|
# @return [JIRA::Version]
|
23
26
|
def add_version_to_project_with_key project_key, version
|
24
|
-
JIRA::Version.new_with_xml
|
27
|
+
JIRA::Version.new_with_xml jira_call( 'addVersion', project_key, version )
|
25
28
|
end
|
26
29
|
|
30
|
+
##
|
27
31
|
# The archive state can only be set to true for versions that have not been
|
28
32
|
# released. However, this is not reflected by the return value of this method.
|
33
|
+
#
|
29
34
|
# @param [String] project_key
|
30
35
|
# @param [String] version_name
|
31
36
|
# @param [Boolean] state
|
32
37
|
# @return [Boolean] true if successful
|
33
38
|
def set_archive_state_for_version_for_project project_key, version_name, state
|
34
|
-
|
39
|
+
jira_call 'archiveVersion', project_key, version_name, state
|
35
40
|
true
|
36
41
|
end
|
37
42
|
|
43
|
+
##
|
38
44
|
# You can set the release state for a project with this method.
|
45
|
+
#
|
39
46
|
# @param [String] project_name
|
40
47
|
# @param [JIRA::Version] version
|
41
48
|
# @return [Boolean] true if successful
|
42
49
|
def release_state_for_version_for_project project_name, version
|
43
|
-
|
50
|
+
jira_call 'releaseVersion', project_name, version
|
44
51
|
true
|
45
52
|
end
|
46
53
|
|
47
|
-
# @endgroup
|
48
|
-
end
|
49
54
|
end
|
50
|
-
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module JIRA::RemoteAPI
|
2
|
+
|
3
|
+
# @group Worklogs
|
4
|
+
|
5
|
+
##
|
6
|
+
# Adds a worklog to the given issue.
|
7
|
+
#
|
8
|
+
# @param [String] issue_key
|
9
|
+
# @param [JIRA::Worklog] worklog
|
10
|
+
def add_worklog_and_auto_adjust_remaining_estimate issue_key, worklog
|
11
|
+
JIRA::Worklog.new_with_xml call( 'addWorklogAndAutoAdjustRemainingEstimate', issue_key, worklog ).first
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/jiraSOAP/entities.rb
CHANGED
@@ -10,6 +10,7 @@ require 'jiraSOAP/entities/project_role'
|
|
10
10
|
|
11
11
|
require 'jiraSOAP/entities/username'
|
12
12
|
require 'jiraSOAP/entities/user'
|
13
|
+
require 'jiraSOAP/entities/usergroup'
|
13
14
|
|
14
15
|
require 'jiraSOAP/entities/field_value'
|
15
16
|
require 'jiraSOAP/entities/custom_field_value'
|
@@ -38,3 +39,5 @@ require 'jiraSOAP/entities/issue'
|
|
38
39
|
require 'jiraSOAP/entities/time_info'
|
39
40
|
require 'jiraSOAP/entities/server_info'
|
40
41
|
require 'jiraSOAP/entities/server_configuration'
|
42
|
+
|
43
|
+
require 'jiraSOAP/entities/worklog'
|
@@ -1,14 +1,33 @@
|
|
1
|
-
|
1
|
+
##
|
2
2
|
# Only contains the metadata for an attachment. The URI for an attachment
|
3
3
|
# appears to be of the form
|
4
4
|
# "{JIRA::JIRAService.endpoint_url}/secure/attachment/{#id}/{#file_name}"
|
5
5
|
class JIRA::AttachmentMetadata < JIRA::NamedEntity
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
|
7
|
+
# @return [String]
|
8
|
+
add_attribute :author, 'author', :content
|
9
|
+
|
10
|
+
# @return [String]
|
11
|
+
add_attribute :file_name, 'filename', :content
|
12
|
+
alias_method :filename, :file_name
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
add_attribute :mime_type, 'mimetype', :content
|
16
|
+
alias_method :content_type, :mime_type
|
17
|
+
|
18
|
+
##
|
19
|
+
# Measured in bytes
|
20
|
+
#
|
21
|
+
# @return [Number]
|
22
|
+
add_attribute :file_size, 'filesize', :to_i
|
23
|
+
|
24
|
+
# @return [Time]
|
25
|
+
add_attribute :create_time, 'created', :to_iso_date
|
26
|
+
|
27
|
+
##
|
28
|
+
# Fetch the attachment from the server.
|
29
|
+
def attachment
|
30
|
+
raise NotImplementedError, 'Please implement me. :('
|
31
|
+
end
|
32
|
+
|
14
33
|
end
|