syncano 3.1.1.beta

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +304 -0
  7. data/Rakefile +7 -0
  8. data/lib/generators/syncano/install_generator.rb +17 -0
  9. data/lib/generators/syncano/templates/initializers/syncano.rb +4 -0
  10. data/lib/syncano.rb +92 -0
  11. data/lib/syncano/batch_queue.rb +58 -0
  12. data/lib/syncano/batch_queue_element.rb +33 -0
  13. data/lib/syncano/clients/base.rb +115 -0
  14. data/lib/syncano/clients/rest.rb +69 -0
  15. data/lib/syncano/clients/sync.rb +132 -0
  16. data/lib/syncano/errors.rb +13 -0
  17. data/lib/syncano/jimson_client.rb +34 -0
  18. data/lib/syncano/packets/auth.rb +7 -0
  19. data/lib/syncano/packets/base.rb +64 -0
  20. data/lib/syncano/packets/call.rb +34 -0
  21. data/lib/syncano/packets/call_response.rb +33 -0
  22. data/lib/syncano/packets/error.rb +19 -0
  23. data/lib/syncano/packets/message.rb +30 -0
  24. data/lib/syncano/packets/notification.rb +39 -0
  25. data/lib/syncano/packets/ping.rb +12 -0
  26. data/lib/syncano/query_builder.rb +144 -0
  27. data/lib/syncano/resources/admin.rb +26 -0
  28. data/lib/syncano/resources/api_key.rb +46 -0
  29. data/lib/syncano/resources/base.rb +375 -0
  30. data/lib/syncano/resources/collection.rb +186 -0
  31. data/lib/syncano/resources/data_object.rb +304 -0
  32. data/lib/syncano/resources/folder.rb +34 -0
  33. data/lib/syncano/resources/notifications/base.rb +103 -0
  34. data/lib/syncano/resources/notifications/create.rb +20 -0
  35. data/lib/syncano/resources/notifications/destroy.rb +20 -0
  36. data/lib/syncano/resources/notifications/message.rb +9 -0
  37. data/lib/syncano/resources/notifications/update.rb +24 -0
  38. data/lib/syncano/resources/project.rb +42 -0
  39. data/lib/syncano/resources/role.rb +11 -0
  40. data/lib/syncano/resources/subscription.rb +12 -0
  41. data/lib/syncano/resources/user.rb +47 -0
  42. data/lib/syncano/response.rb +22 -0
  43. data/lib/syncano/sync_connection.rb +110 -0
  44. data/lib/syncano/version.rb +4 -0
  45. data/spec/admins_spec.rb +16 -0
  46. data/spec/api_keys_spec.rb +34 -0
  47. data/spec/collections_spec.rb +67 -0
  48. data/spec/data_objects_spec.rb +113 -0
  49. data/spec/folders_spec.rb +39 -0
  50. data/spec/notifications_spec.rb +43 -0
  51. data/spec/projects_spec.rb +35 -0
  52. data/spec/roles_spec.rb +13 -0
  53. data/spec/spec_helper.rb +13 -0
  54. data/spec/sync_resources_spec.rb +35 -0
  55. data/spec/syncano_spec.rb +9 -0
  56. data/syncano.gemspec +32 -0
  57. metadata +250 -0
