ribose 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: c12513b80d2e78ad9faf2e3c64de85c0943ac3ba3b9ea7e048820e3fd35564eb
4
- data.tar.gz: 373f583f12f65efb0ab2f76f6123d9f140c52e25fffd557718f0165b8bbff1f2
2
+ SHA1:
3
+ metadata.gz: e18f5e392f562d3757a8dffaf903af1f63c2f8d0
4
+ data.tar.gz: bcef222cf96830b6d150ead2571d31607f06b4d0
5
5
  SHA512:
6
- metadata.gz: 734153a69502c7a0ffde70a561d9ba7842c5654c5f0cfde03267799a3a9dd2d0c0311382cab689135c87cfb28ca0349a6d4642abca7bac1786906e007af6b5f9
7
- data.tar.gz: 578bab38e786870468f9a22c71785e819d06ee46e94fe050ea65e0aed1083abd96558bea5189939b9d64ffde608c5b19aa7303f7b697c17eb070b340d76069b6
6
+ metadata.gz: 49235eefd2aa174afc2eb97febd0d5fb34b5832aa016665c37e17f6d52ff3a02ee29e352e287e623f9f2074b5176bb41488508e90ab47bb5cab0e3b531d22303
7
+ data.tar.gz: c85dc140fa9b23184aa77d6ef9334e59afe0286ad23f5b85417746ad70b78958fe0591ce3b0d6a9712ccab40e3f79babf8e3891d7f3fd87100a17fc681cf18ea
@@ -1,3 +1,13 @@
1
+ ## 0.4.0 (2018-12-15)
2
+
3
+ Features:
4
+ - Add new interface to remove a user's space
5
+ - Add an interface to disconnect a connection
6
+ - Interface to create & download file version
7
+
8
+ Fixes:
9
+ - Fix the unknown file upload related issues
10
+
1
11
  ## 0.3.2 (2018-06-28)
2
12
 
3
13
  Features:
data/README.md CHANGED
@@ -265,6 +265,18 @@ Ribose::FileVersion.fetch(
265
265
  )
266
266
  ```
267
267
 
268
+ #### Create a new file version
269
+
270
+ ```ruby
271
+ Ribose::FileVersion.create(
272
+ space_id: your_space_id,
273
+ file_id: existing_file_id_in_space,
274
+ file: file_path_for_the_new_version,
275
+
276
+ **any_other_additional_attributes
277
+ )
278
+ ```
279
+
268
280
  ### Conversations
269
281
 
270
282
  #### Listing Space Conversations
@@ -395,6 +407,16 @@ and it will return the connection as `Sawyer::Resource`.
395
407
  Ribose::Connection.all
396
408
  ```
397
409
 
410
+ #### Disconnect a connection
411
+
412
+ To disconnect with an existing connection, we can use `Connection.disconnect`
413
+ interface as following. This expect us to provide the connection id, and it also
414
+ support an additional options hash to provide custom options.
415
+
416
+ ```ruby
417
+ Ribose::Connection.disconnect(connection_id, options)
418
+ ```
419
+
398
420
  #### Connection suggestions
399
421
 
400
422
  To retrieve the list of user connection suggestions,
@@ -16,6 +16,7 @@ module Ribose
16
16
  def web_url
17
17
  ["https", api_host].join("://")
18
18
  end
19
+
19
20
  def add_default_middleware(builder)
20
21
  builder.use(Ribose::Response::RaiseError)
21
22
  builder.response(:logger, nil, bodies: true) if debug_mode?
@@ -1,6 +1,7 @@
1
1
  module Ribose
2
2
  class Connection < Ribose::Base
3
3
  include Ribose::Actions::All
4
+ include Ribose::Actions::Delete
4
5
 
5
6
  # List Connections
6
7
  #
@@ -26,6 +27,20 @@ module Ribose
26
27
  suggested_connection
27
28
  end
28
29
 
30
+ # Disconnect
31
+ #
32
+ # Disconnect connection / contact with the provided
33
+ # connection id. This will return nothing for successful
34
+ # request, but if disconnect fails then it will raise an
35
+ # Error for the client.
36
+ #
37
+ # @params resource_id [Integer] Connection Id
38
+ # @return nil
39
+ #
40
+ def self.disconnect(resource_id, options = {})
41
+ delete(resource_id, options)
42
+ end
43
+
29
44
  private
