syncano 4.0.0.alpha4 → 4.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/README.md +1 -13
  4. data/circle.yml +1 -1
  5. data/lib/active_attr/dirty.rb +26 -0
  6. data/lib/active_attr/typecasting/hash_typecaster.rb +34 -0
  7. data/lib/active_attr/typecasting_override.rb +29 -0
  8. data/lib/syncano.rb +9 -55
  9. data/lib/syncano/api.rb +2 -20
  10. data/lib/syncano/connection.rb +47 -48
  11. data/lib/syncano/model/associations.rb +121 -0
  12. data/lib/syncano/model/associations/base.rb +38 -0
  13. data/lib/syncano/model/associations/belongs_to.rb +30 -0
  14. data/lib/syncano/model/associations/has_many.rb +75 -0
  15. data/lib/syncano/model/associations/has_one.rb +22 -0
  16. data/lib/syncano/model/base.rb +257 -0
  17. data/lib/syncano/model/callbacks.rb +49 -0
  18. data/lib/syncano/model/scope_builder.rb +158 -0
  19. data/lib/syncano/query_builder.rb +7 -11
  20. data/lib/syncano/resources/base.rb +66 -91
  21. data/lib/syncano/schema.rb +159 -10
  22. data/lib/syncano/schema/attribute_definition.rb +0 -75
  23. data/lib/syncano/schema/resource_definition.rb +2 -24
  24. data/lib/syncano/version.rb +1 -1
  25. data/spec/integration/syncano_spec.rb +26 -268
  26. data/spec/spec_helper.rb +1 -3
  27. data/spec/unit/connection_spec.rb +74 -34
  28. data/spec/unit/query_builder_spec.rb +2 -2
  29. data/spec/unit/resources_base_spec.rb +64 -125
  30. data/spec/unit/schema/resource_definition_spec.rb +3 -24
  31. data/spec/unit/schema_spec.rb +55 -5
  32. data/spec/unit/syncano_spec.rb +9 -45
  33. data/syncano.gemspec +0 -5
  34. metadata +14 -87
  35. data/lib/syncano/api/endpoints.rb +0 -17
  36. data/lib/syncano/poller.rb +0 -55
  37. data/lib/syncano/resources.rb +0 -158
  38. data/lib/syncano/resources/paths.rb +0 -48
  39. data/lib/syncano/resources/resource_invalid.rb +0 -15
  40. data/lib/syncano/response.rb +0 -55
  41. data/lib/syncano/schema/endpoints_whitelist.rb +0 -40
  42. data/lib/syncano/upload_io.rb +0 -7
  43. data/spec/unit/resources/paths_spec.rb +0 -21
  44. data/spec/unit/response_spec.rb +0 -75
  45. data/spec/unit/schema/attribute_definition_spec.rb +0 -18
@@ -1,26 +1,33 @@
1
1
  require_relative './schema/attribute_definition'
2
2
  require_relative './schema/resource_definition'
3
- require_relative './schema/endpoints_whitelist'
4
-
5
- require 'singleton'
6
3
 
7
4
  module Syncano
8
5
  class Schema
6
+ SCHEMA_PATH = 'schema/'
9
7
 
10
8
  attr_reader :schema
11
9
 
12
- def self.schema_path
13
- "/#{Syncano::Connection::API_VERSION}/schema/"
10
+ def initialize(connection)
11
+ self.connection = connection
12
+ load_schema
14
13
  end
15
14
 
16
- def initialize(connection = ::Syncano::Connection.new)
17
- self.connection = connection
15
+ def process!
16
+ schema.each do |resource_name, resource_definition|
17
+ self.class.generate_resource_class(resource_name, resource_definition)
18
+ if resource_definition[:collection].present? && resource_definition[:collection][:path].scan(/\{([^}]+)\}/).empty?
19
+ self.class.generate_client_method(resource_name)
20
+ end
21
+ end
18
22
  end
19
23
 
24
+ private
25
+
20
26
  attr_accessor :connection
27
+ attr_writer :schema
21
28
 
