syncano 3.1.4 → 4.0.0.alpha

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 (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +3 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +25 -1
  7. data/Guardfile +22 -4
  8. data/README.md +68 -447
  9. data/Rakefile +48 -5
  10. data/circle.yml +10 -0
  11. data/lib/active_attr/dirty.rb +3 -17
  12. data/lib/active_attr/typecasting/hash_typecaster.rb +34 -0
  13. data/lib/active_attr/typecasting_override.rb +29 -0
  14. data/lib/syncano.rb +53 -92
  15. data/lib/syncano/api.rb +13 -0
  16. data/lib/syncano/connection.rb +97 -0
  17. data/lib/syncano/model/associations.rb +121 -0
  18. data/lib/syncano/{active_record/association → model/associations}/base.rb +5 -5
  19. data/lib/syncano/{active_record/association → model/associations}/belongs_to.rb +6 -6
  20. data/lib/syncano/{active_record/association → model/associations}/has_many.rb +15 -9
  21. data/lib/syncano/{active_record/association → model/associations}/has_one.rb +4 -4
  22. data/lib/syncano/model/base.rb +257 -0
  23. data/lib/syncano/{active_record → model}/callbacks.rb +16 -13
  24. data/lib/syncano/{active_record → model}/scope_builder.rb +53 -69
  25. data/lib/syncano/query_builder.rb +19 -129
  26. data/lib/syncano/resources.rb +126 -0
  27. data/lib/syncano/resources/base.rb +304 -300
  28. data/lib/syncano/resources/collection.rb +19 -223
  29. data/lib/syncano/resources/space.rb +29 -0
  30. data/lib/syncano/schema.rb +86 -0
  31. data/lib/syncano/schema/attribute_definition.rb +83 -0
  32. data/lib/syncano/schema/resource_definition.rb +36 -0
  33. data/lib/syncano/scope.rb +10 -0
  34. data/lib/syncano/version.rb +3 -4
  35. data/spec/integration/syncano_spec.rb +228 -0
  36. data/spec/spec_helper.rb +15 -9
  37. data/spec/unit/api_spec.rb +5 -0
  38. data/spec/unit/connection_spec.rb +137 -0
  39. data/spec/unit/query_builder_spec.rb +75 -0
  40. data/spec/unit/resources/collection_spec.rb +36 -0
  41. data/spec/unit/resources/space_spec.rb +28 -0
  42. data/spec/unit/resources_base_spec.rb +185 -0
  43. data/spec/unit/schema/attribute_definition_spec.rb +18 -0
  44. data/spec/unit/schema/resource_definition_spec.rb +25 -0
  45. data/spec/unit/schema_spec.rb +3532 -0
  46. data/spec/unit/syncano_spec.rb +63 -0
  47. data/syncano.gemspec +8 -14
  48. metadata +85 -210
  49. data/lib/generators/syncano/install_generator.rb +0 -17
  50. data/lib/generators/syncano/templates/initializers/syncano.rb +0 -7
  51. data/lib/syncano/active_record/associations.rb +0 -112
  52. data/lib/syncano/active_record/base.rb +0 -318
  53. data/lib/syncano/batch_queue.rb +0 -58
  54. data/lib/syncano/batch_queue_element.rb +0 -33
  55. data/lib/syncano/clients/base.rb +0 -123
  56. data/lib/syncano/clients/rest.rb +0 -79
  57. data/lib/syncano/clients/sync.rb +0 -164
  58. data/lib/syncano/errors.rb +0 -17
  59. data/lib/syncano/jimson_client.rb +0 -66
  60. data/lib/syncano/packets/auth.rb +0 -27
  61. data/lib/syncano/packets/base.rb +0 -70
  62. data/lib/syncano/packets/call.rb +0 -34
  63. data/lib/syncano/packets/call_response.rb +0 -33
  64. data/lib/syncano/packets/error.rb +0 -19
  65. data/lib/syncano/packets/message.rb +0 -30
  66. data/lib/syncano/packets/notification.rb +0 -39
  67. data/lib/syncano/packets/ping.rb +0 -12
  68. data/lib/syncano/resources/admin.rb +0 -26
  69. data/lib/syncano/resources/api_key.rb +0 -108
  70. data/lib/syncano/resources/data_object.rb +0 -316
  71. data/lib/syncano/resources/folder.rb +0 -88
  72. data/lib/syncano/resources/notifications/base.rb +0 -103
  73. data/lib/syncano/resources/notifications/create.rb +0 -20
  74. data/lib/syncano/resources/notifications/destroy.rb +0 -20
  75. data/lib/syncano/resources/notifications/message.rb +0 -9
  76. data/lib/syncano/resources/notifications/update.rb +0 -24
  77. data/lib/syncano/resources/project.rb +0 -96
  78. data/lib/syncano/resources/role.rb +0 -11
  79. data/lib/syncano/resources/subscription.rb +0 -12
  80. data/lib/syncano/resources/user.rb +0 -65
  81. data/lib/syncano/response.rb +0 -22
  82. data/lib/syncano/sync_connection.rb +0 -133
  83. data/spec/admins_spec.rb +0 -16
  84. data/spec/api_keys_spec.rb +0 -34
  85. data/spec/collections_spec.rb +0 -67
  86. data/spec/data_objects_spec.rb +0 -113
  87. data/spec/folders_spec.rb +0 -39
  88. data/spec/notifications_spec.rb +0 -43
  89. data/spec/projects_spec.rb +0 -35
  90. data/spec/roles_spec.rb +0 -13
  91. data/spec/sync_resources_spec.rb +0 -35
  92. data/spec/syncano_spec.rb +0 -9
@@ -1,240 +1,36 @@
1
- class Syncano
1
+ module Syncano
2
2
  module Resources
3
- # Collection resource
4
- class Collection < ::Syncano::Resources::Base
5
- # Association has_many :folders
6
- # @return [Syncano::QueryBuilder] query builder for resource Syncano::Resources::Folder
7
- def folders
8
- ::Syncano::QueryBuilder.new(client, ::Syncano::Resources::Folder, scope_parameters.merge(collection_id: id))
9
- end
10
-
11
- # Association has_many :data_objects
12
- # @return [Syncano::QueryBuilder] query builder for resource Syncano::Resources::DataObject
13
- def data_objects
14
- ::Syncano::QueryBuilder.new(client, ::Syncano::Resources::DataObject, scope_parameters.merge(collection_id: id))
15
- end
16
-
17
- # Association has_many :users
18
- # @return [Syncano::QueryBuilder] query builder for resource Syncano::Resources::User
19
- def users
20
- ::Syncano::QueryBuilder.new(client, ::Syncano::Resources::User, scope_parameters.merge(collection_id: id))
21
- end
22
-
23
- # Wrapper for api "get_one" method with collection_key as a key
24
- # @param [Syncano::Clients::Base] client
25
- # @param [String] key
26
- # @param [Hash] scope_parameters
27
- # @param [Hash] conditions
28
- # @return [Syncano::Resources::Collection]
29
- def self.find_by_key(client, key, scope_parameters = {}, conditions = {})
30
- perform_find(client, :key, key, scope_parameters, conditions)
31
- end
32
-
33
- # Wrapper for api "activate" method
34
- # @param [TrueClass, FalseClass] force
35
- # @return [Syncano::Resources::Collection]
36
- def activate(force = false)
37
- response = perform_activate(nil, force)
38
- reload! if response.status
39
-
40
- self
41
- end
42
-
43
- # Batch version of "activate" method
44
- # @param [Jimson::BatchClient] batch_client
45
- # @param [TrueClass, FalseClass] force
46
- # @return [Syncano::Response]
47
- def batch_activate(batch_client, force = false)
48
- perform_activate(batch_client, force)
49
- end
50
-
51
- # Wrapper for api "deactivate" method
52
- # @return [Syncano::Resources::Collection]
53
- def deactivate
54
- response = perform_deactivate(nil)
55
- reload! if response.status
56
-
57
- self
58
- end
59
-
60
- # Batch version of "deactivate" method
61
- # @param [Jimson::BatchClient] batch_client
62
- # @return [Syncano::Response]
63
- def batch_deactivate(batch_client)
64
- perform_deactivate(batch_client)
65
- end
66
-
67
- # Wrapper for api "add_tag" method
68
- # @param [String, Array] tags
69
- # @param [Numeric] weight
70
- # @param [TrueClass, FalseClass] remove_other
71
- # @return [Syncano::Resources::Collection]
72
- def add_tag(tags, weight = 1, remove_other = false)
73
- response = perform_add_tag(nil, tags, weight, remove_other)
74
- reload! if response.status
75
-
76
- self
77
- end
78
-
79
- # Batch version of "add_tags" method
80
- # @param [Jimson::BatchClient] batch_client
81
- # @param [String, Array] tags
82
- # @param [Numeric] weight
83
- # @param [TrueClass, FalseClass] remove_other
84
- # @return [Syncano::Response]
85
- def batch_add_tags(batch_client, tags, weight = 1, remove_other = false)
86
- perform_add_tag(batch_client, tags, weight, remove_other)
87
- end
3
+ class Collection
4
+ include Enumerable
88
5
 
89
- # Wrapper for api "delete_tag" method
90
- # @param [String, Array] tags
91
- # @return [Syncano::Resources::Collection]
92
- def delete_tags(tags)
93
- response = perform_delete_tag(nil, tags)
94
- reload! if response.status
6
+ delegate :last, :[], to: :collection
95
7
 
96
- self
8
+ def self.from_database(response, scope, element_class)
9
+ new response, scope, element_class, true
97
10
  end
98
11
 
99
- # Batch version of "delete_tag" method
100
- # @param [Jimson::BatchClient] batch_client
101
- # @param [String, Array] tags
102
- # @return [Syncano::Response]
103
- def batch_delete_tag(batch_client, tags)
104
- perform_delete_tag(batch_client, tags)
12
+ def each(&block)
13
+ collection.each &block
105
14
  end
106
15
 
107
- # Wrapper for api "subscription.subscribe_collection" method
108
- # @return [Syncano::Resource::Collection]
109
- def subscribe
110
- perform_subscribe
111
- reload!
16
+ def prev?
17
+ @prev
112
18
  end
113
19
 
114
- # Wrapper for api "subscription.unsubscribe_collection" method
115
- # @return [Syncano::Resource::Collection]
116
- def unsubscribe
117
- perform_unsubscribe
118
- reload!
119
- end
120
-
121
- # Wrapper for api "authorize" method
122
- # @param [Integer] api_client_id
123
- # @param [String] permission
124
- # @return [Syncano::Resources::Base]
125
- def authorize(api_client_id, permission)
126
- perform_authorize(nil, api_client_id: api_client_id, permission: permission)
127
- self
128
- end
129
-
130
- # Wrapper for api "authorize" method
131
- # @param [Jimson::BatchClient] batch_client
132
- # @param [Integer] api_client_id
133
- # @param [String] permission
134
- # @return [Syncano::Resources::Base]
135
- def batch_authorize(batch_client, api_client_id, permission)
136
- perform_authorize(batch_client, api_client_id: api_client_id, permission: permission)
137
- self
138
- end
139
-
140
- # Wrapper for api "deauthorize" method
141
- # @param [Integer] api_client_id
142
- # @param [String] permission
143
- # @return [Syncano::Resources::Base]
144
- def deauthorize(api_client_id, permission)
145
- perform_deauthorize(nil, api_client_id: api_client_id, permission: permission)
146
- self
147
- end
148
-
149
- # Wrapper for api "deauthorize" method
150
- # @param [Jimson::BatchClient] batch_client
151
- # @param [Integer] api_client_id
152
- # @param [String] permission
153
- # @return [Syncano::Resources::Base]
154
- def batch_deauthorize(batch_client, api_client_id, permission)
155
- perform_deauthorize(batch_client, api_client_id: api_client_id, permission: permission)
156
- self
20
+ def next?
21
+ @next
157
22
  end
158
23
 
159
24
  private
160
25
 
161
- self.scope_parameters = [:project_id]
162
-
163
- # Executes proper activate request
164
- # @param [Jimson::BatchClient] batch_client
165
- # @param [TrueClass, FalseClass] force
166
- # @return [Syncano::Response]
167
- def perform_activate(batch_client, force)
168
- self.class.make_request(client, batch_client, :activate, scope_parameters.merge(
169
- self.class.primary_key_name => primary_key,
170
- force: force
171
- ))
172
- end
173
-
174
- # Executes proper deactivate request
175
- # @param [Jimson::BatchClient] batch_client
176
- # @return [Syncano::Response]
177
- def perform_deactivate(batch_client)
178
- self.class.make_request(client, batch_client, :deactivate, scope_parameters.merge(
179
- self.class.primary_key_name => primary_key
180
- ))
181
- end
182
-
183
- # Executes proper add_tags request
184
- # @param [Jimson::BatchClient] batch_client
185
- # @param [String, Array] tags
186
- # @param [Numeric] weight
187
- # @param [TrueClass, FalseClass] remove_other
188
- # @return [Syncano::Response]
189
- def perform_add_tags(batch_client, tags, weight, remove_other)
190
- self.class.make_request(client, batch_client, :add_tag, scope_parameters.merge(
191
- self.class.primary_key_name => primary_key,
192
- tags: tags,
193
- weight: weight,
194
- remove_other: remove_other
195
- ))
196
- end
197
-
198
- # Executes proper delete_tags request
199
- # @param [Jimson::BatchClient] batch_client
200
- # @param [String, Array] tags
201
- # @return [Syncano::Response]
202
- def perform_delete_tags(batch_client, tags)
203
- self.class.make_request(client, batch_client, :delete_tag, scope_parameters.merge(
204
- self.class.primary_key_name => primary_key,
205
- tags: tags
206
- ))
207
- end
208
-
209
- # Executes proper subscribe request
210
- # @return [Syncano::Response]
211
- def perform_subscribe
212
- check_if_sync_client!
213
- client.make_request(:subscription, :subscribe_collection, scope_parameters.merge(collection_id: id))
214
- end
215
-
216
- # Executes proper unsubscribe request
217
- # @return [Syncano::Response]
218
- def perform_unsubscribe
219
- check_if_sync_client!
220
- client.make_request(:subscription, :unsubscribe_collection, scope_parameters.merge(collection_id: id))
221
- end
222
-
223
- # Executes proper authorize request
224
- # @param [Jimson::BatchClient] batch_client
225
- # @param [Hash] parameters
226
- # @return [Syncano::Response]
227
- def perform_authorize(batch_client, parameters)
228
- self.class.make_request(client, batch_client, :authorize, scope_parameters.merge(parameters.merge(self.class.primary_key_name.to_sym => primary_key)))
229
- end
26
+ attr_accessor :collection
230
27
 
231
- # Executes proper deauthorize request
232
- # @param [Jimson::BatchClient] batch_client
233
- # @param [Hash] parameters
234
- # @return [Syncano::Response]
235
- def perform_deauthorize(batch_client, parameters)
236
- self.class.make_request(client, batch_client, :deauthorize, scope_parameters.merge(parameters.merge(self.class.primary_key_name.to_sym => primary_key)))
28
+ def initialize(response, scope, element_class, from_database)
29
+ @prev, @next = response['prev'].present?, response['next'].present?
30
+ self.collection = response['objects'].map do |attributes|
31
+ element_class.new scope.connection, scope.scope_parameters, attributes, from_database
32
+ end
237
33
  end
238
34
  end
239
35
  end
240
- end
36
+ end
@@ -0,0 +1,29 @@
1
+ module Syncano
2
+ module Resources
3
+ class Space
4
+
5
+ DEFAULT_DIRECTION = :next
6
+ DIRECTIONS = { DEFAULT_DIRECTION => 1, :prev => 0 }
7
+ DIRECTIONS.default_proc = ->(hash, key) do
8
+ if key.nil?
9
+ hash[DEFAULT_DIRECTION]
10
+ else
11
+ raise Syncano::RuntimeError.new("Valid orders are #{hash.keys}, you passed #{key.inspect}")
12
+ end
13
+ end
14
+ DIRECTIONS.freeze
15
+
16
+ attr_accessor :at, :query_builder, :direction
17
+
18
+ def initialize(at, query_builder, options = {})
19
+ self.at = at
20
+ self.query_builder = query_builder
21
+ self.direction = DIRECTIONS[options[:direction]]
22
+ end
23
+
24
+ def all
25
+ query_builder.all last_pk: at.primary_key, direction: direction
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,86 @@
1
+ require_relative './schema/attribute_definition'
2
+ require_relative './schema/resource_definition'
3
+
4
+ module Syncano
5
+ class Schema
6
+ SCHEMA_PATH = 'schema/'
7
+
8
+ attr_reader :schema
9
+
10
+ def initialize(connection)
11
+ self.connection = connection
12
+ load_schema
13
+ end
14
+
15
+ def process!
16
+ schema.each do |name, raw_resource_definition|
17
+ resource_definition = Syncano::Schema::ResourceDefinition.new(name, raw_resource_definition)
18
+ resource_class = ::Syncano::Resources.define_resource_class(resource_definition)
19
+
20
+ if resource_definition[:collection].present? && resource_definition[:collection][:path].scan(/\{([^}]+)\}/).empty?
21
+ self.class.generate_client_method(name, resource_class)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :connection
29
+ attr_writer :schema
30
+
31
+ def load_schema
32
+ raw_schema = connection.request(:get, SCHEMA_PATH)
33
+ resources = {}
34
+
35
+ raw_schema.each do |resource_schema|
36
+ class_name = resource_schema['name']
37
+
38
+ resources[class_name] = {
39
+ attributes: {},
40
+ associations: {},
41
+ collection: nil,
42
+ member: nil,
43
+ custom_methods: []
44
+ }
45
+
46
+ fields = resource_schema['endpoints']['list'].try(:[], 'fields') || {}
47
+
48
+ resources[class_name][:attributes].merge! fields
49
+
50
+ if fields['links']
51
+ resources[class_name][:attributes].delete('links')
52
+ resources[class_name][:associations].merge!(fields['links'])
53
+ end
54
+
55
+ resource_schema['endpoints'].each do |type, endpoint|
56
+ endpoint_data = {
57
+ path: endpoint['path'],
58
+ http_methods: endpoint['methods'],
59
+ params: endpoint['properties']
60
+ }
61
+
62
+ if type == 'list'
63
+ resources[class_name][:collection] = endpoint_data
64
+ elsif type == 'detail'
65
+ resources[class_name][:member] = endpoint_data
66
+ else
67
+ endpoint_data.merge!(name: type)
68
+ resources[class_name][:custom_methods] << endpoint_data
69
+ end
70
+ end
71
+ end
72
+
73
+ self.schema = resources
74
+ end
75
+
76
+ class << self
77
+ def generate_client_method(resource_name, resource_class)
78
+ method_name = resource_name.tableize
79
+
80
+ ::Syncano::API.send(:define_method, method_name) do
81
+ ::Syncano::QueryBuilder.new(connection, resource_class)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,83 @@
1
+ module Syncano
2
+ class Schema
3
+ class AttributeDefinition
4
+ attr_accessor :name, :type, :default
5
+
6
+ TYPES_MAPPING = { 'string' => ::String,
7
+ 'email' => ::String,
8
+ 'choice' => ::String,
9
+ 'slug' => ::String,
10
+ 'integer' => ::Integer,
11
+ 'float' => ::Float,
12
+ 'date' => ::Date,
13
+ 'datetime' => ::DateTime,
14
+ 'field' => ::Object }
15
+
16
+ def initialize(name, raw_definition)
17
+ # TODO implement #original_name to send request with correct parameters
18
+ self.name = name == 'class' ? 'associated_class' : name
19
+ self.raw_definition = raw_definition
20
+
21
+ set_type
22
+ set_default
23
+ end
24
+
25
+ def force_default?
26
+ !default.nil?
27
+ end
28
+
29
+ def writable?
30
+ raw_definition['read_only'] == false
31
+ end
32
+
33
+ def required?
34
+ raw_definition['required'] == true
35
+ end
36
+
37
+ def required_length
38
+ begin
39
+ { maximum: Integer(raw_definition['max_length']) }
40
+ rescue TypeError, ArgumentError
41
+ end
42
+ end
43
+
44
+ def required_values_inclusion
45
+ return unless choices = raw_definition['choices']
46
+
47
+ { in: choices.map { |choice| choice['value'] } }
48
+ end
49
+
50
+ alias :updatable? :writable?
51
+
52
+ def [](key)
53
+ raw_definition[key]
54
+ end
55
+
56
+ private
57
+
58
+ attr_accessor :raw_definition
59
+
60
+ def set_type
61
+ self.type = if %w[owner group].include?(name)
62
+ ::Integer
63
+ elsif raw_definition['type'].blank?
64
+ ::Object
65
+ else
66
+ TYPES_MAPPING[raw_definition['type']]
67
+ end
68
+ end
69
+
70
+ def set_default
71
+ self.default = if name == 'channel'
72
+ nil
73
+ elsif raw_definition['type'].present? && raw_definition['type'].to_sym == :field
74
+ {}
75
+ elsif raw_definition['type'].present? && raw_definition['type'].to_sym == :choice
76
+ raw_definition['choices'].first['value']
77
+ else
78
+ nil
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end