30
45
 
31
46
  def resource
@@ -51,7 +51,9 @@ module Ribose
51
51
 
52
52
  def notify_ribose_file_upload_endpoint(response, key)
53
53
  if response.status.to_i == 200
54
- content = Request.post(space_file_path, file_attributes.merge(key: key))
54
+ attributes = notifiable_attributes(file_attributes, key)
55
+
56
+ content = Request.post(space_file_path, attributes)
55
57
  content.is_a?(Sawyer::Resource) ? content : parse_to_ribose_os(content)
56
58
  end
57
59
  end
@@ -70,13 +72,18 @@ module Ribose
70
72
 
71
73
  def content_type_form_file
72
74
  require "mime/types"
73
- MIME::Types.type_for(file.path).first.content_type
75
+ mime = MIME::Types.type_for(file.path).first
76
+ mime ? mime.content_type : "application/octet-stream"
74
77
  end
75
78
 
76
79
  def parse_to_ribose_os(content)
77
80
  JSON.parse(content, object_class: Ribose::OpenStruct)
78
81
  end
79
82
 
83
+ def notifiable_attributes(attributes, key)
84
+ attributes.merge(key: key)
85
+ end
86
+
80
87
  def file_attributes
81
88
  @file_attributes ||= {
82
89
  filesize: file.size,
@@ -1,7 +1,13 @@
1
+ require "ribose/version_uploader"
2
+
1
3
  module Ribose
2
4
  class FileVersion < Ribose::Base
3
5
  include Ribose::Actions::Fetch
4
6
 
7
+ def download
8
+ download_file || raise(Ribose::BadRequest)
9
+ end
10
+
5
11
  # Fetch file version
6
12
  #
7
13
  # @params :space_id [UUID] The space Id
@@ -18,15 +24,48 @@ module Ribose
18
24
  ).fetch
19
25
  end
20
26
 
27
+ # Download file version
28
+ #
29
+ # @param space_id [UUID] The space Id
30
+ # @param file_id [Integer] The file Id
31
+ # @param version_id [Hash] The file version Id
32
+ # @param options [Hash] Options as key and value pair
33
+ #
34
+ def self.download(space_id, file_id, version_id:, **options)
35
+ new(
36
+ file_id: file_id,
37
+ space_id: space_id,
38
+ resource_id: version_id,
39
+ **options,
40
+ ).download
41
+ end
42
+
43
+ # Create a new file version
44
+ #
45
+ # @params space_id [UUID] The space UUID
46
+ # @params file_id [Integer] The space file ID
47
+ # @params file [File] The new version for file
48
+ # @params attributes [Hash] Other file attributes
49
+ # @return [Sawyer::Resource] Newly updated version
50
+ #
51
+ def self.create(space_id, file_id, file:, **attributes)
52
+ upload = VersionUploader.upload(
53
+ space_id, file_id, attributes.merge(file: file)
54
+ )
55
+
56
+ upload[:attachment]
57
+ end
58
+
21
59
  private
22
60
 
23
- attr_reader :file_id, :space_id
61
+ attr_reader :output, :file_id, :space_id
24
62
 
25
63
  def resource
26
64
  nil
27
65
  end
28
66
 
29
67
  def extract_local_attributes
68
+ @output = attributes.delete(:output)
30
69
  @file_id = attributes.delete(:file_id)
31
70
  @space_id = attributes.delete(:space_id)
32
71
  end
@@ -38,5 +77,21 @@ module Ribose
38
77
  def files_path
39
78
  ["spaces", space_id, "file", "files"].join("/")
40
79
  end
80
+
81
+ def download_file
82
+ data = Ribose::Request.get(
83
+ resource_path, parse: false, headers: { accept: "text/html" }
84
+ )
85
+
86
+ if data.headers["status"].match?(/^30[12]/)
87
+ fetch_and_write_to_file(data)
88
+ end
89
+ end
90
+
91
+ def fetch_and_write_to_file(data)
92
+ File.open(output || "download", "w") do |file|
93
+ file << data.agent.call(:get, data.headers["location"]).data
94
+ end
95
+ end
41
96
  end