22
- def definition
23
- raw_schema = connection.request(:get, self.class.schema_path)
29
+ def load_schema
30
+ raw_schema = connection.request(:get, SCHEMA_PATH)
24
31
  resources = {}
25
32
 
26
33
  raw_schema.each do |resource_schema|
@@ -61,7 +68,149 @@ module Syncano
61
68
  end
62
69
  end
63
70
 
64
- resources
71
+ self.schema = resources
72
+ end
73
+
74
+ def self.generate_resource_class(name, definition_hash)
75
+ delete_colliding_links definition_hash
76
+
77
+ resource_definition = ::Syncano::Schema::ResourceDefinition.new(definition_hash)
78
+ resource_class = new_resource_class(resource_definition, name)
79
+
80
+ ::Syncano::Resources.const_set(name, resource_class)
81
+ end
82
+
83
+ def self.delete_colliding_links(definition)
84
+ definition[:attributes].each do |k, v|
85
+ definition[:associations]['links'].delete_if { |link| link['name'] == k } if definition[:associations]['links']
86
+ end
87
+ end
88
+
89
+ def self.new_resource_class(definition, name)
90
+ attributes_definitions = []
91
+
92
+ definition[:attributes].each do |attribute_name, attribute|
93
+ attributes_definitions << {
94
+ name: attribute_name,
95
+ type: map_syncano_attribute_type(attribute['type']),
96
+ default: attribute_name != 'channel' ? default_value_for_attribute(attribute) : nil,
97
+ presence_validation: attribute['required'],
98
+ length_validation_options: extract_length_validation_options(attribute),
99
+ inclusion_validation_options: extract_inclusion_validation_options(attribute),
100
+ create_writeable: attribute['read_only'] == false,
101
+ update_writeable: attribute['read_only'] == false,
102
+ }
103
+ end
104
+
105
+ ::Class.new(::Syncano::Resources::Base) do
106
+ self.create_writable_attributes = []
107
+ self.update_writable_attributes = []
108
+
109
+ attributes_definitions.each do |attribute_definition|
110
+ attribute attribute_definition[:name], type: attribute_definition[:type], default: attribute_definition[:default], force_default: !attribute_definition[:default].nil?
111
+ validates attribute_definition[:name], presence: true if attribute_definition[:presence_validation]
112
+ validates attribute_definition[:name], length: attribute_definition[:length_validation_options]
113
+
114
+ if attribute_definition[:inclusion_validation_options]
115
+ validates attribute_definition[:name], inclusion: attribute_definition[:inclusion_validation_options]
116
+ end
117
+
118
+ self.create_writable_attributes << attribute_definition[:name].to_sym if attribute_definition[:create_writeable]
119
+ self.update_writable_attributes << attribute_definition[:name].to_sym if attribute_definition[:update_writeable]
120
+ end
121
+
122
+ if name == 'Object' #TODO: extract to a separate module + spec
123
+ attribute :custom_attributes, type: ::Object, default: nil, force_default: true
124
+
125
+ def attributes=(new_attributes)
126
+ super
127
+
128
+ self.custom_attributes = new_attributes.select { |k, v| !self.class.attributes.keys.include?(k) }
129
+ end
130
+
131
+ def method_missing(method_name, *args, &block)
132
+ if method_name.to_s =~ /=$/
133
+ custom_attributes[method_name.to_s.gsub(/=$/, '')] = args.first
134
+ else
135
+ if custom_attributes.has_key? method_name.to_s
136
+ custom_attributes[method_name]
137
+ else
138
+ super
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ (definition[:associations]['links'] || []).each do |association_schema|
145
+ if association_schema['type'] == 'list'
146
+ define_method(association_schema['name']) do
147
+ has_many_association(association_schema['name'])
148
+ end
149
+ elsif association_schema['type'] == 'detail' && association_schema['name'] != 'self'
150
+ define_method(association_schema['name']) do
151
+ belongs_to_association(association_schema['name'])
152
+ end
153
+ elsif association_schema['type'] == 'run'
154
+ define_method(association_schema['name']) do |config = nil|
155
+ custom_method association_schema['name'], config
156
+ end
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ self.resource_definition = definition
163
+ end
164
+ end
165
+
166
+ def self.generate_client_method(resource_name)
167
+ method_name = resource_name.tableize
168
+ resource_class = "::Syncano::Resources::#{resource_name}".constantize
169
+
170
+ ::Syncano::API.send(:define_method, method_name) do
171
+ ::Syncano::QueryBuilder.new(connection, resource_class)
172
+ end
173
+ end
174
+
175
+ def self.extract_length_validation_options(attribute_definition)
176
+ maximum = begin
177
+ Integer attribute_definition['max_length']
178
+ rescue TypeError, ArgumentError
179
+ end
180
+
181
+ { maximum: maximum } unless maximum.nil?
182
+ end
183
+
184
+ def self.extract_inclusion_validation_options(attribute_definition)
185
+ return unless choices = attribute_definition['choices']
186
+
187
+ { in: choices.map { |choice| choice['value'] } }
188
+ end
189
+
190
+ def self.map_syncano_attribute_type(type)
191
+ mapping = HashWithIndifferentAccess.new(
192
+ string: ::String,
193
+ email: ::String,
194
+ choice: ::String,
195
+ slug: ::String,
196
+ integer: ::Integer,
197
+ float: ::Float,
198
+ date: ::Date,
199
+ datetime: ::DateTime,
200
+ field: ::Object
201
+ )
202
+
203
+ type.present? ? mapping[type] : Object
204
+ end
205
+
206
+ def self.default_value_for_attribute(attribute)
207
+ if attribute['type'].present? && attribute['type'].to_sym == :field
208
+ {}
209
+ elsif attribute['type'].present? && attribute['type'].to_sym == :choice
210
+ attribute['choices'].first['value']
211
+ else
212
+ nil
213
+ end
65
214
  end
