restful_objects 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe '/domain-types/:type/actions/:action' do
5
+ before(:all) do
6
+ RestfulObjects::DomainModel.current.reset
7
+ end
8
+
9
+ it 'should generate an action description representation' do
10
+ class ActionTest
11
+ include RestfulObjects::Object
12
+ action :do_something, :int, {}, friendly_name: 'do something!', description: 'description of something'
13
+ end
14
+
15
+ expected = {
16
+ 'id' => 'do_something',
17
+ 'friendlyName' => 'do something!',
18
+ 'description' => 'description of something',
19
+ 'hasParams' => false,
20
+ 'memberOrder' => :integer,
21
+ 'parameters' => {},
22
+ 'links' => [
23
+ { 'rel' => 'self',
24
+ 'href' => 'http://localhost/domain-types/ActionTest/actions/do_something',
25
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-description"',
26
+ 'method' => 'GET' },
27
+ { 'rel' => 'up',
28
+ 'href' => 'http://localhost/domain-types/ActionTest',
29
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
30
+ 'method' => 'GET' },
31
+ { 'rel' => 'urn:org.restfulobjects:rels/return-type',
32
+ 'href' => 'http://localhost/domain-types/int',
33
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
34
+ 'method' => 'GET' }
35
+ ]
36
+ }
37
+
38
+ get '/domain-types/ActionTest/actions/do_something'
39
+
40
+ last_response.body.should match_json_expression expected
41
+ end
42
+ end
43
+
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe '/domain-types/:type/collections/:collection' do
5
+ before(:all) do
6
+ RestfulObjects::DomainModel.current.reset
7
+ end
8
+
9
+ it 'should generate a collection description representation' do
10
+ class CollectionTest
11
+ include RestfulObjects::Object
12
+ collection :items, 'ItemClass', friendly_name: 'item collection', description: 'a collection description'
13
+ end
14
+
15
+ expected = {
16
+ 'id' => 'items',
17
+ 'friendlyName' => 'item collection',
18
+ 'description' => 'a collection description',
19
+ 'memberOrder' => 1,
20
+ 'links' => [
21
+ { 'rel' => 'self',
22
+ 'href' => 'http://localhost/domain-types/CollectionTest/collections/items',
23
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/collection-description"',
24
+ 'method' => 'GET' },
25
+ { 'rel' => 'up',
26
+ 'href' => 'http://localhost/domain-types/CollectionTest',
27
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
28
+ 'method' => 'GET' },
29
+ { 'rel' => 'urn:org.restfulobjects:rels/return-type',
30
+ 'href' => 'http://localhost/domain-types/list',
31
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
32
+ 'method' => 'GET' },
33
+ { 'rel' => 'urn:org.restfulobjects:rels/element-type',
34
+ 'href' => 'http://localhost/domain-types/ItemClass',
35
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
36
+ 'method' => 'GET' }
37
+ ],
38
+ 'extensions' => wildcard_matcher
39
+ }
40
+
41
+ get '/domain-types/CollectionTest/collections/items'
42
+ last_response.body.should match_json_expression expected
43
+ end
44
+ end
45
+
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe '/domain-types/:type/properties/:property' do
5
+ before(:all) do
6
+ RestfulObjects::DomainModel.current.reset
7
+ end
8
+
9
+ it 'should generate a property description representation' do
10
+ class PropertyTest
11
+ include RestfulObjects::Object
12
+ property :name, :string, friendly_name: 'a friendly name', description: 'name description'
13
+ end
14
+
15
+ expected = {
16
+ 'id' => 'name',
17
+ 'friendlyName' => 'a friendly name',
18
+ 'description' => 'name description',
19
+ 'optional' => true,
20
+ 'memberOrder' => :integer,
21
+ 'links' => [
22
+ { 'rel' => 'self',
23
+ 'href' => 'http://localhost/domain-types/PropertyTest/properties/name',
24
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/property-description"',
25
+ 'method' => 'GET' },
26
+ { 'rel' => 'up',
27
+ 'href' => 'http://localhost/domain-types/PropertyTest',
28
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
29
+ 'method' => 'GET' },
30
+ { 'rel' => 'urn:org.restfulobjects:rels/return-type',
31
+ 'href' => 'http://localhost/domain-types/string',
32
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
33
+ 'method' => 'GET' }
34
+ ]
35
+ }
36
+
37
+ get '/domain-types/PropertyTest/properties/name'
38
+ last_response.body.should match_json_expression expected
39
+ end
40
+ end
41
+
@@ -0,0 +1,98 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe '/' do
5
+ before(:all) do
6
+ RestfulObjects::DomainModel.current.reset
7
+ end
8
+
9
+ it 'should generate a homepage resource' do
10
+ homepage = {
11
+ 'links' => [
12
+ { 'rel' => 'self',
13
+ 'href' => 'http://localhost/',
14
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/homepage"',
15
+ 'method' => 'GET' },
16
+ { 'rel' => 'urn:org.restfulobjects:rels/user',
17
+ 'href' => 'http://localhost/user',
18
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/user"',
19
+ 'method' => 'GET' },
20
+ { 'rel' => 'urn:org.restfulobjects:rels/services',
21
+ 'href' => 'http://localhost/services',
22
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/list"',
23
+ 'method' => 'GET' },
24
+ { 'rel' => 'urn:org.restfulobjects:rels/version',
25
+ 'href' => 'http://localhost/version',
26
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/version"',
27
+ 'method' => 'GET' },
28
+ { 'rel' => 'urn:org.restfulobjects:rels/domain-types',
29
+ 'href' => 'http://localhost/domain-types',
30
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/type-list"',
31
+ 'method' => 'GET' },
32
+ ],
33
+ 'extensions' => { }
34
+ }
35
+
36
+ get '/'
37
+ last_response.body.should match_json_expression homepage
38
+ end
39
+
40
+ it 'should generate a version resource' do
41
+ version = {
42
+ 'links' => [
43
+ { 'rel' => 'self',
44
+ 'href' => 'http://localhost/version',
45
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/version"',
46
+ 'method' => 'GET' },
47
+ { 'rel' => 'up',
48
+ 'href' => 'http://localhost/',
49
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/homepage"',
50
+ 'method' => 'GET' }
51
+ ],
52
+ 'specVersion' => '1.0',
53
+ 'optionalCapabilities' => {
54
+ 'blobsClobs' => true,
55
+ 'deleteObjects' => true,
56
+ 'domainModel' => 'selectable',
57
+ 'protoPersistentObjects' => true,
58
+ 'validateOnly' => false
59
+ },
60
+ 'extensions' => {}
61
+ }
62
+
63
+ get '/version'
64
+ last_response.body.should match_json_expression version
65
+ end
66
+
67
+ it 'should generate a services list resource' do
68
+ class ServiceTest
69
+ include RestfulObjects::Service
70
+ end
71
+
72
+ RestfulObjects::DomainModel.current.services['ServiceTest'].title = 'Service Test Title'
73
+
74
+ services_list = {
75
+ 'links' => [
76
+ { 'rel' => 'self',
77
+ 'href' => 'http://localhost/services',
78
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/services"',
79
+ 'method' => 'GET' },
80
+ { 'rel' => 'up',
81
+ 'href' => 'http://localhost/',
82
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/homepage"',
83
+ 'method' => 'GET' }
84
+ ],
85
+ 'value' => [
86
+ { 'rel' => 'urn:org.restfulobjects:rels/service;serviceId="ServiceTest"',
87
+ 'href' => 'http://localhost/services/ServiceTest',
88
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
89
+ 'method' => 'GET',
90
+ 'title' => 'Service Test Title' }
91
+ ],
92
+ 'extensions' => { }
93
+ }
94
+
95
+ get '/services'
96
+ last_response.body.should match_json_expression services_list
97
+ end
98
+ end
@@ -0,0 +1,52 @@
1
+ require 'json_expressions/rspec'
2
+ require 'rack/test'
3
+ require 'pp'
4
+
5
+ require_relative '../lib/restful_objects.rb'
6
+
7
+ module Helpers
8
+ def app
9
+ RestfulObjects::Server
10
+ end
11
+
12
+ def model
13
+ RestfulObjects::DomainModel.current
14
+ end
15
+
16
+ def pretty_print_json(json_string)
17
+ puts JSON.pretty_generate(JSON.parse(json_string))
18
+ end
19
+ end
20
+
21
+ module JsonExpressions
22
+ module RSpec
23
+ module Matchers
24
+ class MatchJsonExpression
25
+ def failure_message_for_should
26
+ "expected:\n#{JSON.pretty_generate @target}\n to match JSON expression:\n#{@expected.inspect}\n\n" +
27
+ @expected.last_error
28
+ end
29
+
30
+ def failure_message_for_should_not
31
+ "expected:\n#{JSON.pretty_generate @target}\n not to match JSON expression:\n#{@expected.inspect}\n"
32
+ end
33
+
34
+ def description
35
+ "should equal JSON expression:\n#{@expected.inspect}\n"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+
43
+ RSpec::configure do |config|
44
+ config.include(Helpers)
45
+ config.include Rack::Test::Methods
46
+ end
47
+
48
+ JsonExpressions::Matcher.assume_strict_arrays = false
49
+ JsonExpressions::Matcher.assume_strict_hashes = false
50
+
51
+ RestfulObjects::Server.set :show_exceptions, false
52
+ RestfulObjects::Server.set :raise_errors, true
@@ -0,0 +1,380 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe RestfulObjects::ObjectActions do
5
+ before :all do
6
+ RestfulObjects::DomainModel.current.reset
7
+
8
+ class ActionResult
9
+ include RestfulObjects::Object
10
+ end
11
+
12
+ class ActionsTest
13
+ include RestfulObjects::Object
14
+
15
+ property :string_prop, :string
16
+ property :int_prop, :int
17
+ property :decimal_prop, :decimal
18
+ property :date_prop, :date
19
+ property :blob_prop, :blob
20
+
21
+ action :string_action, :string
22
+ action :int_action, :int
23
+ action :decimal_action, :decimal
24
+ action :date_action, :date
25
+ action :blob_action, :blob
26
+
27
+ def string_action
28
+ string_prop
29
+ end
30
+
31
+ def int_action
32
+ int_prop
33
+ end
34
+
35
+ def decimal_action
36
+ decimal_prop
37
+ end
38
+
39
+ def date_action
40
+ date_prop
41
+ end
42
+
43
+ def blob_action
44
+ blob_prop
45
+ end
46
+
47
+ action :multiply, :int, { 'arg1' => :int, 'arg2' => :int }
48
+ action :get_nil_scalar, :int
49
+ action :get_object, [:object, ActionResult]
50
+ action :get_nil_object, [:object, ActionResult]
51
+ action :get_list, [:list, ActionResult]
52
+ action :get_nil_list, [:list, ActionResult]
53
+
54
+ def multiply(arg1, arg2)
55
+ arg1 * arg2
56
+ end
57
+
58
+ def get_nil_scalar
59
+ nil
60
+ end
61
+
62
+ def get_object
63
+ @cached ||= ActionResult.new
64
+ end
65
+
66
+ def get_nil_object
67
+ nil
68
+ end
69
+
70
+ def get_list
71
+ @cached ||= [ActionResult.new, ActionResult.new]
72
+ end
73
+
74
+ def get_nil_list
75
+ nil
76
+ end
77
+ end
78
+ end
79
+
80
+ before :each do
81
+ @object = ActionsTest.new
82
+ end
83
+
84
+ it 'should get action' do
85
+ expected = {
86
+ 'id' => 'multiply',
87
+ 'parameters' => {
88
+ 'arg1' => {
89
+ 'links' => [],
90
+ 'extensions' => { }
91
+ },
92
+ 'arg2' => {
93
+ 'links' => [],
94
+ 'extensions' => { }
95
+ },
96
+ },
97
+ 'links' => [
98
+ { 'rel' => 'self',
99
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/multiply",
100
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-action"',
101
+ 'method' => 'GET' },
102
+ { 'rel' => 'up',
103
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}",
104
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
105
+ 'method' => 'GET' },
106
+ {
107
+ 'rel' => 'urn:org.restfulobjects:rels/invoke;action="multiply"',
108
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/multiply/invoke",
109
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
110
+ 'method' => 'GET',
111
+ 'arguments' => {
112
+ 'arg1' => { 'value' => nil },
113
+ 'arg2' => { 'value' => nil }
114
+ }
115
+ }
116
+ ]
117
+ }
118
+
119
+ get "/objects/ActionsTest/#{@object.object_id}/actions/multiply"
120
+
121
+ last_response.body.should match_json_expression expected
122
+ end
123
+
124
+ it 'should process different actions result types' do
125
+ @object.string_prop = 'A string'
126
+ @object.int_prop = 1234
127
+ @object.decimal_prop = 333.33
128
+ @object.date_prop = Date.new(2012, 2, 29)
129
+ @object.blob_prop = "\xE5\xA5\xB4\x30\xF2\x8C\x71\xD9"
130
+
131
+ expected = {
132
+ 'string_action' => 'A string',
133
+ 'int_action' => 1234,
134
+ 'decimal_action' => 333.33,
135
+ 'date_action' => '2012-02-29',
136
+ 'blob_action' => '5aW0MPKMcdk=' }
137
+
138
+ expected.each do |action, value|
139
+ get "/objects/ActionsTest/#{@object.object_id}/actions/#{action}/invoke"
140
+ JSON.parse(last_response.body)['result']['value'].should eq value
141
+ end
142
+ end
143
+
144
+ it 'should invoke action (get) with simple arguments as a query string' do
145
+ expected = {
146
+ 'resultType' => 'scalar',
147
+ 'result' => {
148
+ 'value' => 28,
149
+ }
150
+ }
151
+
152
+ get "/objects/ActionsTest/#{@object.object_id}/actions/multiply/invoke?arg1=4&arg2=7"
153
+
154
+ last_response.body.should match_json_expression expected
155
+ end
156
+
157
+ it 'should invoke action get with arguments returning scalar' do
158
+ arguments = { 'arg1' => { 'value' => 3 },
159
+ 'arg2' => { 'value' => 9 } }.to_json
160
+
161
+ expected = {
162
+ 'links' => [
163
+ { 'rel' => 'self',
164
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/multiply/invoke",
165
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
166
+ 'arguments' =>
167
+ { 'arg1' => { 'value' => 3 },
168
+ 'arg2' => { 'value' => 9 } },
169
+ 'method' => 'GET' }
170
+ ],
171
+ 'resultType' => 'scalar',
172
+ 'result' => {
173
+ 'links' => [
174
+ { 'rel' => 'urn:org.restfulobjects:rels/return-type',
175
+ 'href' => 'http://localhost/domain-types/int',
176
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
177
+ 'method' => 'GET' } ],
178
+ 'value' => 27,
179
+ 'extensions' => { }
180
+ },
181
+ 'extensions' => {}
182
+ }
183
+
184
+ get "/objects/ActionsTest/#{@object.object_id}/actions/multiply/invoke", {}, {input: arguments}
185
+
186
+ last_response.body.should match_json_expression expected
187
+ end
188
+
189
+ it 'should invoke action and return a null scalar representation' do
190
+ expected = {
191
+ 'links' => [
192
+ { 'rel' => 'self',
193
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/get_nil_scalar/invoke",
194
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
195
+ 'method' => 'GET',
196
+ 'arguments' => nil }
197
+ ],
198
+ 'resultType' => 'scalar',
199
+ 'result' => nil,
200
+ 'extensions' => {}
201
+ }
202
+
203
+ get "/objects/ActionsTest/#{@object.object_id}/actions/get_nil_scalar/invoke"
204
+
205
+ last_response.body.should match_json_expression expected
206
+ end
207
+
208
+ it 'should invoke action and return an object representation' do
209
+ action_result = @object.get_object
210
+
211
+ expected = {
212
+ 'links' => [
213
+ { 'rel' => 'self',
214
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/get_object/invoke",
215
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
216
+ 'method' => 'GET',
217
+ 'arguments' => nil }
218
+ ],
219
+ 'resultType' => 'object',
220
+ 'result' => {
221
+ 'instanceId' => "#{action_result.object_id}",
222
+ 'links' => [
223
+ { 'rel' => 'self',
224
+ 'href' => "http://localhost/objects/ActionResult/#{action_result.object_id}",
225
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
226
+ 'method' => 'GET' },
227
+ { 'rel' => 'describedby',
228
+ 'href' => "http://localhost/domain-types/ActionResult",
229
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
230
+ 'method' => 'GET' }
231
+ ],
232
+ 'members' => wildcard_matcher
233
+ }
234
+ }
235
+
236
+ get "/objects/ActionsTest/#{@object.object_id}/actions/get_object/invoke"
237
+
238
+ last_response.body.should match_json_expression expected
239
+ end
240
+
241
+ it 'should invoke action and return a null object representation' do
242
+ expected = {
243
+ 'links' => [
244
+ { 'rel' => 'self',
245
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/get_nil_object/invoke",
246
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
247
+ 'method' => 'GET',
248
+ 'arguments' => nil }
249
+ ],
250
+ 'resultType' => 'object',
251
+ 'result' => nil,
252
+ 'extensions' => { }
253
+ }
254
+
255
+ get "/objects/ActionsTest/#{@object.object_id}/actions/get_nil_object/invoke"
256
+
257
+ last_response.body.should match_json_expression expected
258
+ end
259
+
260
+ it 'should invoke action and return a list representation' do
261
+ action_result = @object.get_list
262
+
263
+ expected = {
264
+ 'links' => [
265
+ { 'rel' => 'self',
266
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/get_list/invoke",
267
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
268
+ 'method' => 'GET',
269
+ 'arguments' => nil }
270
+ ],
271
+ 'resultType' => 'list',
272
+ 'result' => {
273
+ 'links' => [
274
+ { 'rel' => 'urn:org.restfulobjects:rels/element-type',
275
+ 'href' => "http://localhost/domain-types/ActionResult",
276
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
277
+ 'method' => 'GET' } ],
278
+ 'value' => [
279
+ { 'rel' =>'urn:org.restfulobjects:rels/element',
280
+ 'href' => "http://localhost/objects/ActionResult/#{action_result[0].object_id}",
281
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
282
+ 'method' => 'GET' },
283
+ { 'rel' =>'urn:org.restfulobjects:rels/element',
284
+ 'href' => "http://localhost/objects/ActionResult/#{action_result[1].object_id}",
285
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
286
+ 'method' => 'GET' }
287
+ ],
288
+ 'extensions' => { }
289
+ },
290
+ 'extensions' => { }
291
+ }
292
+
293
+ get "/objects/ActionsTest/#{@object.object_id}/actions/get_list/invoke"
294
+
295
+ last_response.body.should match_json_expression expected
296
+ end
297
+
298
+ it 'should invoke action and return a null list respresentation' do
299
+ expected = {
300
+ 'links' => [
301
+ { 'rel' => 'self',
302
+ 'href' => "http://localhost/objects/ActionsTest/#{@object.object_id}/actions/get_nil_list/invoke",
303
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/action-result"',
304
+ 'method' => 'GET',
305
+ 'arguments' => nil }
306
+ ],
307
+ 'resultType' => 'list',
308
+ 'result' => nil,
309
+ 'extensions' => { }
310
+ }
311
+
312
+ get "/objects/ActionsTest/#{@object.object_id}/actions/get_nil_list/invoke"
313
+
314
+ last_response.body.should match_json_expression expected
315
+ end
316
+
317
+ it 'should generate metadata in extensions' do
318
+ class ActionsTest
319
+ action :action_full_metadata, :string, {}, friendly_name: 'Meta Action', description: 'To Test Metadata'
320
+ end
321
+
322
+ expected = {
323
+ 'id' => 'action_full_metadata',
324
+ 'extensions' => {
325
+ 'friendlyName' => 'Meta Action',
326
+ 'description' => 'To Test Metadata',
327
+ 'returnType' => 'string',
328
+ 'hasParams' => false,
329
+ 'memberOrder' => Fixnum
330
+ }.strict!
331
+ }
332
+
333
+ get "/objects/ActionsTest/#{@object.object_id}/actions/action_full_metadata"
334
+
335
+ last_response.body.should match_json_expression expected
336
+ end
337
+
338
+ it 'should generate metadata for parameters in extensions' do
339
+ class ActionsTest
340
+ action :metadata_test, :void, { param1: :string,
341
+ param2: [:int, { optional: false, friendly_name: 'friendly', description: 'description' }],
342
+ param3: ActionResult }
343
+ end
344
+
345
+ expected = {
346
+ 'id' => 'metadata_test',
347
+ 'parameters' => {
348
+ 'param1' => {
349
+ 'extensions' => {
350
+ 'friendlyName' => 'param1',
351
+ 'description' => 'param1',
352
+ 'returnType' => 'string',
353
+ 'optional' => true
354
+ }
355
+ },
356
+ 'param2' => {
357
+ 'extensions' => {
358
+ 'friendlyName' => 'friendly',
359
+ 'description' => 'description',
360
+ 'returnType' => 'int',
361
+ 'optional' => false
362
+ },
363
+ },
364
+ 'param3' => {
365
+ 'extensions' => {
366
+ 'friendlyName' => 'param3',
367
+ 'description' => 'param3',
368
+ 'returnType' => 'ActionResult',
369
+ 'optional' => true
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ get "/objects/ActionsTest/#{@object.object_id}/actions/metadata_test"
376
+
377
+ last_response.body.should match_json_expression expected
378
+ end
379
+ end
380
+