42
97
  end
@@ -20,8 +20,11 @@ module Ribose
20
20
  # @return [Sawyer::Resource]
21
21
  #
22
22
  def request(options = {})
23
+ parsable = extract_config_option(:parse) != false
23
24
  options[:query] = extract_config_option(:query) || {}
24
- agent.call(http_method, api_endpoint, data, options).data
25
+
26
+ response = agent.call(http_method, api_endpoint, data, options)
27
+ parsable == true ? response.data : response
25
28
  end
26
29
 
27
30
  # Make a HTTP GET Request
@@ -79,12 +82,15 @@ module Ribose
79
82
 
80
83
  def find_suitable_client
81
84
  client = extract_config_option(:client) || Ribose::Client.new
82
- client.is_a?(Ribose::Client) ? client: raise(Ribose::Unauthorized)
85
+ client.is_a?(Ribose::Client) ? client : raise(Ribose::Unauthorized)
83
86
  end
84
87
 
85
88
  def require_auth_headers?
86
- auth_header = extract_config_option(:auth_header)
87
- auth_header == false ? false : true
89
+ extract_config_option(:auth_header) != false
90
+ end
91
+
92
+ def custom_content_headers
93
+ extract_config_option(:headers) || {}
88
94
  end
89
95
 
90
96
  def api_endpoint
@@ -107,10 +113,17 @@ module Ribose
107
113
  end
108
114
  end
109
115
 
116
+ def set_content_type(headers)
117
+ header = custom_content_headers
118
+ default_type = "application/json"
119
+
120
+ headers[:content_type] = default_type
121
+ headers[:accept] = header.fetch(:accept, default_type)
122
+ end
123
+
110
124
  def agent
111
125
  @agent ||= Sawyer::Agent.new(ribose_host, sawyer_options) do |http|
112
- http.headers[:accept] = "application/json"
113
- http.headers[:content_type] = "application/json"
126
+ set_content_type(http.headers)
114
127
 
115
128
  if require_auth_headers?
116
129
  http.headers["X-Indigo-Token"] = client.api_token
@@ -15,6 +15,10 @@ module Ribose
15
15
  Ribose::Request.post("spaces/#{space_uuid}/freeze", options)
16
16
  end
17
17
 
18
+ def self.delete(space_uuid, confirmation:, **options)
19
+ remove(space_uuid, options.merge(password_confirmation: confirmation))
20
+ end
21
+
18
22
  private
19
23
 
20
24
  attr_reader :space
@@ -34,6 +34,21 @@ module Ribose
34
34
  new(space_id: space_id, resource_id: file_id, **options).fetch
35
35
  end
36
36
 
37
+ # Download a space file
38
+ #
39
+ # @param space_id [UUID] The Space UUID
40
+ # @param file_id [Integer] The File Id
41
+ # @param options [Hash] Options as key and value pair.
42
+ #
43
+ # Two important keys are :version_id, and :output and
44
+ # if these are provided then it will use those otherwise
45
+ # it will do additional request to retirve those details
46
+ #
47
+ def self.download(space_id, file_id, options = {})
48
+ options[:version_id] ||= fetch(space_id, file_id).current_version_id
49
+ Ribose::FileVersion.download(space_id, file_id, **options)
50
+ end
51
+
37
52
  # Create a new file upload
38
53
  #
39
54
  # @param space_id [String] The Space UUID
@@ -1,3 +1,3 @@
1
1
  module Ribose