66
215
  end
67
216
  end
@@ -1,82 +1,7 @@
1
1
  module Syncano
2
2
  class Schema
3
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
4
  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 validate?
26
- writable? && required?
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'].try(:first).try :[], 'value'
77
- else
78
- nil
79
- end
80
5
  end
81
6
  end
82
7
  end
@@ -2,40 +2,18 @@ module Syncano
2
2
  class Schema
3
3
  class ResourceDefinition
4
4
  attr_accessor :attributes
5
- attr_accessor :name
6
5
 
7
- def initialize(name, raw_defitnition)
6
+ def initialize(raw_defitnition)
8
7
  @raw_definition = raw_defitnition
9
8
 
10
- delete_colliding_links
11
-
12
- self.name = name
13
-
14
9
  self.attributes = raw_defitnition[:attributes].map do |name, raw_attribute_definition|
15
- Syncano::Schema::AttributeDefinition.new name, raw_attribute_definition
10
+ AttributeDefinition.new name, raw_attribute_definition
16
11
  end
17
12
  end
18
13
 
19
14
  def [](key)
20
15
  @raw_definition[key]
21
16
  end
22
-
23
- def top_level?
24
- @raw_definition[:collection].present? &&
25
- @raw_definition[:collection][:path].scan(/\{([^}]+)\}/).empty?
26
- end
27
-
28
- private
29
-
30
- def delete_colliding_links
31
- @raw_definition[:attributes].each do |k, v|
32
- if @raw_definition[:associations]['links']
33
- @raw_definition[:associations]['links'].delete_if do |link|
34
- link['name'] == k
35
- end
36
- end
37
- end
38
- end
39
17
  end
40
18
  end
41
19
  end
@@ -1,3 +1,3 @@
1
1
  module Syncano
2
- VERSION = '4.0.0.alpha4'
2
+ VERSION = '4.0.0.pre'
3
3
  end
@@ -4,32 +4,29 @@ WebMock.allow_net_connect!
4
4
 
5
5
  describe Syncano do
6
6
  before(:all) do
