syncano 4.0.0.alpha4 → 4.0.0.pre
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 +4 -4
- data/.ruby-version +1 -1
- data/README.md +1 -13
- data/circle.yml +1 -1
- data/lib/active_attr/dirty.rb +26 -0
- data/lib/active_attr/typecasting/hash_typecaster.rb +34 -0
- data/lib/active_attr/typecasting_override.rb +29 -0
- data/lib/syncano.rb +9 -55
- data/lib/syncano/api.rb +2 -20
- data/lib/syncano/connection.rb +47 -48
- data/lib/syncano/model/associations.rb +121 -0
- data/lib/syncano/model/associations/base.rb +38 -0
- data/lib/syncano/model/associations/belongs_to.rb +30 -0
- data/lib/syncano/model/associations/has_many.rb +75 -0
- data/lib/syncano/model/associations/has_one.rb +22 -0
- data/lib/syncano/model/base.rb +257 -0
- data/lib/syncano/model/callbacks.rb +49 -0
- data/lib/syncano/model/scope_builder.rb +158 -0
- data/lib/syncano/query_builder.rb +7 -11
- data/lib/syncano/resources/base.rb +66 -91
- data/lib/syncano/schema.rb +159 -10
- data/lib/syncano/schema/attribute_definition.rb +0 -75
- data/lib/syncano/schema/resource_definition.rb +2 -24
- data/lib/syncano/version.rb +1 -1
- data/spec/integration/syncano_spec.rb +26 -268
- data/spec/spec_helper.rb +1 -3
- data/spec/unit/connection_spec.rb +74 -34
- data/spec/unit/query_builder_spec.rb +2 -2
- data/spec/unit/resources_base_spec.rb +64 -125
- data/spec/unit/schema/resource_definition_spec.rb +3 -24
- data/spec/unit/schema_spec.rb +55 -5
- data/spec/unit/syncano_spec.rb +9 -45
- data/syncano.gemspec +0 -5
- metadata +14 -87
- data/lib/syncano/api/endpoints.rb +0 -17
- data/lib/syncano/poller.rb +0 -55
- data/lib/syncano/resources.rb +0 -158
- data/lib/syncano/resources/paths.rb +0 -48
- data/lib/syncano/resources/resource_invalid.rb +0 -15
- data/lib/syncano/response.rb +0 -55
- data/lib/syncano/schema/endpoints_whitelist.rb +0 -40
- data/lib/syncano/upload_io.rb +0 -7
- data/spec/unit/resources/paths_spec.rb +0 -21
- data/spec/unit/response_spec.rb +0 -75
- data/spec/unit/schema/attribute_definition_spec.rb +0 -18
data/lib/syncano/schema.rb
CHANGED
@@ -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
|
13
|
-
|
10
|
+
def initialize(connection)
|
11
|
+
self.connection = connection
|
12
|
+
load_schema
|
14
13
|
end
|
15
14
|
|
16
|
-
def
|
17
|
-
|
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
|
23
|
-
raw_schema = connection.request(:get,
|
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(
|
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
|
-
|
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
|
data/lib/syncano/version.rb
CHANGED
@@ -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:
|
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::
|
25
|
+
expect { subject.find('kaszanka') }.to raise_error(Syncano::ClientError)
|
29
26
|
end
|
30
27
|
|
31
28
|
specify do
|
32
|
-
subject.create name: '
|
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 {
|
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
|
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
|
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
|
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
|
147
|
+
expect(second.result).to eq('123')
|
186
148
|
|
187
|
-
expect { @instance.schedules.create
|
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
|
-
|
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
|
-
|
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
|
-
|
405
|
-
book.group = group.primary_key
|
406
|
-
book.save
|
164
|
+
expect(subject.first.run['result']).to eq('currywurst')
|
407
165
|
|
408
|
-
expect
|
166
|
+
expect { subject.first.destroy }.to destroy_resource
|
409
167
|
end
|
410
168
|
end
|
411
169
|
|