api-transformer 0.1.0

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.
@@ -0,0 +1,746 @@
1
+ require "goliath/test_helper"
2
+ require "goliath/test_helper_streaming"
3
+
4
+ require_relative "./spec_helper"
5
+ require_relative "../lib/api_transformer"
6
+
7
+ def create_server(&block)
8
+ server = Class.new(ApiTransformer::Server) do
9
+ base_url "http://localhost:9900"
10
+ end
11
+
12
+ server.instance_eval(&block)
13
+
14
+ server
15
+ end
16
+
17
+ def extend_server(server, &block)
18
+ server.instance_eval(&block)
19
+ server
20
+ end
21
+
22
+ def echo_greeting_server
23
+ create_server do
24
+ get "/echo_greeting" do |params|
25
+ response do
26
+ attribute :greeting, params[:greeting]
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe ApiTransformer::Server do
33
+ include Goliath::TestHelper
34
+
35
+ let(:err) { proc { |c| fail "HTTP Request failed #{c.response}" } }
36
+
37
+ before do
38
+ ApiTransformer::Server.reset_routes
39
+ end
40
+
41
+ it "works without any backend requests" do
42
+ no_backend = create_server do
43
+ get "/ping" do
44
+ response do
45
+ success { attribute :result, "pong" }
46
+ end
47
+ end
48
+ end
49
+
50
+ with_api(no_backend) do
51
+ get_request(path: "/ping") do |c|
52
+ c.response.must_equal '{"result":"pong"}'
53
+ end
54
+ end
55
+ end
56
+
57
+ it "works with a backend request" do
58
+ backend = create_server do
59
+ get "/one" do
60
+ request :two do
61
+ path "/two"
62
+ method :get
63
+ end
64
+
65
+ response do |data|
66
+ success { attribute :two, data[:two].json }
67
+ end
68
+ end
69
+
70
+ get "/two" do
71
+ response do
72
+ success { attribute :result, "OK" }
73
+ end
74
+ end
75
+ end
76
+
77
+ with_api(backend) do
78
+ get_request(path: "/one") do |c|
79
+ c.response.must_equal '{"two":{"result":"OK"}}'
80
+ end
81
+ end
82
+ end
83
+
84
+ it "fails without a method on backend requests" do
85
+ server = create_server do
86
+ get "/nomethod" do
87
+ request :somewhere do
88
+ path "/somewhere"
89
+ end
90
+
91
+ response do
92
+ success 204
93
+ end
94
+ end
95
+ end
96
+
97
+ with_api(server) do
98
+ get_request(path: "/nomethod") do |c|
99
+ c.response.must_equal "Missing method for backend request: somewhere"
100
+ end
101
+ end
102
+ end
103
+
104
+ it "works with a namespace" do
105
+ namspaced = create_server do
106
+ namespace "/first" do
107
+ get "/second" do
108
+ response do
109
+ success { attribute :result, "Found me!" }
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ with_api(namspaced) do
116
+ get_request(path: "/first/second") do |c|
117
+ c.response.must_equal '{"result":"Found me!"}'
118
+ end
119
+ end
120
+ end
121
+
122
+ it "accepts path params from incoming requests" do
123
+ server = create_server do
124
+ get "/echo_greeting/:greeting" do |params|
125
+ response do
126
+ attribute :greeting, params[:greeting]
127
+ end
128
+ end
129
+ end
130
+
131
+ with_api(server) do
132
+ get_request(path: "/echo_greeting/hola") do |c|
133
+ c.response.must_equal '{"greeting":"hola"}'
134
+ end
135
+ end
136
+ end
137
+
138
+ it "accepts query params from incoming requests" do
139
+ with_api(echo_greeting_server) do
140
+ get_request(path: "/echo_greeting", query: { greeting: "yo" }) do |c|
141
+ c.response.must_equal '{"greeting":"yo"}'
142
+ end
143
+ end
144
+ end
145
+
146
+ it "accepts form params from incoming requests" do
147
+ with_api(echo_greeting_server) do
148
+ get_request(path: "/echo_greeting", body: { greeting: "hullo" }) do |c|
149
+ c.response.must_equal '{"greeting":"hullo"}'
150
+ end
151
+ end
152
+ end
153
+
154
+ it "accepts json params from incoming requests" do
155
+ with_api(echo_greeting_server) do
156
+ get_request(
157
+ path: "/echo_greeting",
158
+ body: '{"greeting":"howdy"}',
159
+ head: { "Content-Type" => "application/json" }
160
+ ) do |c|
161
+ c.response.must_equal '{"greeting":"howdy"}'
162
+ end
163
+ end
164
+ end
165
+
166
+ it "sends query params in backend requests" do
167
+ proxy_query = extend_server echo_greeting_server do
168
+ get "/proxy_query_param" do
169
+ request :echo do
170
+ path "/echo_greeting"
171
+ method :get
172
+
173
+ query_param :greeting, "hello"
174
+ end
175
+
176
+ response do |data|
177
+ attribute :greeting, data[:echo][:greeting]
178
+ end
179
+ end
180
+ end
181
+
182
+ with_api(proxy_query) do
183
+ get_request(path: "/proxy_query_param") do |c|
184
+ c.response.must_equal '{"greeting":"hello"}'
185
+ end
186
+ end
187
+ end
188
+
189
+ it "sends form params in backend requests" do
190
+ proxy_form = extend_server echo_greeting_server do
191
+ get "/proxy_form_param" do
192
+ request :echo do
193
+ path "/echo_greeting"
194
+ method :get
195
+
196
+ form_param :greeting, "hello"
197
+ end
198
+
199
+ response do |data|
200
+ attribute :greeting, data[:echo][:greeting]
201
+ end
202
+ end
203
+ end
204
+
205
+ with_api(proxy_form) do
206
+ get_request(path: "/proxy_form_param") do |c|
207
+ c.response.must_equal '{"greeting":"hello"}'
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "json params" do
213
+ it "sends json params in backend requests" do
214
+ proxy_json = extend_server echo_greeting_server do
215
+ get "/proxy_json_param" do
216
+ request :echo do
217
+ path "/echo_greeting"
218
+ method :get
219
+
220
+ json_param :greeting, "hello"
221
+ end
222
+
223
+ response do |data|
224
+ attribute :greeting, data[:echo][:greeting]
225
+ end
226
+ end
227
+ end
228
+
229
+ with_api(proxy_json) do
230
+ get_request(path: "/proxy_json_param") do |c|
231
+ c.response.must_equal '{"greeting":"hello"}'
232
+ end
233
+ end
234
+ end
235
+
236
+ it "does not allow both json and form params together" do
237
+ conflicting_params = create_server do
238
+ get "/conflicting_params" do
239
+ request :echo do
240
+ path "/echo_greeting"
241
+ method :get
242
+
243
+ json_param :greeting, "hello"
244
+ form_param :name, "Bob"
245
+ end
246
+
247
+ response do |data|
248
+ attribute :greeting, data[:echo][:greeting]
249
+ end
250
+ end
251
+ end
252
+
253
+ with_api(conflicting_params) do
254
+ get_request(path: "/conflicting_params") do |c|
255
+ c.response.must_equal "A request cannot have both json and form " \
256
+ "parameters"
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ # TODO: make this test work
263
+ # It works if the /proxy_cookie_param and /echo_greeting endpoints are run
264
+ # in an actual server instance, but not here in the test.
265
+ #
266
+ # it "sends cookie params in backend requests" do
267
+ # proxy_cookie = extend_server echo_greeting_server do
268
+ # get "/proxy_cookie_param" do
269
+ # request :echo do
270
+ # path "/echo_greeting"
271
+ # method :get
272
+
273
+ # cookie_param :greeting, "hello"
274
+ # end
275
+
276
+ # response do |data|
277
+ # attribute :greeting, data[:echo][:greeting]
278
+ # end
279
+ # end
280
+ # end
281
+
282
+ # with_api(proxy_cookie) do
283
+ # get_request(path: "/proxy_cookie_param") do |c|
284
+ # c.response.must_equal '{"greeting":"hello"}'
285
+ # end
286
+ # end
287
+ # end
288
+
289
+ it "handles frontend post requests" do
290
+ frontend_post = create_server do
291
+ post "/ping" do
292
+ response do
293
+ success { attribute :result, "pong" }
294
+ end
295
+ end
296
+ end
297
+
298
+ with_api(frontend_post) do
299
+ post_request(path: "/ping") do |c|
300
+ c.response.must_equal '{"result":"pong"}'
301
+ end
302
+ end
303
+ end
304
+
305
+ it "handles frontend put requests" do
306
+ frontend_post = create_server do
307
+ put "/ping" do
308
+ response do
309
+ success { attribute :result, "pong" }
310
+ end
311
+ end
312
+ end
313
+
314
+ with_api(frontend_post) do
315
+ put_request(path: "/ping") do |c|
316
+ c.response.must_equal '{"result":"pong"}'
317
+ end
318
+ end
319
+ end
320
+
321
+ it "handles frontend delete requests" do
322
+ frontend_post = create_server do
323
+ delete "/ping" do
324
+ response do
325
+ success { attribute :result, "pong" }
326
+ end
327
+ end
328
+ end
329
+
330
+ with_api(frontend_post) do
331
+ delete_request(path: "/ping") do |c|
332
+ c.response.must_equal '{"result":"pong"}'
333
+ end
334
+ end
335
+ end
336
+
337
+ it "passes frontend request headers through to backend requests" do
338
+ echo_header = create_server do
339
+ get "/proxy" do
340
+ request :echo do
341
+ path "/echo"
342
+ method :get
343
+ end
344
+
345
+ response do |data|
346
+ success { attribute :result, data[:echo][:header] }
347
+ end
348
+ end
349
+
350
+ get "/echo" do |_, headers|
351
+ response do
352
+ success { attribute :header, headers["Bob-Content"] }
353
+ end
354
+ end
355
+ end
356
+
357
+ with_api(echo_header) do
358
+ get_request(
359
+ path: "/proxy",
360
+ head: { "Bob-Content" => "bob/classic" }
361
+ ) do |c|
362
+ c.response.must_equal '{"result":"bob/classic"}'
363
+ end
364
+ end
365
+ end
366
+
367
+ it "can set the entire body of the reponse" do
368
+ server = create_server do
369
+ get "/endpoint" do
370
+ response do
371
+ success { body "free form text!" }
372
+ end
373
+ end
374
+ end
375
+
376
+ with_api(server) do
377
+ get_request(path: "/endpoint") do |c|
378
+ c.response.must_equal "free form text!"
379
+ end
380
+ end
381
+ end
382
+
383
+ it "can respond with objects" do
384
+ class Bob
385
+ def initialize(last_name)
386
+ @last_name = last_name
387
+ end
388
+
389
+ def to_hash
390
+ { first_name: "Bob", last_name: @last_name }
391
+ end
392
+ end
393
+
394
+ object_response = create_server do
395
+ get "/object" do
396
+ response do
397
+ success { object :bob, Bob, "Saget" }
398
+ end
399
+ end
400
+ end
401
+
402
+ with_api(object_response) do
403
+ get_request(path: "/object") do |c|
404
+ c.response.must_equal '{"bob":{"first_name":"Bob","last_name":"Saget"}}'
405
+ end
406
+ end
407
+ end
408
+
409
+ it "can respond with arrays" do
410
+ # Example serialization class
411
+ class Bob
412
+ def initialize(last_name)
413
+ @last_name = last_name
414
+ end
415
+
416
+ def to_hash
417
+ { first_name: "Bob", last_name: @last_name }
418
+ end
419
+ end
420
+
421
+ object_response = create_server do
422
+ get "/object" do
423
+ response do
424
+ success { array :bobs, Bob, %w(Ross Marley) }
425
+ end
426
+ end
427
+ end
428
+
429
+ with_api(object_response) do
430
+ get_request(path: "/object") do |c|
431
+ c.response.must_equal '{"bobs":[' \
432
+ '{"first_name":"Bob","last_name":"Ross"},' \
433
+ '{"first_name":"Bob","last_name":"Marley"}' \
434
+ "]}"
435
+ end
436
+ end
437
+ end
438
+
439
+ it "can respond with cookies" do
440
+ server = create_server do
441
+ get "/cookie" do
442
+ response do
443
+ success { cookie "cookie-monster", "mmm... cookie" }
444
+ end
445
+ end
446
+ end
447
+
448
+ with_api(server) do
449
+ get_request(path: "/cookie") do |c|
450
+ expected = "cookie-monster=mmm... cookie"
451
+ c.response_header["Set-Cookie"].must_equal expected
452
+ end
453
+ end
454
+ end
455
+
456
+ it "can respond with a specified content type" do
457
+ server = create_server do
458
+ get "/endpoint" do
459
+ response do
460
+ success { content_type "image/jpeg" }
461
+ end
462
+ end
463
+ end
464
+
465
+ with_api(server) do
466
+ get_request(path: "/endpoint") do |c|
467
+ c.response_header["CONTENT_TYPE"].must_equal "image/jpeg"
468
+ end
469
+ end
470
+ end
471
+
472
+ it "makes the results of requests available to subsequent requests" do
473
+ multiple_requests = create_server do
474
+ get "/multi" do
475
+ request :one do
476
+ path "/one"
477
+ method :get
478
+ end
479
+
480
+ request :two do |data|
481
+ path "/two"
482
+ method :get
483
+
484
+ query_param :name, data[:one][:name]
485
+ end
486
+
487
+ response do |data|
488
+ success do
489
+ attribute :name, data[:two][:result]
490
+ cookie "type", data[:two].cookies[:type]
491
+ end
492
+ end
493
+ end
494
+
495
+ get "/one" do
496
+ response do
497
+ success { attribute :name, "Bob" }
498
+ end
499
+ end
500
+
501
+ get "/two" do |params|
502
+ response do
503
+ success do
504
+ attribute :result, params[:name]
505
+ cookie "type", "delicious"
506
+ end
507
+ end
508
+ end
509
+ end
510
+
511
+ with_api(multiple_requests) do
512
+ get_request(path: "/multi") do |c|
513
+ c.response.must_equal '{"name":"Bob"}'
514
+ c.response_header["Set-Cookie"].must_equal "type=delicious"
515
+ end
516
+ end
517
+ end
518
+
519
+ it "allows conditional requests" do
520
+ server = create_server do
521
+ get "/conditional" do
522
+ request :nonexistent, when: false do
523
+ path "/nonexistent"
524
+ method :get
525
+ end
526
+
527
+ response do
528
+ success { attribute :result, "OK" }
529
+ end
530
+ end
531
+ end
532
+
533
+ with_api(server) do
534
+ get_request(path: "/conditional") do |c|
535
+ c.response.must_equal '{"result":"OK"}'
536
+ end
537
+ end
538
+ end
539
+
540
+ it "streams downloads" do
541
+ server = create_server do
542
+ get "/one" do
543
+ stream true
544
+
545
+ request :two do
546
+ path "/two"
547
+ method :get
548
+ end
549
+
550
+ response do |data|
551
+ success do
552
+ stream do
553
+ data[:two].stream do |chunk|
554
+ stream_write chunk
555
+ end
556
+ end
557
+ end
558
+ end
559
+ end
560
+
561
+ get "/two" do
562
+ stream true
563
+
564
+ response do
565
+ success do
566
+ stream do
567
+ (1..9).each { |n| stream_write n }
568
+ end
569
+ end
570
+ end
571
+ end
572
+ end
573
+
574
+ with_api(server) do
575
+ get_request(path: "/one") do |c|
576
+ c.response.must_equal "123456789"
577
+ end
578
+ end
579
+ end
580
+
581
+ it "supports failure handlers in the response declaration" do
582
+ server = create_server do
583
+ get "/fail" do
584
+ response do
585
+ failure true do
586
+ status 422
587
+ attribute :result, "fail"
588
+ end
589
+ end
590
+ end
591
+ end
592
+
593
+ with_api(server) do
594
+ get_request(path: "/fail") do |c|
595
+ c.response.must_equal '{"result":"fail"}'
596
+ end
597
+ end
598
+ end
599
+
600
+ it "responds with the first matching failure handler" do
601
+ server = create_server do
602
+ get "/doublefail" do
603
+ response do
604
+ failure true do
605
+ attribute :fail, "one"
606
+ end
607
+
608
+ failure true do
609
+ attribute :fail, "two"
610
+ end
611
+ end
612
+ end
613
+ end
614
+
615
+ with_api(server) do
616
+ get_request(path: "/doublefail") do |c|
617
+ c.response.must_equal '{"fail":"one"}'
618
+ end
619
+ end
620
+ end
621
+
622
+ it "checks unhandled failures" do
623
+ server = create_server do
624
+ unhandled_failures do |data|
625
+ failure data.status == 400 do
626
+ status 422
627
+ attribute :fail, "yep"
628
+ end
629
+ end
630
+
631
+ get "/unhandled" do
632
+ request :fail do
633
+ path "/fail"
634
+ method :get
635
+ end
636
+
637
+ response do
638
+ success { attribute :result, "win" }
639
+ end
640
+ end
641
+
642
+ get "/fail" do
643
+ response do
644
+ success do
645
+ status 400
646
+ end
647
+ end
648
+ end
649
+ end
650
+
651
+ with_api(server) do
652
+ get_request(path: "/unhandled") do |c|
653
+ c.response.must_equal '{"fail":"yep"}'
654
+ end
655
+ end
656
+ end
657
+
658
+ it "passes through completely unhandled failures" do
659
+ server = create_server do
660
+ get "/completely_unhandled" do
661
+ request :fail do
662
+ path "/fail"
663
+ method :get
664
+ end
665
+
666
+ response do
667
+ success { attribute :result, "win" }
668
+ end
669
+ end
670
+
671
+ get "/fail" do
672
+ response do
673
+ success do
674
+ status 400
675
+ attribute :oh, "no"
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ with_api(server) do
682
+ get_request(path: "/completely_unhandled") do |c|
683
+ c.response.must_equal '{"oh":"no"}'
684
+ end
685
+ end
686
+ end
687
+
688
+ it "does not crash when there are no routes for an HTTP verb" do
689
+ server = create_server do
690
+ get "/ping" do
691
+ response do
692
+ success { attribute :result, "pong" }
693
+ end
694
+ end
695
+ end
696
+
697
+ with_api(server) do
698
+ post_request(path: "/whatever") do |c|
699
+ c.response.must_equal "nope"
700
+ end
701
+ end
702
+ end
703
+
704
+ it "allows a shorthand for setting success response status" do
705
+ server = create_server do
706
+ get "/no_body" do
707
+ response do
708
+ success 204
709
+ end
710
+ end
711
+ end
712
+
713
+ with_api(server) do
714
+ get_request(path: "/no_body") do |c|
715
+ c.response_header.http_status.must_equal 204
716
+ c.response.must_equal ""
717
+ end
718
+ end
719
+ end
720
+
721
+ it "provides a way to add helper methods" do
722
+ server = create_server do
723
+ helpers do
724
+ def victory_attribute
725
+ attribute :result, "VICTORY"
726
+ end
727
+
728
+ def victory!
729
+ response do
730
+ success { victory_attribute }
731
+ end
732
+ end
733
+ end
734
+
735
+ get "/status" do
736
+ victory!
737
+ end
738
+ end
739
+
740
+ with_api(server) do
741
+ get_request(path: "/status") do |c|
742
+ c.response.must_equal '{"result":"VICTORY"}'
743
+ end
744
+ end
745
+ end
746
+ end