@@ -0,0 +1,58 @@
1
+ class Syncano
2
+ # Class representing queues used in batch requests
3
+ class BatchQueue
4
+ # Limit for amount of batch operations send in one request
5
+ REQUEST_LIMIT = 10
6
+ attr_reader :responses
7
+
8
+ # Constructor for Syncano::BatchQueue
9
+ # @param [Syncano::Clients::Base] client
10
+ def initialize(client)
11
+ super()
12
+ self.client = client
13
+ self.queue = []
14
+ self.responses = []
15
+ end
16
+
17
+ # Adds element to the queue and prune it if is full
18
+ # @param [Syncano::BatchQueueElement] element
19
+ def add(element)
20
+ self.queue << element
21
+ prune! while full?
22
+ end
23
+
24
+ # Alias for "add" method
25
+ # @param [Syncano::BatchQueueElement] element
26
+ def <<(element)
27
+ add(element)
28
+ end
29
+
30
+ # Counts elements in the queue
31
+ # @return [Integer]
32
+ def count
33
+ queue.count
34
+ end
35
+
36
+ # Checks if queue is full
37
+ # @return [TrueClass, FalseClass]
38
+ def full?
39
+ count >= REQUEST_LIMIT
40
+ end
41
+
42
+ # Prunes queue and makes batch request to the api
43
+ # @return [Array] collection of Syncano::Response objects
44
+ def prune!
45
+ part = self.queue.slice!(0, 10)
46
+ ::Jimson::Client.batch(client) do |batch_client|
47
+ part.each do |element|
48
+ element.perform!(batch_client)
49
+ end
50
+ end.collect { |response| self.responses << response }
51
+ end
52
+
53
+ protected
54
+
55
+ attr_accessor :client, :queue
56
+ attr_writer :responses
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ class Syncano
2
+ # Class representing objects batch requests queued for processing
3
+ class BatchQueueElement
4
+ # Constructor for Syncano::BatchQueueElement
5
+ # @param [Syncano::QueryBuilder, Syncano::Resources::Base] resource
6
+ def initialize(resource)
7
+ super()
8
+ self.resource = resource.dup
9
+ end
10
+
11
+ # Overwritten method_missing used for preparing execution of proper batch method on the resource object
12
+ # @param [Symbol] sym
13
+ # @param [Array] args
14
+ # @param [Proc] block
15
+ # @return [Syncano::BatchQueueElement]
16
+ def method_missing(sym, *args, &block)
17
+ self.method_name = 'batch_' + sym.to_s
18
+ self.args = args
19
+ self.block = block
20
+ self
21
+ end
22
+
23
+ # Executes batch method on the resource object
24
+ def perform!(batch_client)
25
+ args.unshift(batch_client)
26
+ resource.send(method_name, *args, &block)
27
+ end
28
+
29
+ private
30
+
31
+ attr_accessor :resource, :method_name, :args, :block
32
+ end
33
+ end
@@ -0,0 +1,115 @@
1
+ class Syncano
2
+ # Module used as a scope for classes representing clients
3
+ module Clients
4
+ # Base class for representing clients
5
+ class Base
6
+ attr_reader :instance_name, :api_key
7
+
8
+ # Constructor for Syncano::Clients::Base object
9
+ # @param [String] instance_name
10
+ # @param [String] api_key
11
+ def initialize(instance_name, api_key)
12
+ super()
13
+
14
+ self.instance_name = instance_name
15
+ self.api_key = api_key
16
+ end
17
+
18
+ # Returns query builder for Syncano::Resources::Admin objects
19
+ # @return [Syncano::QueryBuilder]
20
+ def admins
21
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Admin)
22
+ end
23
+
24
+ # Returns query builder for Syncano::Resources::ApiKey objects
25
+ # @return [Syncano::QueryBuilder]
26
+ def api_keys
27
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::ApiKey)
28
+ end
29
+
30
+ # Returns query builder for Syncano::Resources::Role objects
31
+ # @return [Syncano::QueryBuilder]
32
+ def roles
33
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Role)
34
+ end
35
+
36
+ # Returns query builder for Syncano::Resources::Project objects
37
+ # @return [Syncano::QueryBuilder]
38
+ def projects
39
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Project)
40
+ end
41
+
42
+ # Returns query builder for Syncano::Resources::Project objects
43
+ # @param [Integer, String] project_id
44
+ # @return [Syncano::QueryBuilder]
45
+ def collections(project_id)
46
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Collection, project_id: project_id)
47
+ end
48
+
49
+ # Returns query builder for Syncano::Resources::Folder objects
50
+ # @param [Integer, String] project_id
51
+ # @param [Integer, String] collection_id
52
+ # @return [Syncano::QueryBuilder]
53
+ def folders(project_id, collection_id)
54
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Collection, project_id: project_id, collection_id: collection_id)
55
+ end
56
+
57
+ # Returns query builder for Syncano::Resources::DataObject objects
58
+ # @param [Integer, String] project_id
59
+ # @param [Integer, String] collection_id
60
+ # @return [Syncano::QueryBuilder]
61
+ def data_objects(project_id, collection_id)
62
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::DataObject, project_id: project_id, collection_id: collection_id)
63
+ end
64
+
65
+ # Returns query builder for Syncano::Resources::User objects
66
+ # @param [Integer, String] project_id
67
+ # @param [Integer, String] collection_id
68
+ # @return [Syncano::QueryBuilder]
69
+ def users(project_id, collection_id)
70
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::User, project_id: project_id, collection_id: collection_id)
71
+ end
72
+
73
+ # Performs request to Syncano api
74
+ # This should be overwritten in inherited classes
75
+ # @param [String] resource_name
76
+ # @param [String] method_name
77
+ # @param [Hash] params additional params sent in the request
78
+ # @param [String] response_key for cases when response from api is incompatible with the convention
79
+ # @return [Syncano::Response]
80
+ def make_request(resource_name, method_name, params = {}, response_key = nil)
81
+ end
82
+
83
+ # Performs batch request to Syncano api
84
+ # This should be overwritten in inherited classes
85
+ # @param [Jimson::BatchClient] batch_client
86
+ # @param [String] resource_name
87
+ # @param [String] method_name
88
+ # @param [Hash] params additional params sent in the request
89
+ def make_batch_request(batch_client, resource_name, method_name, params = {})
90
+ end
91
+
92
+ private
93
+
94
+ attr_writer :instance_name, :api_key
95
+
96
+ # Parses Syncano api response and returns Syncano::Response object
97
+ # @param [String] response_key
98
+ # @param [Hash] raw_response
99
+ # @return [Syncano::Response]
100
+ def self.parse_response(response_key, raw_response)
101
+ status = raw_response.nil? || raw_response['result'] != 'NOK'
102
+ if raw_response.nil?
103
+ data = nil
104
+ elsif raw_response[response_key].present?
105
+ data = raw_response[response_key]
106
+ else
107
+ data = raw_response['count']
108
+ end
109
+ errors = status ? [] : raw_response['error']
110
+
111
+ ::Syncano::Response.new(status, data, errors)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,69 @@
1
+ class Syncano
2
+ module Clients
3
+ # Client used for communication with the JSON-RPC endpoint
4
+ class Rest < Syncano::Clients::Base
5
+ attr_reader :client
6
+
7
+ # Constructor for Syncano::Clients::Rest object
8
+ # @param [String] instance_name
9
+ # @param [String] api_key
10
+ def initialize(instance_name, api_key)
11
+ super(instance_name, api_key)
12
+ self.client = ::Jimson::Client.new(json_rpc_url)
13
+ end
14
+
15
+ # Performs request to Syncano api
16
+ # @param [String] resource_name
17
+ # @param [String] method_name
18
+ # @param [Hash] params additional params sent in the request
19
+ # @param [String] response_key for cases when response from api is incompatible with the convention
20
+ # @return [Syncano::Response]
21
+ def make_request(resource_name, method_name, params = {}, response_key = nil)
22
+ response_key ||= resource_name
23
+ response = client.send("#{resource_name}.#{method_name}", request_params.merge(params))
24
+ response = self.class.parse_response(response_key, response)
25
+
26
+ response.errors.present? ? raise(Syncano::ApiError.new(response.errors)) : response
27
+ end
28
+
29
+ # Performs batch request to Syncano api
30
+ # @param [Jimson::BatchClient] batch_client
31
+ # @param [String] resource_name
32
+ # @param [String] method_name
33
+ # @param [Hash] params additional params sent in the request
34
+ def make_batch_request(batch_client, resource_name, method_name, params = {})
35
+ batch_client.send("#{resource_name}.#{method_name}", request_params.merge(params))
36
+ end
37
+
38
+ # Gets block in which Syncano::BatchQueue object is provided and batch requests can be executed
39
+ # @param [Block]
40
+ # @return [Array] collection of parsed responses
41
+ def batch
42
+ queue = ::Syncano::BatchQueue.new(client)
43
+ yield(queue)
44
+ queue.prune!
45
+
46
+ queue.responses.collect do |response|
47
+ resource_name = response.first.method.split('.').first
48
+ self.class.parse_response(resource_name, response.last.result)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ attr_writer :client
55
+
56
+ # Generates url to json rpc api
57
+ # @return [String]
58
+ def json_rpc_url
59
+ "https://#{instance_name}.syncano.com/api/jsonrpc"
60
+ end
61
+
62
+ # Prepares hash with default request params
63
+ # @return [Hash]
64
+ def request_params
65
+ { api_key: api_key }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,132 @@
1
+ class Syncano
2
+ module Clients
3
+ # Client used for communication with the Sync Server
4
+ class Sync < Syncano::Clients::Base
5
+ include ::Singleton
6
+
7
+ attr_accessor :connection
8
+
9
+ # Constructor for Syncano::Clients::Sync object
10
+ # @param [String] instance_name
11
+ # @param [String] api_key
12
+ def initialize(instance_name, api_key)
13
+ super(instance_name, api_key)
14
+ self.connection = nil
15
+ end
16
+
17
+ # Getter for Singleton instance
18
+ # @param [String] instance_name
19
+ # @param [String] api_key
20
+ # @return [Syncano::Clients::Base]
21
+ def self.instance(instance_name = nil, api_key = nil)
22
+ unless @singleton__instance__
23
+ @singleton__mutex__.synchronize do
24
+ return @singleton__instance__ if @singleton__instance__
25
+ @singleton__instance__ = new(instance_name, api_key)
26
+ end
27
+ end
28
+ @singleton__instance__
29
+ end
30
+
31
+ # Connects with the Sync api
32
+ def connect
33
+ if connection.blank?
34
+ hostname = 'api.syncano.com'
35
+ port = 8200
36
+
37
+ Thread.new do
38
+ begin
39
+ EM.run do
40
+ EM.connect(hostname, port, Syncano::SyncConnection)
41
+ end
42
+ rescue Exception => e
43
+ p e.message
44
+ p e.backtrace
45
+ end
46
+ end
47
+
48
+ timeout = 30
49
+
50
+ while connection.blank? && timeout > 0
51
+ timeout -= 1
52
+ sleep 1
53
+ end
54
+
55
+ raise ::Syncano::ConnectionError.new('Connection timeout') unless timeout > 0
56
+ end
57
+ end
58
+
59
+ # Disconnects with the Sync api
60
+ def disconnect
61
+ EM.stop
62
+ self.connection = nil
63
+ end
64
+
65
+ # Reconnects with the Sync api
66
+ def reconnect
67
+ disconnect
68
+ connect
69
+ end
70
+
71
+ # Returns query builder for Syncano::Resources::Subscription objects
72
+ # @return [Syncano::QueryBuilder]
73
+ def subscriptions
74
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Subscription)
75
+ end
76
+
77
+ # Returns query builder for Syncano::Resources::Notifications::Base objects
78
+ # @return [Syncano::QueryBuilder]
79
+ def notifications
80
+ ::Syncano::QueryBuilder.new(self, ::Syncano::Resources::Notifications::Base)
81
+ end
82
+
83
+ # Appends callback for processing notifications to the end of callbacks queue
84
+ # @return [Syncano::QueryBuilder]
85
+ def append_callback(callback_name, &callback)
86
+ connection.append_callback(callback_name, callback)
87
+ end
88
+
89
+ # Prepends callback for processing notifications to the beginning of callbacks queue
90
+ # @return [Syncano::QueryBuilder]
91
+ def prepend_callback(callback_name, &callback)
92
+ connection.prepend_callback(callback_name, callback)
93
+ end
94
+
95
+ # Removes callback from the callbacks queue
96
+ # @return [Syncano::QueryBuilder]
97
+ def remove_callback(callback_name)
98
+ connection.remove_callback(callback_name)
99
+ end
100
+
101
+ # Performs request to Syncano api
102
+ # This should be overwritten in inherited classes
103
+ # @param [String] resource_name
104
+ # @param [String] method_name
105
+ # @param [Hash] params additional params sent in the request
106
+ # @param [String] response_key for cases when response from api is incompatible with the convention
107
+ # @return [Syncano::Response]
108
+ def make_request(resource_name, method_name, params = {}, response_key = nil)
109
+ response_key ||= resource_name
110
+
111
+ packet = ::Syncano::Packets::Call.new(resource_name: resource_name, method_name: method_name, data: params)
112
+ connection.send_data("#{packet.to_json}\n")
113
+
114
+ response_packet = nil
115
+ timer = 600
116
+
117
+ while timer > 0
118
+ response_packet = connection.get_response(packet.message_id)
119
+ if response_packet.nil?
120
+ timer -= 1
121
+ sleep 1.0 / 10.0
122
+ else
123
+ break
124
+ end
125
+ end
126
+
127
+ response = self.class.parse_response(response_key, response_packet.to_response)
128
+ response.errors.present? ? raise(Syncano::ApiError.new(response.errors)) : response
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,13 @@
1
+ class Syncano
2
+ # General errors
3
+ class BaseError < RuntimeError
4
+ end
5
+
6
+ # Class represeting errors returned by the Syncano API
7
+ class ApiError < BaseError
8
+ end
9
+
10
+ # Class representing errors during connections
11
+ class ConnectionError < BaseError
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # Overwritten module from Jimson gem
2
+ module Jimson
3
+ # Overwritten helper from Jimson gem
4
+ class ClientHelper
5
+ # Overwritten send_batch method, so it now returns collection of responses
6
+ # @return [Array] collection of responses
7
+ def send_batch
8
+ batch = @batch.map(&:first) # get the requests
9
+ response = send_batch_request(batch)
10
+
11
+ begin
12
+ responses = JSON.parse(response)
13
+ rescue
14
+ raise Jimson::ClientError::InvalidJSON.new(json)
15
+ end
16
+
17
+ process_batch_response(responses)
18
+ responses = @batch
19
+
20
+ @batch = []
21
+
22
+ responses
23
+ end
24
+ end
25
+
26
+ # Overwritten Request class from Jimson gem
27
+ class Request
28
+ # Overwritten as_json method which solves bug with serialization batch requests
29
+ # @return [Hash]
30
+ def as_json(options = {})
31
+ to_h
32
+ end
33
+ end
34
+ end