2
- VERSION = "0.3.2".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
@@ -0,0 +1,27 @@
1
+ require "ribose/file_uploader"
2
+
3
+ module Ribose
4
+ class VersionUploader < Ribose::FileUploader
5
+ def initialize(space_id, file_id, file:, **attributes)
6
+ @file_id = file_id
7
+ super(space_id, file: file, **attributes)
8
+ end
9
+
10
+ def self.upload(space_id, file_id, file:, **attributes)
11
+ new(space_id, file_id, attributes.merge(file: file)).create
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :file_id
17
+
18
+ def notifiable_attributes(attributes, key)
19
+ attributes[:file_info_version] = attributes.delete(:file_info)
20
+ attributes.merge(key: key)
21
+ end
22
+
23
+ def space_file_path
24
+ ["spaces", space_id, "file", "files", file_id, "versions"].join("/")
25
+ end
26
+ end
27
+ end
@@ -23,4 +23,15 @@ RSpec.describe Ribose::Connection do
23
23
  expect(suggestions.first.name).to eq("Jennie Doe")
24
24
  end
25
25
  end
26
+
27
+ describe ".disconnect" do
28
+ it "disconnect with provided connection" do
29
+ connection_id = 123_456
30
+ stub_ribose_connection_delete_api(connection_id)
31
+
32
+ expect do
33
+ Ribose::Connection.disconnect(connection_id)
34
+ end.not_to raise_error
35
+ end
36
+ end
26
37
  end
@@ -14,11 +14,24 @@ RSpec.describe Ribose::FileUploader do
14
14
  expect(file_upload.attachment.content_type).to eq("image/png")
15
15
  end
16
16
  end
17
+
18
+ context "with unknown file type" do
19
+ it "creates a new upload as octet-stream" do
20
+ space_id = 123_456_789
21
+ attributes = file_attributes(File.join(Ribose.root, "Rakefile"))
22
+
23
+ stub_ribose_space_file_upload_api(space_id, attributes)
24
+ file_upload = Ribose::FileUploader.upload(space_id, attributes)
25
+
26
+ expect(file_upload.attachment.id).not_to be_nil
27
+ expect(file_upload.attachment.author).to eq("John Doe")
28
+ end
29
+ end
17
30
  end
18
31
 
19
- def file_attributes
32
+ def file_attributes(file = nil)
20
33
  {
21
- file: sample_fixture_file,
34
+ file: file || sample_fixture_file,
22
35
  tag_list: "sample, file, samplefile",
23
36
  description: "This is a sample file",
24
37
  }
@@ -17,4 +17,60 @@ RSpec.describe Ribose::FileVersion do
17
17
  expect(file_version.current_version_id).to eq(789012)
18
18
  end
19
19
  end
20
+
21
+ describe ".download" do
22
+ context "with version id specified" do
23
+ it "downloads the specific file version" do
24
+ file_id = 123_456
25
+ space_id = 456_789
26
+ version_id = 789_012
27
+
28
+ output_file = "./tmp/download"
29
+ content = "This is the content in the file"
30
+
31
+ stub_aws_file_version_download_api(content)
32
+ buffer = stub_file_write_to_io(output_file)
33
+ stub_ribose_file_version_download_api(space_id, file_id, version_id)
34
+
35
+ Ribose::FileVersion.download(
36
+ space_id, file_id, version_id: version_id, output: output_file
37
+ )
38
+
39
+ expect(buffer).to eq(content)
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".create" do
45
+ it "create a new file version" do
46
+ file_id = 123_456
47
+ space_id = 456_789
48
+
49
+ stub_ribose_space_file_upload_api(space_id, file_attributes, file_id)
50
+ file = Ribose::FileVersion.create(space_id, file_id, file_attributes)
51
+
52
+ expect(file.id).not_to be_nil
53
+ expect(file.author).to eq("John Doe")
54
+ expect(file.content_type).to eq("image/png")
55
+ end
56
+ end
57
+
58
+ def file_attributes
59
+ {
60
+ file: sample_fixture_file,
61
+ description: "Version 2.0",
62
+ tag_list: "tags for new version",
63
+ }
64
+ end
65
+
66
+ def sample_fixture_file
67
+ @sample_fixture_file ||= File.join(Ribose.root, "spec/fixtures/sample.png")
68
+ end
69
+
70
+ def stub_file_write_to_io(output_file)
71
+ buffer = StringIO.new
72
+ allow(File).to receive(:open).with(output_file, "w").and_yield(buffer)
73
+
74
+ buffer.string
75
+ end
20
76
  end