7
- Syncano::API.send :initialized=, false
8
-
9
7
  @api_key = ENV['INTEGRATION_TEST_API_KEY']
10
8
  @api = Syncano.connect(api_key: @api_key)
11
9
  end
12
10
 
13
11
  before(:each) do
14
12
  @api.instances.all.each &:destroy
15
- @instance = @api.instances.create(name: instance_name )
13
+ @instance = @api.instances.create(name: "a#{@api_key}")
16
14
  @instance.classes.all.select { |c| c.name != 'user_profile'}.each &:destroy
17
15
  @instance.groups.all.each &:destroy
18
16
  @instance.users.all.each &:delete
19
17
  end
20
18
 
21
- let(:instance_name) { "a#{SecureRandom.hex(24)}" }
22
19
  let(:group) { @instance.groups.create name: 'wheel' }
23
20
 
24
21
  describe 'working with instances' do
25
22
  subject { @api.instances }
26
23
 
27
24
  it 'should raise an error on not found instance' do
28
- expect { subject.find('kaszanka') }.to raise_error(Syncano::NotFound)
25
+ expect { subject.find('kaszanka') }.to raise_error(Syncano::ClientError)
29
26
  end
30
27
 
31
28
  specify do
32
- subject.create name: 'fafarafaa'
29
+ subject.create name: 'fafarafa'
33
30
  end
34
31
  end
35
32
 
@@ -37,7 +34,7 @@ describe Syncano do
37
34
  subject { @instance.classes }
38
35
 
39
36
  specify do
40
- expect { subject.create name: 'sausage', schema: [{name: 'name', type: 'string' }] }.to create_resource
37
+ expect { subject.create name: 'sausage', schema: [{name: 'name', type: 'string' }], group: group.primary_key }.to create_resource
41
38
 
42
39
  new_klass = subject.last
43
40
 
@@ -60,15 +57,18 @@ describe Syncano do
60
57
 
61
58
  describe 'working with objects' do
62
59
  before do
60
+ @owner = @instance.users.create username: 'admin', password: 'dupa.8'
63
61
  @class = @instance.classes.create name: 'account',
62
+ group: group.primary_key,
64
63
  schema: [{name: 'currency', type: 'string', filter_index: true},
65
64
  {name: 'ballance', type: 'integer', filter_index: true, order_index: true}]
66
65
  end
67
66
 
68
67
  subject { @class.objects }
69
68
 
69
+
70
70
  specify 'basic operations' do
71
- expect { subject.create currency: 'USD', ballance: 1337 }.to create_resource
71
+ expect { subject.create currency: 'USD', ballance: 1337, group: group.primary_key, owner: @owner.primary_key }.to create_resource
72
72
 
73
73
  object = subject.first
74
74
 
@@ -82,63 +82,25 @@ describe Syncano do
82
82
  expect(object.ballance).to eq(54)
83
83
  expect(object.currency).to eq('GBP')
84
84
 
85
- expect { subject.destroy(object.primary_key) }.to destroy_resource
86
- end
87
-
88
-
89
- specify 'PATH and POST' do
90
- initial_yuan = subject.create currency: 'CNY', ballance: 98123
91
-
92
- yuan = subject.first
93
- new_yuan = subject.first
94
-
95
- yuan.ballance = 100000
96
- yuan.save
97
-
98
- new_yuan.currency = 'RMB'
99
- new_yuan.save
100
-
101
- yuan = subject.first
102
-
103
- expect(yuan.currency).to eq('RMB')
104
- expect(yuan.ballance).to eq(100000)
105
-
106
- initial_yuan.save(overwrite: true)
107
- yuan.reload!
108
-
109
- expect(yuan.currency).to eq('CNY')
110
- expect(yuan.ballance).to eq(98123)
85
+ expect { object.destroy }.to destroy_resource
111
86
  end
112
87
 
113
88
  specify 'filtering and ordering' do
