groupdocs 0.1.0

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.
Files changed (129) hide show
  1. data/.travis.yml +5 -0
  2. data/Gemfile +3 -0
  3. data/README.md +141 -0
  4. data/Rakefile +9 -0
  5. data/groupdocs.gemspec +28 -0
  6. data/lib/groupdocs.rb +53 -0
  7. data/lib/groupdocs/api.rb +3 -0
  8. data/lib/groupdocs/api/entity.rb +113 -0
  9. data/lib/groupdocs/api/helpers.rb +5 -0
  10. data/lib/groupdocs/api/helpers/access_helper.rb +56 -0
  11. data/lib/groupdocs/api/helpers/actions_helper.rb +48 -0
  12. data/lib/groupdocs/api/helpers/rest_helper.rb +89 -0
  13. data/lib/groupdocs/api/helpers/status_helper.rb +48 -0
  14. data/lib/groupdocs/api/helpers/url_helper.rb +89 -0
  15. data/lib/groupdocs/api/request.rb +73 -0
  16. data/lib/groupdocs/api/sugar/lookup.rb +48 -0
  17. data/lib/groupdocs/datasource.rb +162 -0
  18. data/lib/groupdocs/datasource/field.rb +44 -0
  19. data/lib/groupdocs/document.rb +513 -0
  20. data/lib/groupdocs/document/annotation.rb +215 -0
  21. data/lib/groupdocs/document/annotation/reply.rb +167 -0
  22. data/lib/groupdocs/document/change.rb +35 -0
  23. data/lib/groupdocs/document/field.rb +27 -0
  24. data/lib/groupdocs/document/metadata.rb +26 -0
  25. data/lib/groupdocs/document/rectangle.rb +24 -0
  26. data/lib/groupdocs/document/view.rb +36 -0
  27. data/lib/groupdocs/errors.rb +10 -0
  28. data/lib/groupdocs/job.rb +201 -0
  29. data/lib/groupdocs/questionnaire.rb +221 -0
  30. data/lib/groupdocs/questionnaire/execution.rb +120 -0
  31. data/lib/groupdocs/questionnaire/page.rb +43 -0
  32. data/lib/groupdocs/questionnaire/question.rb +75 -0
  33. data/lib/groupdocs/questionnaire/question/answer.rb +10 -0
  34. data/lib/groupdocs/storage.rb +37 -0
  35. data/lib/groupdocs/storage/file.rb +248 -0
  36. data/lib/groupdocs/storage/folder.rb +314 -0
  37. data/lib/groupdocs/storage/package.rb +42 -0
  38. data/lib/groupdocs/user.rb +50 -0
  39. data/lib/groupdocs/version.rb +3 -0
  40. data/spec/groupdocs/api/entity_spec.rb +54 -0
  41. data/spec/groupdocs/api/helpers/access_helper_spec.rb +89 -0
  42. data/spec/groupdocs/api/helpers/actions_helper_spec.rb +51 -0
  43. data/spec/groupdocs/api/helpers/rest_helper_spec.rb +187 -0
  44. data/spec/groupdocs/api/helpers/status_helper_spec.rb +85 -0
  45. data/spec/groupdocs/api/helpers/url_helper_spec.rb +93 -0
  46. data/spec/groupdocs/api/request_spec.rb +85 -0
  47. data/spec/groupdocs/datasource/field_spec.rb +50 -0
  48. data/spec/groupdocs/datasource_spec.rb +156 -0
  49. data/spec/groupdocs/document/annotation/reply_spec.rb +179 -0
  50. data/spec/groupdocs/document/annotation_spec.rb +226 -0
  51. data/spec/groupdocs/document/change_spec.rb +35 -0
  52. data/spec/groupdocs/document/field_spec.rb +31 -0
  53. data/spec/groupdocs/document/metadata_spec.rb +26 -0
  54. data/spec/groupdocs/document/rectangle_spec.rb +34 -0
  55. data/spec/groupdocs/document/view_spec.rb +36 -0
  56. data/spec/groupdocs/document_spec.rb +509 -0
  57. data/spec/groupdocs/errors_spec.rb +7 -0
  58. data/spec/groupdocs/job_spec.rb +196 -0
  59. data/spec/groupdocs/questionnaire/execution_spec.rb +136 -0
  60. data/spec/groupdocs/questionnaire/page_spec.rb +50 -0
  61. data/spec/groupdocs/questionnaire/question/answer_spec.rb +11 -0
  62. data/spec/groupdocs/questionnaire/question_spec.rb +84 -0
  63. data/spec/groupdocs/questionnaire_spec.rb +217 -0
  64. data/spec/groupdocs/storage/file_spec.rb +242 -0
  65. data/spec/groupdocs/storage/folder_spec.rb +310 -0
  66. data/spec/groupdocs/storage/package_spec.rb +41 -0
  67. data/spec/groupdocs/storage_spec.rb +27 -0
  68. data/spec/groupdocs/user_spec.rb +53 -0
  69. data/spec/groupdocs_spec.rb +56 -0
  70. data/spec/spec_helper.rb +46 -0
  71. data/spec/support/files/resume.pdf +0 -0
  72. data/spec/support/json/annotation_collaborators_set.json +16 -0
  73. data/spec/support/json/annotation_create.json +12 -0
  74. data/spec/support/json/annotation_list.json +32 -0
  75. data/spec/support/json/annotation_remove.json +9 -0
  76. data/spec/support/json/annotation_replies_create.json +9 -0
  77. data/spec/support/json/annotation_replies_get.json +25 -0
  78. data/spec/support/json/comparison_changes.json +46 -0
  79. data/spec/support/json/comparison_compare.json +8 -0
  80. data/spec/support/json/comparison_document.json +10 -0
  81. data/spec/support/json/datasource_add.json +8 -0
  82. data/spec/support/json/datasource_get.json +22 -0
  83. data/spec/support/json/datasource_remove.json +8 -0
  84. data/spec/support/json/datasource_update.json +8 -0
  85. data/spec/support/json/document_access_info_get.json +14 -0
  86. data/spec/support/json/document_convert.json +8 -0
  87. data/spec/support/json/document_datasource.json +10 -0
  88. data/spec/support/json/document_fields.json +34 -0
  89. data/spec/support/json/document_formats.json +8 -0
  90. data/spec/support/json/document_metadata.json +15 -0
  91. data/spec/support/json/document_questionnaire_create.json +9 -0
  92. data/spec/support/json/document_questionnaires.json +23 -0
  93. data/spec/support/json/document_sharers_remove.json +8 -0
  94. data/spec/support/json/document_sharers_set.json +16 -0
  95. data/spec/support/json/document_views.json +32 -0
  96. data/spec/support/json/file_compress.json +8 -0
  97. data/spec/support/json/file_copy.json +14 -0
  98. data/spec/support/json/file_delete.json +5 -0
  99. data/spec/support/json/file_move.json +14 -0
  100. data/spec/support/json/file_upload.json +8 -0
  101. data/spec/support/json/folder_create.json +8 -0
  102. data/spec/support/json/folder_delete.json +5 -0
  103. data/spec/support/json/folder_list.json +21 -0
  104. data/spec/support/json/folder_move.json +8 -0
  105. data/spec/support/json/folder_sharers_get.json +16 -0
  106. data/spec/support/json/folder_sharers_remove.json +8 -0
  107. data/spec/support/json/folder_sharers_set.json +16 -0
  108. data/spec/support/json/job_add_url.json +8 -0
  109. data/spec/support/json/job_create.json +8 -0
  110. data/spec/support/json/job_documents.json +39 -0
  111. data/spec/support/json/job_file_add.json +8 -0
  112. data/spec/support/json/job_update.json +7 -0
  113. data/spec/support/json/jobs_get.json +52 -0
  114. data/spec/support/json/package_create.json +7 -0
  115. data/spec/support/json/questionnaire_create.json +8 -0
  116. data/spec/support/json/questionnaire_datasources.json +26 -0
  117. data/spec/support/json/questionnaire_execution_create.json +9 -0
  118. data/spec/support/json/questionnaire_execution_status_set.json +8 -0
  119. data/spec/support/json/questionnaire_execution_update.json +8 -0
  120. data/spec/support/json/questionnaire_executions.json +24 -0
  121. data/spec/support/json/questionnaire_get.json +14 -0
  122. data/spec/support/json/questionnaire_remove.json +8 -0
  123. data/spec/support/json/questionnaire_update.json +8 -0
  124. data/spec/support/json/questionnaires_get.json +22 -0
  125. data/spec/support/json/storage_info.json +10 -0
  126. data/spec/support/shared_examples/api/entity.rb +37 -0
  127. data/spec/support/shared_examples/api/helpers/status_helper.rb +12 -0
  128. data/spec/support/shared_examples/api/sugar/lookup.rb +57 -0
  129. metadata +356 -0