@@ -28,6 +28,37 @@ RSpec.describe Ribose::SpaceFile do
28
28
  end
29
29
  end
30
30
 
31
+ describe ".download" do
32
+ context "without specific version id" do
33
+ it "fetch version id and then downloads the file" do
34
+ file_id = 123_456_789
35
+ space_id = 456_789_012
36
+
37
+ allow(Ribose::FileVersion).to receive(:download)
38
+ stub_ribose_space_file_fetch_api(space_id, file_id)
39
+
40
+ Ribose::SpaceFile.download(space_id, file_id)
41
+
42
+ expect(Ribose::FileVersion).to have_received(:download).
43
+ with(space_id, file_id, version_id: 11559)
44
+ end
45
+ end
46
+
47
+ context "with specific version id" do
48
+ it "sends downlod message to the downloader" do
49
+ file_id = 123_456_789
50
+ space_id = 456_789_012
51
+ version_id = 123_456_789
52
+
53
+ allow(Ribose::FileVersion).to receive(:download)
54
+ Ribose::SpaceFile.download(space_id, file_id, version_id: version_id)
55
+
56
+ expect(Ribose::FileVersion).to have_received(:download).
57
+ with(space_id, file_id, version_id: version_id)
58
+ end
59
+ end
60
+ end
61
+
31
62
  describe ".create" do
32
63
  it "creates a new file with provided details" do
33
64
  space_id = 123_456_789
@@ -59,6 +59,17 @@ RSpec.describe Ribose::Space do
59
59
  end
60
60
  end
61
61
 
62
+ describe ".delete" do
63
+ it "deletes an existing space" do
64
+ space_id = 123_456_789
65
+ stub_ribose_space_remove_api(space_id, password_confirmation: 1234)
66
+
67
+ expect do
68
+ Ribose::Space.delete(space_id, confirmation: 1234)
69
+ end.not_to raise_error
70
+ end
71
+ end
72
+
62
73
  def space_attributes
63
74
  {
64
75
  access: "private",
@@ -30,6 +30,12 @@ module Ribose
30
30
  )
31
31
  end
32
32
 
33
+ def stub_ribose_space_delete_api(space_id, options = {})
34
+ stub_api_response(
35
+ :delete, "spaces/#{space_id}", data: options, filename: "empty"
36
+ )
37
+ end
38
+
33
39
  def stub_ribose_feed_api
34
40
  stub_api_response(:get, "feeds", filename: "feeds")
35
41
  end
@@ -220,6 +226,17 @@ module Ribose
220
226
  )
221
227
  end
222
228
 
229
+ def stub_ribose_file_version_download_api(sid, fid, vid)
230
+ version = ["spaces", sid, "file/files", fid, "versions", vid].join("/")
231
+ stub_request(:get, ribose_endpoint(version)).to_return(
232
+ headers: { location: "https://ribose-data.aws.com", status: 302 },
233
+ )
234
+ end
235
+
236
+ def stub_aws_file_version_download_api(content)
237
+ stub_request(:get, "https://ribose-data.aws.com").to_return(body: content)
238
+ end
239
+
223
240
  def stub_ribose_space_conversation_list(space_id)