114
- usd = subject.create(currency: 'USD', ballance: 400)
115
- pln = subject.create(currency: 'PLN', ballance: 1600)
116
- eur = subject.create(currency: 'EUR', ballance: 400)
117
- gbp = subject.create(currency: 'GPB', ballance: 270)
118
- chf = subject.create(currency: 'CHF', ballance: 390)
119
- uah = subject.create(currency: 'UAH', ballance: 9100)
120
- rub = subject.create(currency: 'RUB')
89
+ usd = subject.create(currency: 'USD', ballance: 400, group: group.primary_key, owner: @owner.primary_key)
90
+ pln = subject.create(currency: 'PLN', ballance: 1600, group: group.primary_key, owner: @owner.primary_key)
91
+ eur = subject.create(currency: 'EUR', ballance: 400, group: group.primary_key, owner: @owner.primary_key)
92
+ gbp = subject.create(currency: 'GPB', ballance: 270, group: group.primary_key, owner: @owner.primary_key)
93
+ chf = subject.create(currency: 'CHF', ballance: 390, group: group.primary_key, owner: @owner.primary_key)
94
+ uah = subject.create(currency: 'UAH', ballance: 9100, group: group.primary_key, owner: @owner.primary_key)
95
+ rub = subject.create(currency: 'RUB', group: group.primary_key, owner: @owner.primary_key)
121
96
 
122
97
  expect(subject.all(query: { ballance: { _exists: true }}).to_a).to_not include(rub)
123
98
  expect(subject.all(query: { currency: { _in: %w[UAH USD PLN] } }).to_a).to match_array([pln, usd, uah])
124
99
  expect(subject.all(query: { ballance: { _lt: 400, _gte: 270 }}, order_by: '-ballance').to_a).to eq([chf, gbp])
125
100
  end
126
101
 
127
- specify 'fetching only specific fields' do
128
- subject.create(currency: 'USD', ballance: 400)
129
-
130
- account = subject.all(fields: 'currency').first
131
- expect { account.currency }.to_not raise_error
132
- expect { account.ballance }.to raise_error(NoMethodError)
133
-
134
-
135
- account = subject.first(excluded_fields: 'currency')
136
- expect { account.currency }.to raise_error(NoMethodError)
137
- expect { account.ballance }.to_not raise_error
138
- end
139
-
140
102
  specify 'paging', slow: true do
141
- 104.times { subject.create }
103
+ 104.times { subject.create group: group.primary_key, owner: @owner.primary_key }
142
104
 
143
105
  total = 0
144
106
  all = subject.all
@@ -162,7 +124,7 @@ describe Syncano do
162
124
 
163
125
 
164
126
  specify 'basic operations' do
165
- expect { subject.create label: 'df', source: 'puts 1337', runtime_name: 'ruby' }.to create_resource
127
+ expect { subject.create name: 'df', source: 'puts 1337', runtime_name: 'ruby' }.to create_resource
166
128
 
167
129
  codebox = subject.first
168
130
  codebox.run
@@ -170,7 +132,7 @@ describe Syncano do
170
132
  codebox.save
171
133
  codebox.run
172
134
 
173
- without_profiling { sleep 10 }
135
+ without_profiling { sleep 5 }
174
136
  traces = codebox.traces.all
175
137
 
176
138
  expect(traces.count).to eq(2)
@@ -178,13 +140,13 @@ describe Syncano do
178
140
  first = traces[1]
179
141
 
180
142
  expect(first.status).to eq('success')
181
- expect(first.result["stdout"]).to eq('1337')
143
+ expect(first.result).to eq('1337')
182
144
 
183
145
  second = traces[0]
184
146
  expect(second.status).to eq('success')
185
- expect(second.result["stdout"]).to eq('123')
147
+ expect(second.result).to eq('123')
186
148
 
187
- expect { @instance.schedules.create label: 'test', interval_sec: 30, codebox: codebox.primary_key }.
149
+ expect { @instance.schedules.create name: 'test', interval_sec: 30, codebox: codebox.primary_key }.
188
150
  to change { @instance.schedules.all.count }.by(1)
189
151
 
190
152
  expect { codebox.destroy }.to destroy_resource