@@ -0,0 +1,89 @@
1
+ module GroupDocs
2
+ module Api
3
+ module Helpers
4
+ module REST
5
+
6
+ DEFAULT_HEADERS = {
7
+ accept: 'application/json',
8
+ content_length: 0
9
+ }
10
+
11
+ private
12
+
13
+ #
14
+ # Prepares headers, method and payload for request.
15
+ #
16
+ # @api private
17
+ #
18
+ def prepare_request
19
+ if options[:headers].is_a?(Hash)
20
+ options[:headers].merge!(DEFAULT_HEADERS)
21
+ else
22
+ options[:headers] = DEFAULT_HEADERS.dup
23
+ end
24
+
25
+ options[:method] = options[:method].downcase
26
+
27
+ if options[:request_body] && !options[:request_body].is_a?(Object::File)
28
+ options[:request_body] = options[:request_body].to_json
29
+ options[:headers][:content_type]= 'application/json'
30
+ options[:headers][:content_length] = options[:request_body].length
31
+ end
32
+ end
33
+
34
+ #
35
+ # Sends request to API server.
36
+ #
37
+ # @api private
38
+ #
39
+ def send_request
40
+ self.response = case options[:method]
41
+ when :get, :download
42
+ resource[options[:path]].get(options[:headers])
43
+ when :post
44
+ resource[options[:path]].post(options[:request_body], options[:headers])
45
+ when :put
46
+ resource[options[:path]].put(options[:request_body], options[:headers])
47
+ when :delete
48
+ resource[options[:path]].delete(options[:headers])
49
+ else
50
+ raise GroupDocs::Errors::UnsupportedMethodError, "Unsupported HTTP method: #{options[:method].inspect}"
51
+ end
52
+ end
53
+
54
+ #
55
+ # Parses response from API server.
56
+ #
57
+ # @api private
58
+ #
59
+ def parse_response
60
+ # for DOWNLOAD requests, just return response
61
+ if options[:method] == :download
62
+ response
63
+ # for all other requests, parse JSON
64
+ else
65
+ json = JSON.parse(response, symbolize_names: true)
66
+ json[:status] == 'Ok' ? json[:result] : raise_bad_request_error(json)
67
+ end
68
+ end
69
+
70
+ #
71
+ # @raise [GroupDocs::Errors::BadResponseError]
72
+ # @api private
73
+ #
74
+ def raise_bad_request_error(json)
75
+ raise GroupDocs::Errors::BadResponseError, <<-ERR
76
+ Bad response!
77
+ Request method: #{options[:method].upcase}
78
+ Request URL: #{resource[options[:path]]}
79
+ Request body: #{options[:request_body]}
80
+ Status: #{json[:status]}
81
+ Error message: #{json[:error_message]}
82
+ Response body: #{response}
83
+ ERR
84
+ end
85
+
86
+ end # Request
87
+ end # Helpers
88
+ end # Api
89
+ end # GroupDocs
@@ -0,0 +1,48 @@
1
+ module GroupDocs
2
+ module Api
3
+ module Helpers
4
+ module Status
5
+
6
+ STATUSES = {
7
+ draft: -1,
8
+ pending: 0,
9
+ scheduled: 1,
10
+ in_progress: 2,
11
+ completed: 3,
12
+ postponed: 4,
13
+ archived: 5,
14
+ }
15
+
16
+ # @attr [Symbol] status
17
+ attr_accessor :status
18
+
19
+ #
20
+ # Sets status of the entity.
21
+ #
22
+ # @return [Symbol]
23
+ #
24
+ def status
25
+ parse_status(@status)
26
+ end
27
+
28
+ private
29
+
30
+ #
31
+ # Converts status from/to human-readable format.
32
+ #
33
+ # @param [Integer, Symbol] status
34
+ # @return [Symbol, Integer]
35
+ # @api private
36
+ #
37
+ def parse_status(status)
38
+ if status.is_a?(Integer)
39
+ STATUSES.invert[status]
40
+ else
41
+ STATUSES[status]
42
+ end or raise ArgumentError, "Unknown status: #{status.inspect}."
43
+ end
44
+
45
+ end # Status
46
+ end # Helpers
47
+ end # Api
48
+ end # GroupDocs
@@ -0,0 +1,89 @@
1
+ require 'base64'
2
+ require 'hmac-sha1'
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ module GroupDocs
7
+ module Api
8
+ module Helpers
9
+ module URL
10
+
11
+ #
12
+ # Appends path with parameters.
13
+ #
14
+ # @param [Hash] params
15
+ #
16
+ def add_params(params)
17
+ params.each do |param, value|
18
+ value = value.join(?,) if value.is_a?(Array)
19
+ options[:path] << "#{separator}#{param}=#{value}"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ #
26
+ # Parses path replacing {client_id} with real one.
27
+ #
28
+ # @api private
29
+ #
30
+ def parse_path
31
+ options[:path] = options[:path].sub(/\{\{client_id\}\}/, client_id)
32
+ end
33
+
34
+ #
35
+ # URL encodes path.
36
+ #
37
+ # @api private
38
+ #
39
+ def url_encode_path
40
+ options[:path] = URI.escape(options[:path])
41
+ end
42
+
43
+ #
44
+ # Adds signature to path.
45
+ #
46
+ # @api private
47
+ #
48
+ def sign_url
49
+ # calculate a hash of the path with private key
50
+ hash = HMAC::SHA1.new(private_key)
51
+ hash << options[:path]
52
+ hash = hash.digest
53
+ # convert hash to base64
54
+ hash = Base64.strict_encode64(hash)
55
+ # remove trailing '='
56
+ hash = hash.gsub(/=*$/, '')
57
+ # URL encode hash
58
+ hash = CGI.escape(hash)
59
+ # covert all hexademical characters to upper case
60
+ hash = hash.gsub(/(%[A-Fa-f0-9]{1,2})/) { |group| group.upcase }
61
+
62
+ options[:path] << "#{separator}signature=#{hash}"
63
+ end
64
+
65
+ #
66
+ # Returns separator for GET parameters.
67
+ #
68
+ # @return [String] Either ? or &
69
+ # @api private
70
+ #
71
+ def separator
72
+ options[:path].include?('?') ? '&' : '?'
73
+ end
74
+
75
+ #
76
+ # Prepends path with version number if it's set.
77
+ #
78
+ # @api private
79
+ #
80
+ def prepend_version
81
+ if GroupDocs.api_version
82
+ options[:path] = "/v#{GroupDocs.api_version}#{options[:path]}"
83
+ end
84
+ end
85
+
86
+ end # Query
87
+ end # Helpers
88
+ end # Api
89
+ end # GroupDocs
@@ -0,0 +1,73 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+ require 'groupdocs/api/helpers'
4
+
5
+ module GroupDocs
6
+ module Api
7
+ class Request
8
+
9
+ include GroupDocs::Api::Helpers::Access
10
+ include GroupDocs::Api::Helpers::URL
11
+ include GroupDocs::Api::Helpers::REST
12
+
13
+ # @attr_reader [RestClient::Resource] resource Entry point for all API requests
14
+ attr_reader :resource
15
+ # @attr [String] response Response from server in JSON format
16
+ attr_accessor :response
17
+ # @attr [Hash] options Hash of options
18
+ attr_accessor :options
19
+ # @attr [Hash] access Hash of access credentials
20
+ attr_accessor :access
21
+
22
+ #
23
+ # Creates new API request.
24
+ #
25
+ # @example
26
+ # api = GroupDocs::Api::Request.new do |request|
27
+ # request[:method] = :POST
28
+ # request[:path] = "/storage/#{GroupDocs.client_id}/info"
29
+ # end
30
+ #
31
+ # @param [Hash] options
32
+ # @option options [Symbol] :method HTTP method. One of :GET, :DOWNLOAD, :POST, :PUT or :DELETE.
33
+ # @option options [String] :path Path to send request to
34
+ # @option options [Hash] :headers Additional HTTP headers
35
+ # @option options [Hash] :access Access credentials hash
36
+ # @option options [Hash, File] :request_body Payload. If hash, will be converted to JSON, if File, will be send as is.
37
+ #
38
+ # @yieldparam [Hash] options
39
+ #
40
+ def initialize(options = {}, &blk)
41
+ @options = options
42
+ yield @options if block_given?
43
+ @options[:access] ||= {}
44
+ @resource = RestClient::Resource.new(GroupDocs.api_server)
45
+ end
46
+
47
+ #
48
+ # Executes API request to server.
49
+ #
50
+ # It performs the following actions step by step:
51
+ # * Parses path (i.e. replaces client ID)
52
+ # * Prepends path with version if it's set
53
+ # * URL encodes path
54
+ # * Signs URL
55
+ # * Prepare request (add headers, converts payload to JSON, etc.)
56
+ # * Sends request to server
57
+ # * Parses response
58
+ #
59
+ # @return [Hash, String] Parsed response
60
+ #
61
+ def execute!
62
+ parse_path
63
+ prepend_version
64
+ url_encode_path
65
+ sign_url
66
+ prepare_request
67
+ send_request
68
+ parse_response
69
+ end
70
+
71
+ end # Request
72
+ end # Api
73
+ end # GroupDocs
@@ -0,0 +1,48 @@
1
+ module GroupDocs
2
+ module Api
3
+ module Sugar
4
+ module Lookup
5
+
6
+ #
7
+ # Returns first object matching given options.
8
+ #
9
+ # @param [Symbol] attribute
10
+ # @param [Integer, String, Regexp] value
11
+ # @param [Hash] access Access credentials
12
+ # @option access [String] :client_id
13
+ # @option access [String] :private_key
14
+ # @return [GroupDocs::Api::Entity] Matching entity
15
+ #
16
+ def find!(attribute, value, access = {})
17
+ find_all!(attribute, value, access).first
18
+ end
19
+
20
+ #
21
+ # Returns all objects matching given options.
22
+ #
23
+ # Each entity has to implement #all! method for this to work.
24
+ #
25
+ # @param [Symbol] attribute
26
+ # @param [Integer, String, Regexp] value
27
+ # @param [Hash] access Access credentials
28
+ # @option access [String] :client_id
29
+ # @option access [String] :private_key
30
+ # @return [Array] Array of matching entities
31
+ #
32
+ # @raise [NoMethodError] if extending class does not implement #all! class method.
33
+ #
34
+ def find_all!(attribute, value, access = {})
35
+ respond_to?(:all!) or raise NoMethodError, "#{self}.all! is not implemented - aborting."
36
+
37
+ all!('/', access).select do |object|
38
+ case value
39
+ when Regexp then object.send(attribute) =~ value
40
+ else object.send(attribute) == value
41
+ end
42
+ end
43
+ end
44
+
45
+ end # Lookup
46
+ end # Sugar
47
+ end # Api
48
+ end # GroupDocs
@@ -0,0 +1,162 @@
1
+ module GroupDocs
2
+ class DataSource < GroupDocs::Api::Entity
3
+
4
+ require 'groupdocs/datasource/field'
5
+
6
+ #
7
+ # Returns datasource by given identifier.
8
+ #
9
+ # @param [Integer] id
10
+ # @param [Hash] options
11
+ # @option options [Array] :field Array of field names to be returned. All by default
12
+ # @param [Hash] access Access credentials
13
+ # @option access [String] :client_id
14
+ # @option access [String] :private_key
15
+ # @return [GroupDocs::DataSource, nil]
16
+ #
17
+ def self.get!(id, options = {}, access = {})
18
+ api = GroupDocs::Api::Request.new do |request|
19
+ request[:access] = access
20
+ request[:method] = :GET
21
+ request[:path] = "/merge/{{client_id}}/datasources/#{id}"
22
+ end
23
+ api.add_params(options)
24
+ json = api.execute!
25
+
26
+ GroupDocs::DataSource.new(json[:datasource])
27
+ rescue GroupDocs::Errors::BadResponseError
28
+ nil
29
+ end
30
+
31
+ # @attr [Integer] id
32
+ attr_accessor :id
33
+ # @attr [String] descr
34
+ attr_accessor :descr
35
+ # @attr [Integer] questionnaire_id
36
+ attr_accessor :questionnaire_id
37
+ # @attr [Time] created_on
38
+ attr_accessor :created_on
39
+ # @attr [Time] modified_on
40
+ attr_accessor :modified_on
41
+ # @attr [Array<GroupDocs::DataSource::Field>] fields
42
+ attr_accessor :fields
43
+
44
+ # Human-readable accessors
45
+ alias_method :description, :descr
46
+ alias_method :description=, :descr=
47
+
48
+ #
49
+ # Converts timestamp which is return by API server to Time object.
50
+ #
51
+ # @return [Time]
52
+ #
53
+ def created_on
54
+ Time.at(@created_on)
55
+ end
56
+
57
+ #
58
+ # Converts timestamp which is return by API server to Time object.
59
+ #
60
+ # @return [Time]
61
+ #
62
+ def modified_on
63
+ Time.at(@modified_on)
64
+ end
65
+
66
+ #
67
+ # Converts each field to GroupDocs::DataSource::Field object.
68
+ #
69
+ # @param [Array<GroupDocs::DataSource::Field, Hash>] fields
70
+ #
71
+ def fields=(fields)
72
+ if fields
73
+ @fields = fields.map do |field|
74
+ if field.is_a?(GroupDocs::DataSource::Field)
75
+ field
76
+ else
77
+ GroupDocs::DataSource::Field.new(field)
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ #
84
+ # Adds field to datasource.
85
+ #
86
+ # @param [GroupDocs::DataSource::Field] question
87
+ # @raise [ArgumentError] if field is not GroupDocs::DataSource::Field object
88
+ #
89
+ def add_field(field)
90
+ field.is_a?(GroupDocs::DataSource::Field) or raise ArgumentError,
91
+ "Field should be GroupDocs::DataSource::Field object, received: #{field.inspect}"
92
+
93
+ @fields ||= Array.new
94
+ @fields << field
95
+ end
96
+
97
+ #
98
+ # Adds datasource.
99
+ #
100
+ # @example
101
+ # field = GroupDocs::DataSource::Field.new(field: 'test', values: %w(test test))
102
+ # datasource = GroupDocs::DataSource.new
103
+ # datasource.add_field(field)
104
+ # datasource.add!
105
+ #
106
+ # @param [Hash] access Access credentials
107
+ # @option access [String] :client_id
108
+ # @option access [String] :private_key
109
+ #
110
+ def add!(access = {})
111
+ json = GroupDocs::Api::Request.new do |request|
112
+ request[:access] = access
113
+ request[:method] = :POST
114
+ request[:path] = '/merge/{{client_id}}/datasources'
115
+ request[:request_body] = to_hash
116
+ end.execute!
117
+
118
+ self.id = json[:datasource_id]
119
+ end
120
+
121
+ #
122
+ # Updates datasource.
123
+ #
124
+ # @example
125
+ # field = GroupDocs::DataSource::Field.new(field: 'test', values: %w(test test))
126
+ # datasource = GroupDocs::DataSource.get!(180)
127
+ # datasource.add_field(field)
128
+ # datasource.update!
129
+ #
130
+ # @param [Hash] access Access credentials
131
+ # @option access [String] :client_id
132
+ # @option access [String] :private_key
133
+ #
134
+ def update!(access = {})
135
+ GroupDocs::Api::Request.new do |request|
136
+ request[:access] = access
137
+ request[:method] = :PUT
138
+ request[:path] = "/merge/{{client_id}}/datasources/#{id}"
139
+ request[:request_body] = to_hash
140
+ end.execute!
141
+ end
142
+
143
+ #
144
+ # Removes datasource.
145
+ #
146
+ # @param [Hash] access Access credentials
147
+ # @option access [String] :client_id
148
+ # @option access [String] :private_key
149
+ #
150
+ def remove!(access = {})
151
+ GroupDocs::Api::Request.new do |request|
152
+ request[:access] = access
153
+ request[:method] = :DELETE
154
+ request[:path] = "/merge/{{client_id}}/datasources/#{id}"
155
+ end.execute!
156
+ # TODO: fix this in API
157
+ rescue RestClient::BadRequest
158
+ nil
159
+ end
160
+
161
+ end # DataSource
162
+ end # GroupDocs