224
241
  stub_api_response(
225
242
  :get, conversations_path(space_id), filename: "conversations"
@@ -310,6 +327,12 @@ module Ribose
310
327
  stub_api_response(:get, "people/connections?s=", filename: "connections")
311
328
  end
312
329
 
330
+ def stub_ribose_connection_delete_api(id)
331
+ stub_api_response(
332
+ :delete, ["people", "connections", id].join("/"), filename: "empty"
333
+ )
334
+ end
335
+
313
336
  def stub_ribose_suggestion_list_api
314
337
  stub_api_response(
315
338
  :get, "people_finding", filename: "connection_suggestion"
@@ -2,16 +2,16 @@ require "faraday"
2
2
 
3
3
  module Ribose
4
4
  module FileUploadStub
5
- def stub_ribose_space_file_upload_api(space_id, attributes)
5
+ def stub_ribose_space_file_upload_api(space_id, attributes, file_id = nil)
6
6
  stub_ribose_aws_s3_file_upload_api
7
- stub_ribose_file_prepare_api(space_id, attributes)
8
- stub_ribose_file_upload_notify_api(space_id, attributes)
7
+ stub_ribose_file_prepare_api(space_id, attributes, file_id)
8
+ stub_ribose_file_upload_notify_api(space_id, attributes, file_id)
9
9
  end
10
10
 
11
- def stub_ribose_file_prepare_api(space_id, attributes)
11
+ def stub_ribose_file_prepare_api(space_id, attributes, file_id = nil)
12
12
  stub_api_response(
13
13
  :get,
14
- ribose_prepare_endpoint(space_id, attributes),
14
+ ribose_prepare_endpoint(space_id, attributes, file_id),
15
15
  filename: "file_upload_prepared",
16
16
  )
17
17
  end
@@ -22,11 +22,11 @@ module Ribose
22
22
  to_return(response_with(filename: "empty", status: 200))
23
23
  end
24
24
 
25
- def stub_ribose_file_upload_notify_api(space_id, attributes)
25
+ def stub_ribose_file_upload_notify_api(space_id, attributes, file_id = nil)
26
26
  stub_api_response(
27
27
  :post,
28
- ribose_file_endpoint(space_id),
29
- data: build_notify_request_body(attributes),
28
+ ribose_file_endpoint(space_id, file_id),
29
+ data: build_notify_request_body(attributes, file_id),
30
30
  filename: "file_uploaded",
31
31
  content_type: "text/html",
32
32
  )
@@ -34,12 +34,14 @@ module Ribose
34
34
 
35
35
  private
36
36
 
37
- def ribose_file_endpoint(space_id)
38
- ["spaces", space_id, "file", "files"].join("/")
37
+ def ribose_file_endpoint(space_id, file_id = nil)
38
+ end_path = file_id ? "#{file_id}/versions" : nil
39
+ ["spaces", space_id, "file", "files", end_path].compact.join("/")
39
40
  end
40
41
 
41
- def ribose_prepare_endpoint(sid, attrs)
42
- [ribose_file_endpoint(sid), "prepare?#{prepare_params(attrs)}"].join("/")
42
+ def ribose_prepare_endpoint(sid, attrs, file_id = nil)
43
+ [ribose_file_endpoint(sid, file_id), "prepare?#{prepare_params(attrs)}"].
44
+ join("/")
43
45
  end
44
46
 
45
47
  def prepare_params(attributes)
@@ -50,16 +52,19 @@ module Ribose
50
52
  )
51
53
  end
52
54
 
53
- def build_notify_request_body(attributes)
55
+ def build_notify_request_body(attributes, file_id = nil)
56
+ file_info_key = file_id ? "file_info_version" : "file_info"
57
+
54
58
  extract_file_details(attributes).merge(
55
- file_info: extract_file_info(attributes),
59
+ file_info_key.to_sym => extract_file_info(attributes),
56
60
  key: "uploads/123456789/${filename}",
57
61
  )
58
62
  end
59
63
 
60
64
  def content_type_form_file(file)
61
65
  require "mime/types"
62
- MIME::Types.type_for(file).first.content_type
66
+ mime = MIME::Types.type_for(file).first
67
+ mime ? mime.content_type : "application/octet-stream"
63
68
  end
64
69
 
65
70
  def extract_file_details(attributes)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ribose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-29 00:00:00.000000000 Z
11
+ date: 2018-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: id_pack
@@ -199,6 +199,7 @@ files:
199
199
  - lib/ribose/stream.rb
200
200
  - lib/ribose/user.rb
201
201
  - lib/ribose/version.rb
202
+ - lib/ribose/version_uploader.rb
202
203
  - lib/ribose/widget.rb
203
204
  - lib/ribose/wiki.rb
204
205
  - ribose.gemspec
@@ -315,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
315
316
  version: '0'
316
317
  requirements: []
317
318
  rubyforge_project:
318
- rubygems_version: 2.7.7
319
+ rubygems_version: 2.6.8
319
320
  signing_key:
320
321
  specification_version: 4
321
322
  summary: The Ruby interface for Ribose API