@@ -194,218 +156,14 @@ describe Syncano do
194
156
  describe 'working with webhooks' do
195
157
  subject { @instance.webhooks }
196
158
 
197
- describe 'using the gem' do
198
- let!(:codebox) { @instance.codeboxes.create label: 'wurst', source: 'puts "currywurst"', runtime_name: 'ruby' }
199
-
200
- specify do
201
- expect { subject.create name: 'web-wurst', codebox: codebox.primary_key }.to create_resource
202
-
203
- expect(subject.first.run['result']["stdout"]).to eq('currywurst')
204
-
205
- expect { subject.first.destroy }.to destroy_resource
206
- end
207
- end
208
-
209
- describe 'using curl' do
210
- let(:source) {
211
- <<-SOURCE
212
- p ARGS["POST"]
213
- SOURCE
214
- }
215
- let!(:codebox) { @instance.codeboxes.create label: 'curl', source: source, runtime_name: 'ruby' }
216
- let!(:webhook) { subject.create name: 'web-curl', codebox: codebox.primary_key, public: true }
217
-
218
- specify do
219
- url = "#{ENV['API_ROOT']}/v1/instances/#{instance_name}/webhooks/p/#{webhook.public_link}/"
220
- code = %{curl -k --form kiszka=koza -H "kiszonka: 007" -X POST #{url} 2>/dev/null}
221
- output = JSON.parse(`#{code}`)
222
-
223
- expect(output["status"]).to eq("success")
224
- expect(output["result"]["stdout"]).to eq('{"kiszka"=>"koza"}')
225
- end
226
- end
227
- end
228
-
229
- describe 'working with API keys' do
230
- subject { @instance.api_keys }
231
-
232
- specify do
233
- api_key = nil
234
-
235
- expect {
236
- api_key = subject.create allow_user_create: true
237
- }.to create_resource
238
-
239
- expect { api_key.destroy }.to destroy_resource
240
- end
241
- end
242
-
243
- describe 'managing users' do
244
- subject { @instance.users }
245
-
246
- let(:user_profile) { @instance.classes.find("user_profile") }
247
-
248
- before do
249
- user_profile.schema = [{ name: "nickname", type: "text" },
250
- { name: "resume", type: "file" }]
251
- user_profile.save
252
- end
159
+ let!(:codebox) { @instance.codeboxes.create name: 'wurst', source: 'puts "currywurst"', runtime_name: 'ruby' }
253
160
 
254
161
  specify do
255
- user = nil
256
-
257
- expect {
258
- user = subject.create(username: 'koza', password: 'kiszkakoza')
259
- }.to create_resource
260
-
261
- user.update_attributes username: 'kiszka'
262
- expect(subject.find(user.primary_key).username).to eq('kiszka')
263
-
264
-
265
- profile = @instance.classes.find("user_profile").objects.find(1)
266
- profile.nickname = "k0z4"
267
- profile.resume = Syncano::UploadIO.new(File.absolute_path(__FILE__))
268
- profile.save
269
-
270
- expect(profile.nickname).to eq("k0z4")
271
-
272
- expect { user.destroy }.to destroy_resource
273
- end
274
- end
275
-
276
-
277
- describe 'managing groups' do
278
- subject { @instance.groups }
279
-
280
- specify do
281
- creator = @instance.users.create username: 'content', password: 'creator'
282
-
283
- content_creators = nil
284
-
285
- expect {
286
- content_creators = subject.create label: 'content creators'
287
- }.to create_resource
288
-
289
- expect {
290
- content_creators.users.create user: creator.primary_key
291
- }.to change { content_creators.users.all.count }.from(0).to(1)
292
-
293
- expect { content_creators.destroy }.to destroy_resource
294
- end
295
- end
296
-
297
- describe 'managing channels' do
298
- subject do
299
- @instance.channels
300
- end
301
-
302
- specify do
303
- channel = nil
304
- expect { channel = subject.create(name: 'chat') }.to create_resource
305
- expect { channel.destroy }.to destroy_resource
306
- end
307
- end
308
-
309
-
310
- describe 'subscribing to a channel' do
311
- before(:each) { Celluloid.boot }
312
-
313
- after(:each) { Celluloid.shutdown }
314
-
315
- let!(:notifications) do
316
- @instance.classes.create(name: 'notifications', schema: [{name: 'message', type: 'string'}]).objects
317
- end
318
-
319
- let!(:notifications_channel) do
320
- @instance.channels.create name: 'system-notifications', other_permissions: 'subscribe'
321
- end
322
-
323
- specify do
324
- poller = notifications_channel.poll
325
-
326
- notification = notifications.create(message: "A new koza's arrived", channel: 'system-notifications')
327
-
328
- sleep 20
329
-
330
- expect(poller.responses.size).to eq(1)
331
- expect(JSON.parse(poller.responses.last.body)["payload"]["message"]).to eq("A new koza's arrived")
332
-
333
- notification.message = "A koza's gone"
334
- notification.save
335
-
336
- sleep 20
337
-
338
- expect(poller.responses.size).to eq(2)
339
- expect(JSON.parse(poller.responses.last.body)["payload"]["message"]).to eq("A koza's gone")
340
-
341
- poller.terminate
342
- end
343
- end
344
-
345
- describe 'subscribing to a room' do
346
- before(:each) { Celluloid.boot }
347
-
348
- after(:each) { Celluloid.shutdown }
349
-
350
- let!(:house) {
351
- @instance.channels.create name: 'house', type: 'separate_rooms', other_permissions: 'publish', custom_publish: true
352
- }
353
- let!(:shout) {
354
- @instance.classes.create name: 'shout', schema: [{name: 'message', type: 'string'}]
355
- }
356
-
357
- specify do
358
- shout.objects.create channel: 'house', channel_room: 'bathroom', message: "Where's the water?"
359
- shout.objects.create channel: 'house', channel_room: 'basement', message: "Where's the light?"
360
-
361
- expect(house.history.all(room: 'bathroom').count).to eq(1)
362
- expect(house.history.all(room: 'basement').count).to eq(1)
363
- expect(house.history.all.count).to eq(2)
364
- end
365
- end
366
-
367
- describe 'using syncano on behalf of the user' do
368
- let(:user_api_key) { @instance.api_keys.create.api_key }
369
- let(:user) {
370
- @instance.users.create username: 'kiszonka', password: 'passwd'
371
- }
372
- let(:another_user) {
373
- @instance.users.create username: 'another', password: 'user'
374
- }
375
- let(:user_instance) {
376
- Syncano.connect(api_key: user_api_key, user_key: user.user_key).
377
- instances.first
378
- }
379
- let(:another_user_instance) {
380
- Syncano.connect(api_key: user_api_key, user_key: another_user.user_key).
381
- instances.first
382
- }
383
- let(:group) { @instance.groups.create label: 'content creators' }
384
-
385
- before do
386
- group.users.create user: user.primary_key
387
- group.users.create user: another_user.primary_key
388
-
389
- @instance.classes.create name: 'book',
390
- schema: [{ name: 'title', type: 'string' }],
391
- group: group.primary_key,
392
- group_permissions: 'create_objects'
393
- end
394
-
395
- specify do
396
- owner_books = user_instance.classes.find('book').objects
397
- book = owner_books.create(title: 'Oliver Twist', owner_permissions: 'write')
398
-
399
- expect(owner_books.all.to_a).to_not be_empty
400
-
401
- group_member_books = another_user_instance.classes.find('book').objects
402
- expect(group_member_books.all.to_a).to be_empty
162
+ expect { subject.create slug: 'web-wurst', codebox: codebox.primary_key }.to create_resource
403
163
 
404
- book.group_permissions = 'read'
405
- book.group = group.primary_key
406
- book.save
164
+ expect(subject.first.run['result']).to eq('currywurst')
407
165
 
408
- expect(group_member_books.all.to_a).to_not be_empty
166
+ expect { subject.first.destroy }.to destroy_resource
409
167
  end
410
168
  end
411
169