resourcespace-ruby 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.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +355 -0
- data/Rakefile +6 -0
- data/lib/resourcespace/client.rb +245 -0
- data/lib/resourcespace/collection.rb +211 -0
- data/lib/resourcespace/configuration.rb +127 -0
- data/lib/resourcespace/errors.rb +111 -0
- data/lib/resourcespace/metadata.rb +298 -0
- data/lib/resourcespace/resource.rb +285 -0
- data/lib/resourcespace/search.rb +259 -0
- data/lib/resourcespace/user.rb +188 -0
- data/lib/resourcespace/version.rb +6 -0
- data/lib/resourcespace.rb +71 -0
- data/resourcespace-ruby.gemspec +49 -0
- metadata +247 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 12ee38b0f2c62de30514d7c97cb16c75869d80b3887e13b6c9a662f949bfbea6
|
4
|
+
data.tar.gz: ee59831391f451cb97fe9e4e796349cc0f4fdd4cb67b27ad09fbf82da976e2b2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf1a67fa696a6535f9743649f485230129bdf36ffaeaeddea73461bdc9202b9ca5dc8ec93d73f5e3f00aef7193b4bbb565a6264e1ca4f0a06c3c907be9692031
|
7
|
+
data.tar.gz: 0314fdd3b2d5f6c03a98f9bc121855789efe99c3032d4851844edbd783a45dd26e74cf185c5a8b52c5f99e93d14393ffa2c42051d6d5e7a8b862772ba436947f
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-3.4.4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.1.0] - 2024-03-21
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Initial release of BrowserMCP wrapper
|
12
|
+
- Basic browser automation functionality
|
13
|
+
- Navigation (navigate_to, go_back, go_forward)
|
14
|
+
- Element interaction (click, hover, type, select_option)
|
15
|
+
- Element properties (text, value, attributes, state)
|
16
|
+
- Page state management
|
17
|
+
- Screenshot and console log capture
|
18
|
+
- JavaScript evaluation
|
19
|
+
- Robust error handling with automatic retries
|
20
|
+
- Comprehensive configuration options
|
21
|
+
- Integration tests
|
22
|
+
- Documentation
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Your Name
|
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,355 @@
|
|
1
|
+
# ResourceSpace Ruby Client
|
2
|
+
|
3
|
+
A comprehensive Ruby client library for the [ResourceSpace](https://www.resourcespace.com/) open-source Digital Asset Management system. This gem provides an easy-to-use interface for managing web assets including images, CSS files, JavaScript files, fonts, and other digital resources.
|
4
|
+
|
5
|
+
[](https://badge.fury.io/rb/resourcespace-ruby)
|
6
|
+
[](https://github.com/survey-flunkie/resourcespace-ruby/actions)
|
7
|
+
[](https://rubydoc.info/gems/resourcespace-ruby)
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
- **Complete API Coverage**: Supports all major ResourceSpace API endpoints
|
12
|
+
- **Web Asset Focused**: Optimized for managing web development assets (images, CSS, JS, fonts, icons)
|
13
|
+
- **Authentication**: Secure SHA256 signature-based authentication
|
14
|
+
- **File Operations**: Upload, download, and manage files with ease
|
15
|
+
- **Search & Collections**: Powerful search capabilities and collection management
|
16
|
+
- **Metadata Management**: Comprehensive metadata and field management
|
17
|
+
- **Error Handling**: Robust error handling with specific exception types
|
18
|
+
- **Configurable**: Flexible configuration options with global and instance-level settings
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'resourcespace-ruby'
|
26
|
+
```
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ bundle install
|
32
|
+
```
|
33
|
+
|
34
|
+
Or install it yourself as:
|
35
|
+
|
36
|
+
```bash
|
37
|
+
$ gem install resourcespace-ruby
|
38
|
+
```
|
39
|
+
|
40
|
+
## Quick Start
|
41
|
+
|
42
|
+
### Basic Configuration
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'resourcespace'
|
46
|
+
|
47
|
+
# Configure globally
|
48
|
+
ResourceSpace.configure do |config|
|
49
|
+
config.url = "https://your-resourcespace.com/api/"
|
50
|
+
config.user = "your_username"
|
51
|
+
config.private_key = "your_private_key" # Get this from your ResourceSpace profile
|
52
|
+
config.timeout = 30
|
53
|
+
end
|
54
|
+
|
55
|
+
# Or configure per instance
|
56
|
+
client = ResourceSpace::Client.new(
|
57
|
+
url: "https://your-resourcespace.com/api/",
|
58
|
+
user: "your_username",
|
59
|
+
private_key: "your_private_key"
|
60
|
+
)
|
61
|
+
```
|
62
|
+
|
63
|
+
### Basic Usage
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# Test connection
|
67
|
+
status = client.test_connection
|
68
|
+
puts "Connected to ResourceSpace #{status['version']}"
|
69
|
+
|
70
|
+
# Search for web assets
|
71
|
+
results = client.search.search_web_assets("images")
|
72
|
+
puts "Found #{results.length} image assets"
|
73
|
+
|
74
|
+
# Upload a web asset
|
75
|
+
uploaded = client.resources.upload_file(
|
76
|
+
File.open("assets/logo.png"),
|
77
|
+
caption: "Company Logo"
|
78
|
+
)
|
79
|
+
|
80
|
+
# Create a collection for web assets
|
81
|
+
collection = client.collections.create_web_asset_collection(
|
82
|
+
"Website Assets",
|
83
|
+
asset_type: "images"
|
84
|
+
)
|
85
|
+
|
86
|
+
# Add resource to collection
|
87
|
+
client.collections.add_resource_to_collection(uploaded['ref'], collection['ref'])
|
88
|
+
```
|
89
|
+
|
90
|
+
## Web Asset Management
|
91
|
+
|
92
|
+
This gem is specifically designed to work well with web development assets:
|
93
|
+
|
94
|
+
### Upload Web Assets
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
# Upload different types of web assets
|
98
|
+
logo = client.resources.upload_file("assets/logo.png")
|
99
|
+
stylesheet = client.resources.upload_file("assets/main.css")
|
100
|
+
script = client.resources.upload_file("assets/app.js")
|
101
|
+
font = client.resources.upload_file("assets/custom-font.woff2")
|
102
|
+
```
|
103
|
+
|
104
|
+
### Search for Specific Asset Types
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
# Search by asset type
|
108
|
+
images = client.search.search_web_assets("images")
|
109
|
+
css_files = client.search.search_web_assets("css")
|
110
|
+
js_files = client.search.search_web_assets("javascript")
|
111
|
+
fonts = client.search.search_web_assets("fonts")
|
112
|
+
|
113
|
+
# Search by file extension
|
114
|
+
svg_files = client.search.search_by_extension(["svg"])
|
115
|
+
web_fonts = client.search.search_by_extension(["woff", "woff2", "ttf"])
|
116
|
+
```
|
117
|
+
|
118
|
+
### Organize with Collections
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
# Create collections for different asset types
|
122
|
+
image_collection = client.collections.create_web_asset_collection(
|
123
|
+
"Website Images",
|
124
|
+
asset_type: "images"
|
125
|
+
)
|
126
|
+
|
127
|
+
css_collection = client.collections.create_web_asset_collection(
|
128
|
+
"Stylesheets",
|
129
|
+
asset_type: "css"
|
130
|
+
)
|
131
|
+
|
132
|
+
# Get all web asset collections
|
133
|
+
web_collections = client.collections.get_web_asset_collections
|
134
|
+
```
|
135
|
+
|
136
|
+
### Manage Web Asset Metadata
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
# Set up web asset metadata fields (one-time setup)
|
140
|
+
client.metadata.create_web_asset_fields
|
141
|
+
|
142
|
+
# Update resource with web asset metadata
|
143
|
+
client.metadata.update_web_asset_metadata(resource_id, {
|
144
|
+
title: "Hero Background Image",
|
145
|
+
asset_type: "Image",
|
146
|
+
dimensions: "1920x1080",
|
147
|
+
usage_rights: "Creative Commons",
|
148
|
+
purpose: "Website header background"
|
149
|
+
})
|
150
|
+
```
|
151
|
+
|
152
|
+
## Advanced Usage
|
153
|
+
|
154
|
+
### Resource Management
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
# Create a new resource
|
158
|
+
resource = client.resources.create_resource(
|
159
|
+
name: "Company Logo",
|
160
|
+
resource_type: 1,
|
161
|
+
metadata: {
|
162
|
+
12 => "logo, branding, company", # Keywords field
|
163
|
+
51 => "Image" # Custom asset type field
|
164
|
+
}
|
165
|
+
)
|
166
|
+
|
167
|
+
# Get resource details
|
168
|
+
details = client.resources.get_resource_data(resource_id)
|
169
|
+
|
170
|
+
# Update resource metadata
|
171
|
+
client.resources.update_field(resource_id, 8, "New Title")
|
172
|
+
|
173
|
+
# Download resource
|
174
|
+
client.resources.download_resource(resource_id, "/local/path/file.jpg")
|
175
|
+
|
176
|
+
# Get alternative files
|
177
|
+
alternatives = client.resources.get_alternative_files(resource_id)
|
178
|
+
```
|
179
|
+
|
180
|
+
### Advanced Search
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
# Advanced search with multiple criteria
|
184
|
+
results = client.search.advanced_search({
|
185
|
+
title: "logo",
|
186
|
+
extensions: ["png", "svg"],
|
187
|
+
from_date: "2023-01-01",
|
188
|
+
to_date: "2023-12-31"
|
189
|
+
}, {
|
190
|
+
order_by: "date",
|
191
|
+
sort: "desc",
|
192
|
+
fetchrows: 20
|
193
|
+
})
|
194
|
+
|
195
|
+
# Search by date range
|
196
|
+
recent = client.search.search_by_date_range("2023-01-01", "2023-12-31")
|
197
|
+
|
198
|
+
# Get recently added resources
|
199
|
+
latest = client.search.recent_resources(10)
|
200
|
+
```
|
201
|
+
|
202
|
+
### Collection Management
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
# Create collection
|
206
|
+
collection = client.collections.create_collection(
|
207
|
+
"Marketing Assets",
|
208
|
+
public: false,
|
209
|
+
allow_changes: true
|
210
|
+
)
|
211
|
+
|
212
|
+
# Add multiple resources
|
213
|
+
resource_ids = [123, 124, 125]
|
214
|
+
client.collections.add_resources_to_collection(resource_ids, collection_id)
|
215
|
+
|
216
|
+
# Search public collections
|
217
|
+
public_collections = client.collections.search_public_collections("web")
|
218
|
+
```
|
219
|
+
|
220
|
+
### User & Permissions
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
# Check user capabilities
|
224
|
+
capabilities = client.users.capabilities
|
225
|
+
puts "Can upload: #{capabilities[:upload]}"
|
226
|
+
puts "Can edit: #{capabilities[:edit_resources]}"
|
227
|
+
|
228
|
+
# Check specific permissions
|
229
|
+
can_download = client.users.can_download?
|
230
|
+
has_admin = client.users.admin?
|
231
|
+
|
232
|
+
# Check resource-specific permissions
|
233
|
+
can_edit_resource = client.users.can_edit_resource?(resource_id)
|
234
|
+
```
|
235
|
+
|
236
|
+
## Configuration Options
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
ResourceSpace.configure do |config|
|
240
|
+
config.url = "https://your-resourcespace.com/api/" # Required
|
241
|
+
config.user = "username" # Required
|
242
|
+
config.private_key = "your_private_key" # Required
|
243
|
+
config.timeout = 30 # Request timeout (seconds)
|
244
|
+
config.retries = 3 # Number of retry attempts
|
245
|
+
config.verify_ssl = true # Verify SSL certificates
|
246
|
+
config.auth_mode = "userkey" # Authentication mode
|
247
|
+
config.debug = false # Enable debug logging
|
248
|
+
config.logger = Logger.new(STDOUT) # Custom logger
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
## Error Handling
|
253
|
+
|
254
|
+
The gem provides specific exception types for different error conditions:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
begin
|
258
|
+
resource = client.resources.get_resource_data(999)
|
259
|
+
rescue ResourceSpace::NotFoundError => e
|
260
|
+
puts "Resource not found: #{e.message}"
|
261
|
+
rescue ResourceSpace::AuthenticationError => e
|
262
|
+
puts "Authentication failed: #{e.message}"
|
263
|
+
rescue ResourceSpace::AuthorizationError => e
|
264
|
+
puts "Access denied: #{e.message}"
|
265
|
+
rescue ResourceSpace::ValidationError => e
|
266
|
+
puts "Invalid data: #{e.message}"
|
267
|
+
rescue ResourceSpace::ServerError => e
|
268
|
+
puts "Server error: #{e.message}"
|
269
|
+
rescue ResourceSpace::NetworkError => e
|
270
|
+
puts "Network error: #{e.message}"
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
## Rails Integration
|
275
|
+
|
276
|
+
For Rails applications, add an initializer:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
# config/initializers/resourcespace.rb
|
280
|
+
ResourceSpace.configure do |config|
|
281
|
+
config.url = ENV['RESOURCESPACE_URL']
|
282
|
+
config.user = ENV['RESOURCESPACE_USER']
|
283
|
+
config.private_key = ENV['RESOURCESPACE_PRIVATE_KEY']
|
284
|
+
config.timeout = 30
|
285
|
+
end
|
286
|
+
```
|
287
|
+
|
288
|
+
Then use in your application:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
class AssetsController < ApplicationController
|
292
|
+
def upload
|
293
|
+
client = ResourceSpace::Client.new
|
294
|
+
uploaded = client.resources.upload_file(params[:file])
|
295
|
+
|
296
|
+
# Store reference in your model
|
297
|
+
@asset = Asset.create(
|
298
|
+
name: uploaded['title'],
|
299
|
+
resourcespace_id: uploaded['ref'],
|
300
|
+
file_type: uploaded['file_extension']
|
301
|
+
)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
```
|
305
|
+
|
306
|
+
## Testing
|
307
|
+
|
308
|
+
Run the test suite:
|
309
|
+
|
310
|
+
```bash
|
311
|
+
$ bundle exec rspec
|
312
|
+
```
|
313
|
+
|
314
|
+
Run tests with coverage:
|
315
|
+
|
316
|
+
```bash
|
317
|
+
$ COVERAGE=1 bundle exec rspec
|
318
|
+
```
|
319
|
+
|
320
|
+
## Development
|
321
|
+
|
322
|
+
After checking out the repo, run:
|
323
|
+
|
324
|
+
```bash
|
325
|
+
$ bin/setup
|
326
|
+
$ bundle exec rake spec
|
327
|
+
```
|
328
|
+
|
329
|
+
To install this gem onto your local machine:
|
330
|
+
|
331
|
+
```bash
|
332
|
+
$ bundle exec rake install
|
333
|
+
```
|
334
|
+
|
335
|
+
## Contributing
|
336
|
+
|
337
|
+
1. Fork it
|
338
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
339
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
340
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
341
|
+
5. Create new Pull Request
|
342
|
+
|
343
|
+
## API Reference
|
344
|
+
|
345
|
+
For complete API documentation, see the [ResourceSpace API documentation](https://www.resourcespace.com/knowledge-base/api/).
|
346
|
+
|
347
|
+
## License
|
348
|
+
|
349
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
350
|
+
|
351
|
+
## Support
|
352
|
+
|
353
|
+
- Documentation: [https://rubydoc.info/gems/resourcespace-ruby](https://rubydoc.info/gems/resourcespace-ruby)
|
354
|
+
- Issues: [https://github.com/survey-flunkie/resourcespace-ruby/issues](https://github.com/survey-flunkie/resourcespace-ruby/issues)
|
355
|
+
- ResourceSpace Documentation: [https://www.resourcespace.com/knowledge-base/](https://www.resourcespace.com/knowledge-base/)
|
data/Rakefile
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday/multipart"
|
5
|
+
require "json"
|
6
|
+
require "digest"
|
7
|
+
require "uri"
|
8
|
+
|
9
|
+
module ResourceSpace
|
10
|
+
# Main client class for interacting with ResourceSpace API
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# client = ResourceSpace::Client.new(
|
14
|
+
# url: "https://your-resourcespace.com/api/",
|
15
|
+
# user: "your_username",
|
16
|
+
# private_key: "your_private_key"
|
17
|
+
# )
|
18
|
+
class Client
|
19
|
+
# @return [Configuration] client configuration
|
20
|
+
attr_reader :config
|
21
|
+
|
22
|
+
# @return [Resource] resource management interface
|
23
|
+
attr_reader :resources
|
24
|
+
|
25
|
+
# @return [Collection] collection management interface
|
26
|
+
attr_reader :collections
|
27
|
+
|
28
|
+
# @return [Search] search interface
|
29
|
+
attr_reader :search
|
30
|
+
|
31
|
+
# @return [User] user management interface
|
32
|
+
attr_reader :users
|
33
|
+
|
34
|
+
# @return [Metadata] metadata management interface
|
35
|
+
attr_reader :metadata
|
36
|
+
|
37
|
+
# Initialize a new ResourceSpace client
|
38
|
+
#
|
39
|
+
# @param url [String] ResourceSpace API URL
|
40
|
+
# @param user [String] ResourceSpace username
|
41
|
+
# @param private_key [String] ResourceSpace private API key
|
42
|
+
# @param config [Configuration] configuration object
|
43
|
+
# @param options [Hash] additional configuration options
|
44
|
+
def initialize(url: nil, user: nil, private_key: nil, config: nil, **options)
|
45
|
+
@config = config || ResourceSpace.config.dup
|
46
|
+
|
47
|
+
# Set configuration from parameters
|
48
|
+
@config.url = url if url
|
49
|
+
@config.user = user if user
|
50
|
+
@config.private_key = private_key if private_key
|
51
|
+
|
52
|
+
# Apply additional options
|
53
|
+
options.each { |key, value| @config.public_send("#{key}=", value) if @config.respond_to?("#{key}=") }
|
54
|
+
|
55
|
+
# Validate configuration
|
56
|
+
@config.validate!
|
57
|
+
|
58
|
+
# Initialize API interfaces
|
59
|
+
@resources = Resource.new(self)
|
60
|
+
@collections = Collection.new(self)
|
61
|
+
@search = Search.new(self)
|
62
|
+
@users = User.new(self)
|
63
|
+
@metadata = Metadata.new(self)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Make a GET request to the ResourceSpace API
|
67
|
+
#
|
68
|
+
# @param function [String] API function name
|
69
|
+
# @param params [Hash] request parameters
|
70
|
+
# @return [Hash] parsed JSON response
|
71
|
+
def get(function, params = {})
|
72
|
+
request(:get, function, params)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Make a POST request to the ResourceSpace API
|
76
|
+
#
|
77
|
+
# @param function [String] API function name
|
78
|
+
# @param params [Hash] request parameters
|
79
|
+
# @return [Hash] parsed JSON response
|
80
|
+
def post(function, params = {})
|
81
|
+
request(:post, function, params)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Upload a file to ResourceSpace
|
85
|
+
#
|
86
|
+
# @param file [File, String] file object or file path
|
87
|
+
# @param params [Hash] additional parameters
|
88
|
+
# @return [Hash] parsed JSON response
|
89
|
+
def upload_file(file, params = {})
|
90
|
+
file_param = if file.is_a?(String)
|
91
|
+
Faraday::UploadIO.new(file, mime_type_for_file(file))
|
92
|
+
else
|
93
|
+
Faraday::UploadIO.new(file, mime_type_for_file(file.path))
|
94
|
+
end
|
95
|
+
|
96
|
+
params = params.merge(filedata: file_param)
|
97
|
+
request(:post, "upload_file", params, multipart: true)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Download a file from ResourceSpace
|
101
|
+
#
|
102
|
+
# @param download_url [String] download URL
|
103
|
+
# @param file_path [String] local file path to save to
|
104
|
+
# @return [Boolean] true if successful
|
105
|
+
def download_file(download_url, file_path)
|
106
|
+
response = connection.get(download_url)
|
107
|
+
|
108
|
+
if response.success?
|
109
|
+
File.write(file_path, response.body)
|
110
|
+
true
|
111
|
+
else
|
112
|
+
handle_error_response(response)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Test the API connection
|
117
|
+
#
|
118
|
+
# @return [Hash] system status information
|
119
|
+
def test_connection
|
120
|
+
get("get_system_status")
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Make an HTTP request to the ResourceSpace API
|
126
|
+
#
|
127
|
+
# @param method [Symbol] HTTP method (:get or :post)
|
128
|
+
# @param function [String] API function name
|
129
|
+
# @param params [Hash] request parameters
|
130
|
+
# @param multipart [Boolean] whether to use multipart encoding
|
131
|
+
# @return [Hash] parsed JSON response
|
132
|
+
def request(method, function, params = {}, multipart: false)
|
133
|
+
# Prepare base parameters
|
134
|
+
request_params = {
|
135
|
+
user: config.user,
|
136
|
+
function: function
|
137
|
+
}.merge(params)
|
138
|
+
|
139
|
+
# Build query string for signing
|
140
|
+
query_string = URI.encode_www_form(request_params.reject { |_k, v| v.is_a?(Faraday::UploadIO) })
|
141
|
+
|
142
|
+
# Generate signature
|
143
|
+
signature = generate_signature(query_string)
|
144
|
+
request_params[:sign] = signature
|
145
|
+
request_params[:authmode] = config.auth_mode
|
146
|
+
|
147
|
+
# Make the request
|
148
|
+
response = if method == :get
|
149
|
+
connection.get("", request_params)
|
150
|
+
else
|
151
|
+
if multipart
|
152
|
+
connection.post("", request_params)
|
153
|
+
else
|
154
|
+
connection.post("", URI.encode_www_form(request_params))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
handle_response(response)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Generate SHA256 signature for API authentication
|
162
|
+
#
|
163
|
+
# @param query_string [String] URL-encoded query string
|
164
|
+
# @return [String] SHA256 hexadecimal signature
|
165
|
+
def generate_signature(query_string)
|
166
|
+
Digest::SHA256.hexdigest("#{config.private_key}#{query_string}")
|
167
|
+
end
|
168
|
+
|
169
|
+
# Get the Faraday connection instance
|
170
|
+
#
|
171
|
+
# @return [Faraday::Connection] configured connection
|
172
|
+
def connection
|
173
|
+
@connection ||= Faraday.new(url: config.url) do |conn|
|
174
|
+
conn.request :multipart
|
175
|
+
conn.request :url_encoded
|
176
|
+
conn.adapter Faraday.default_adapter
|
177
|
+
|
178
|
+
# Set timeout
|
179
|
+
conn.options.timeout = config.timeout
|
180
|
+
|
181
|
+
# Set headers
|
182
|
+
conn.headers["User-Agent"] = config.user_agent
|
183
|
+
config.default_headers.each { |key, value| conn.headers[key] = value }
|
184
|
+
|
185
|
+
# Add response middleware
|
186
|
+
conn.response :logger, config.logger if config.debug && config.logger
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Handle API response and parse JSON
|
191
|
+
#
|
192
|
+
# @param response [Faraday::Response] HTTP response
|
193
|
+
# @return [Hash] parsed JSON response
|
194
|
+
# @raise [Error] if response indicates an error
|
195
|
+
def handle_response(response)
|
196
|
+
if response.success?
|
197
|
+
parse_json_response(response.body)
|
198
|
+
else
|
199
|
+
handle_error_response(response)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Handle error responses and raise appropriate exceptions
|
204
|
+
#
|
205
|
+
# @param response [Faraday::Response] HTTP response
|
206
|
+
# @raise [Error] appropriate error based on status code
|
207
|
+
def handle_error_response(response)
|
208
|
+
message = "HTTP #{response.status}"
|
209
|
+
|
210
|
+
# Try to extract error message from response body
|
211
|
+
if response.body && !response.body.empty?
|
212
|
+
begin
|
213
|
+
parsed_body = JSON.parse(response.body)
|
214
|
+
message = parsed_body["error"] || parsed_body["message"] || message
|
215
|
+
rescue JSON::ParserError
|
216
|
+
message = response.body.length > 200 ? "#{response.body[0..200]}..." : response.body
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
raise ResourceSpace.from_response(response.status, message, response.body)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Parse JSON response body
|
224
|
+
#
|
225
|
+
# @param body [String] response body
|
226
|
+
# @return [Hash] parsed JSON
|
227
|
+
# @raise [ParseError] if JSON parsing fails
|
228
|
+
def parse_json_response(body)
|
229
|
+
return {} if body.nil? || body.empty?
|
230
|
+
|
231
|
+
JSON.parse(body)
|
232
|
+
rescue JSON::ParserError => e
|
233
|
+
raise ParseError.new("Failed to parse JSON response: #{e.message}", data: { body: body })
|
234
|
+
end
|
235
|
+
|
236
|
+
# Get MIME type for a file
|
237
|
+
#
|
238
|
+
# @param file_path [String] file path
|
239
|
+
# @return [String] MIME type
|
240
|
+
def mime_type_for_file(file_path)
|
241
|
+
require "mime/types"
|
242
|
+
MIME::Types.type_for(file_path).first&.content_type || "application/octet-stream"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|