grape 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/README.md +56 -8
  4. data/UPGRADING.md +43 -4
  5. data/lib/grape/api.rb +2 -2
  6. data/lib/grape/dsl/helpers.rb +1 -0
  7. data/lib/grape/dsl/inside_route.rb +26 -38
  8. data/lib/grape/dsl/routing.rb +2 -4
  9. data/lib/grape/middleware/base.rb +2 -1
  10. data/lib/grape/middleware/error.rb +10 -12
  11. data/lib/grape/middleware/stack.rb +17 -4
  12. data/lib/grape/request.rb +1 -1
  13. data/lib/grape/router.rb +1 -1
  14. data/lib/grape/router/attribute_translator.rb +2 -2
  15. data/lib/grape/util/base_inheritable.rb +2 -2
  16. data/lib/grape/util/lazy_value.rb +1 -0
  17. data/lib/grape/validations/params_scope.rb +2 -1
  18. data/lib/grape/validations/types/custom_type_coercer.rb +13 -1
  19. data/lib/grape/validations/validators/as.rb +1 -1
  20. data/lib/grape/validations/validators/base.rb +2 -4
  21. data/lib/grape/validations/validators/default.rb +3 -4
  22. data/lib/grape/validations/validators/except_values.rb +1 -1
  23. data/lib/grape/validations/validators/values.rb +1 -1
  24. data/lib/grape/version.rb +1 -1
  25. data/spec/grape/api_spec.rb +10 -0
  26. data/spec/grape/dsl/inside_route_spec.rb +7 -0
  27. data/spec/grape/endpoint/declared_spec.rb +590 -0
  28. data/spec/grape/endpoint_spec.rb +0 -534
  29. data/spec/grape/entity_spec.rb +6 -0
  30. data/spec/grape/middleware/error_spec.rb +1 -1
  31. data/spec/grape/middleware/stack_spec.rb +3 -1
  32. data/spec/grape/validations/params_scope_spec.rb +26 -0
  33. data/spec/grape/validations/validators/coerce_spec.rb +24 -0
  34. data/spec/grape/validations/validators/default_spec.rb +49 -0
  35. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  36. data/spec/spec_helper.rb +0 -10
  37. data/spec/support/chunks.rb +14 -0
  38. data/spec/support/versioned_helpers.rb +3 -5
  39. metadata +9 -5
@@ -280,540 +280,6 @@ describe Grape::Endpoint do
280
280
  end
281
281
  end
282
282
 
283
- describe '#declared' do
284
- before do
285
- subject.format :json
286
- subject.params do
287
- requires :first
288
- optional :second
289
- optional :third, default: 'third-default'
290
- optional :nested, type: Hash do
291
- optional :fourth
292
- optional :fifth
293
- optional :nested_two, type: Hash do
294
- optional :sixth
295
- optional :nested_three, type: Hash do
296
- optional :seventh
297
- end
298
- end
299
- optional :nested_arr, type: Array do
300
- optional :eighth
301
- end
302
- end
303
- optional :arr, type: Array do
304
- optional :nineth
305
- end
306
- end
307
- end
308
-
309
- context 'when params are not built with default class' do
310
- it 'returns an object that corresponds with the params class - hash with indifferent access' do
311
- subject.params do
312
- build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
313
- end
314
- subject.get '/declared' do
315
- d = declared(params, include_missing: true)
316
- { declared_class: d.class.to_s }
317
- end
318
-
319
- get '/declared?first=present'
320
- expect(JSON.parse(last_response.body)['declared_class']).to eq('ActiveSupport::HashWithIndifferentAccess')
321
- end
322
-
323
- it 'returns an object that corresponds with the params class - hashie mash' do
324
- subject.params do
325
- build_with Grape::Extensions::Hashie::Mash::ParamBuilder
326
- end
327
- subject.get '/declared' do
328
- d = declared(params, include_missing: true)
329
- { declared_class: d.class.to_s }
330
- end
331
-
332
- get '/declared?first=present'
333
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hashie::Mash')
334
- end
335
-
336
- it 'returns an object that corresponds with the params class - hash' do
337
- subject.params do
338
- build_with Grape::Extensions::Hash::ParamBuilder
339
- end
340
- subject.get '/declared' do
341
- d = declared(params, include_missing: true)
342
- { declared_class: d.class.to_s }
343
- end
344
-
345
- get '/declared?first=present'
346
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hash')
347
- end
348
- end
349
-
350
- it 'should show nil for nested params if include_missing is true' do
351
- subject.get '/declared' do
352
- declared(params, include_missing: true)
353
- end
354
-
355
- get '/declared?first=present'
356
- expect(last_response.status).to eq(200)
357
- expect(JSON.parse(last_response.body)['nested']['fourth']).to be_nil
358
- end
359
-
360
- it 'does not work in a before filter' do
361
- subject.before do
362
- declared(params)
363
- end
364
- subject.get('/declared') { declared(params) }
365
-
366
- expect { get('/declared') }.to raise_error(
367
- Grape::DSL::InsideRoute::MethodNotYetAvailable
368
- )
369
- end
370
-
371
- it 'has as many keys as there are declared params' do
372
- subject.get '/declared' do
373
- declared(params)
374
- end
375
- get '/declared?first=present'
376
- expect(last_response.status).to eq(200)
377
- expect(JSON.parse(last_response.body).keys.size).to eq(5)
378
- end
379
-
380
- it 'has a optional param with default value all the time' do
381
- subject.get '/declared' do
382
- declared(params)
383
- end
384
- get '/declared?first=one'
385
- expect(last_response.status).to eq(200)
386
- expect(JSON.parse(last_response.body)['third']).to eql('third-default')
387
- end
388
-
389
- it 'builds nested params' do
390
- subject.get '/declared' do
391
- declared(params)
392
- end
393
-
394
- get '/declared?first=present&nested[fourth]=1'
395
- expect(last_response.status).to eq(200)
396
- expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 4
397
- end
398
-
399
- it 'builds nested params when given array' do
400
- subject.get '/dummy' do
401
- end
402
- subject.params do
403
- requires :first
404
- optional :second
405
- optional :third, default: 'third-default'
406
- optional :nested, type: Array do
407
- optional :fourth
408
- end
409
- end
410
- subject.get '/declared' do
411
- declared(params)
412
- end
413
-
414
- get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
415
- expect(last_response.status).to eq(200)
416
- expect(JSON.parse(last_response.body)['nested'].size).to eq 2
417
- end
418
-
419
- context 'sets nested objects when the param is missing' do
420
- it 'to be a hash when include_missing is true' do
421
- subject.get '/declared' do
422
- declared(params, include_missing: true)
423
- end
424
-
425
- get '/declared?first=present'
426
- expect(last_response.status).to eq(200)
427
- expect(JSON.parse(last_response.body)['nested']).to eq({})
428
- end
429
-
430
- it 'to be an array when include_missing is true' do
431
- subject.get '/declared' do
432
- declared(params, include_missing: true)
433
- end
434
-
435
- get '/declared?first=present'
436
- expect(last_response.status).to eq(200)
437
- expect(JSON.parse(last_response.body)['arr']).to be_a(Array)
438
- end
439
-
440
- it 'to be an array when nested and include_missing is true' do
441
- subject.get '/declared' do
442
- declared(params, include_missing: true)
443
- end
444
-
445
- get '/declared?first=present&nested[fourth]=1'
446
- expect(last_response.status).to eq(200)
447
- expect(JSON.parse(last_response.body)['nested']['nested_arr']).to be_a(Array)
448
- end
449
-
450
- it 'to be nil when include_missing is false' do
451
- subject.get '/declared' do
452
- declared(params, include_missing: false)
453
- end
454
-
455
- get '/declared?first=present'
456
- expect(last_response.status).to eq(200)
457
- expect(JSON.parse(last_response.body)['nested']).to be_nil
458
- end
459
- end
460
-
461
- it 'filters out any additional params that are given' do
462
- subject.get '/declared' do
463
- declared(params)
464
- end
465
- get '/declared?first=one&other=two'
466
- expect(last_response.status).to eq(200)
467
- expect(JSON.parse(last_response.body).key?(:other)).to eq false
468
- end
469
-
470
- it 'stringifies if that option is passed' do
471
- subject.get '/declared' do
472
- declared(params, stringify: true)
473
- end
474
-
475
- get '/declared?first=one&other=two'
476
- expect(last_response.status).to eq(200)
477
- expect(JSON.parse(last_response.body)['first']).to eq 'one'
478
- end
479
-
480
- it 'does not include missing attributes if that option is passed' do
481
- subject.get '/declared' do
482
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:second)
483
- ''
484
- end
485
-
486
- get '/declared?first=one&other=two'
487
- expect(last_response.status).to eq(200)
488
- end
489
-
490
- it 'does not include renamed missing attributes if that option is passed' do
491
- subject.params do
492
- optional :renamed_original, as: :renamed
493
- end
494
- subject.get '/declared' do
495
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:renamed)
496
- ''
497
- end
498
-
499
- get '/declared?first=one&other=two'
500
- expect(last_response.status).to eq(200)
501
- end
502
-
503
- it 'includes attributes with value that evaluates to false' do
504
- subject.params do
505
- requires :first
506
- optional :boolean
507
- end
508
-
509
- subject.post '/declared' do
510
- error!('expected false', 400) if declared(params, include_missing: false)[:boolean] != false
511
- ''
512
- end
513
-
514
- post '/declared', ::Grape::Json.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
515
- expect(last_response.status).to eq(201)
516
- end
517
-
518
- it 'includes attributes with value that evaluates to nil' do
519
- subject.params do
520
- requires :first
521
- optional :second
522
- end
523
-
524
- subject.post '/declared' do
525
- error!('expected nil', 400) unless declared(params, include_missing: false)[:second].nil?
526
- ''
527
- end
528
-
529
- post '/declared', ::Grape::Json.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
530
- expect(last_response.status).to eq(201)
531
- end
532
-
533
- it 'includes missing attributes with defaults when there are nested hashes' do
534
- subject.get '/dummy' do
535
- end
536
-
537
- subject.params do
538
- requires :first
539
- optional :second
540
- optional :third, default: nil
541
- optional :nested, type: Hash do
542
- optional :fourth, default: nil
543
- optional :fifth, default: nil
544
- requires :nested_nested, type: Hash do
545
- optional :sixth, default: 'sixth-default'
546
- optional :seven, default: nil
547
- end
548
- end
549
- end
550
-
551
- subject.get '/declared' do
552
- declared(params, include_missing: false)
553
- end
554
-
555
- get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
556
- json = JSON.parse(last_response.body)
557
- expect(last_response.status).to eq(200)
558
- expect(json['first']).to eq 'present'
559
- expect(json['nested'].keys).to eq %w[fourth fifth nested_nested]
560
- expect(json['nested']['fourth']).to eq ''
561
- expect(json['nested']['nested_nested'].keys).to eq %w[sixth seven]
562
- expect(json['nested']['nested_nested']['sixth']).to eq 'sixth'
563
- end
564
-
565
- it 'does not include missing attributes when there are nested hashes' do
566
- subject.get '/dummy' do
567
- end
568
-
569
- subject.params do
570
- requires :first
571
- optional :second
572
- optional :third
573
- optional :nested, type: Hash do
574
- optional :fourth
575
- optional :fifth
576
- end
577
- end
578
-
579
- subject.get '/declared' do
580
- declared(params, include_missing: false)
581
- end
582
-
583
- get '/declared?first=present&nested[fourth]=4'
584
- json = JSON.parse(last_response.body)
585
- expect(last_response.status).to eq(200)
586
- expect(json['first']).to eq 'present'
587
- expect(json['nested'].keys).to eq %w[fourth]
588
- expect(json['nested']['fourth']).to eq '4'
589
- end
590
- end
591
-
592
- describe '#declared; call from child namespace' do
593
- before do
594
- subject.format :json
595
- subject.namespace :parent do
596
- params do
597
- requires :parent_name, type: String
598
- end
599
-
600
- namespace ':parent_name' do
601
- params do
602
- requires :child_name, type: String
603
- requires :child_age, type: Integer
604
- end
605
-
606
- namespace ':child_name' do
607
- params do
608
- requires :grandchild_name, type: String
609
- end
610
-
611
- get ':grandchild_name' do
612
- {
613
- 'params' => params,
614
- 'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
615
- 'with_parent_namespaces' => declared(params, include_parent_namespaces: true)
616
- }
617
- end
618
- end
619
- end
620
- end
621
-
622
- get '/parent/foo/bar/baz', child_age: 5, extra: 'hello'
623
- end
624
-
625
- let(:parsed_response) { JSON.parse(last_response.body, symbolize_names: true) }
626
-
627
- it { expect(last_response.status).to eq 200 }
628
-
629
- context 'with include_parent_namespaces: false' do
630
- it 'returns declared parameters only from current namespace' do
631
- expect(parsed_response[:without_parent_namespaces]).to eq(
632
- grandchild_name: 'baz'
633
- )
634
- end
635
- end
636
-
637
- context 'with include_parent_namespaces: true' do
638
- it 'returns declared parameters from every parent namespace' do
639
- expect(parsed_response[:with_parent_namespaces]).to eq(
640
- parent_name: 'foo',
641
- child_name: 'bar',
642
- grandchild_name: 'baz',
643
- child_age: 5
644
- )
645
- end
646
- end
647
-
648
- context 'without declaration' do
649
- it 'returns all requested parameters' do
650
- expect(parsed_response[:params]).to eq(
651
- parent_name: 'foo',
652
- child_name: 'bar',
653
- grandchild_name: 'baz',
654
- child_age: 5,
655
- extra: 'hello'
656
- )
657
- end
658
- end
659
- end
660
-
661
- describe '#declared; from a nested mounted endpoint' do
662
- before do
663
- doubly_mounted = Class.new(Grape::API)
664
- doubly_mounted.namespace :more do
665
- params do
666
- requires :y, type: Integer
667
- end
668
- route_param :y do
669
- get do
670
- {
671
- params: params,
672
- declared_params: declared(params)
673
- }
674
- end
675
- end
676
- end
677
-
678
- mounted = Class.new(Grape::API)
679
- mounted.namespace :another do
680
- params do
681
- requires :mount_space, type: Integer
682
- end
683
- route_param :mount_space do
684
- mount doubly_mounted
685
- end
686
- end
687
-
688
- subject.format :json
689
- subject.namespace :something do
690
- params do
691
- requires :id, type: Integer
692
- end
693
- resource ':id' do
694
- mount mounted
695
- end
696
- end
697
- end
698
-
699
- it 'can access parent attributes' do
700
- get '/something/123/another/456/more/789'
701
- expect(last_response.status).to eq 200
702
- json = JSON.parse(last_response.body, symbolize_names: true)
703
-
704
- # test all three levels of params
705
- expect(json[:declared_params][:y]).to eq 789
706
- expect(json[:declared_params][:mount_space]).to eq 456
707
- expect(json[:declared_params][:id]).to eq 123
708
- end
709
- end
710
-
711
- describe '#declared; mixed nesting' do
712
- before do
713
- subject.format :json
714
- subject.resource :users do
715
- route_param :id, type: Integer, desc: 'ID desc' do
716
- # Adding this causes route_setting(:declared_params) to be nil for the
717
- # get block in namespace 'foo' below
718
- get do
719
- end
720
-
721
- namespace 'foo' do
722
- get do
723
- {
724
- params: params,
725
- declared_params: declared(params),
726
- declared_params_no_parent: declared(params, include_parent_namespaces: false)
727
- }
728
- end
729
- end
730
- end
731
- end
732
- end
733
-
734
- it 'can access parent route_param' do
735
- get '/users/123/foo', bar: 'bar'
736
- expect(last_response.status).to eq 200
737
- json = JSON.parse(last_response.body, symbolize_names: true)
738
-
739
- expect(json[:declared_params][:id]).to eq 123
740
- expect(json[:declared_params_no_parent][:id]).to eq nil
741
- end
742
- end
743
-
744
- describe '#declared; with multiple route_param' do
745
- before do
746
- mounted = Class.new(Grape::API)
747
- mounted.namespace :albums do
748
- get do
749
- declared(params)
750
- end
751
- end
752
-
753
- subject.format :json
754
- subject.namespace :artists do
755
- route_param :id, type: Integer do
756
- get do
757
- declared(params)
758
- end
759
-
760
- params do
761
- requires :filter, type: String
762
- end
763
- get :some_route do
764
- declared(params)
765
- end
766
- end
767
-
768
- route_param :artist_id, type: Integer do
769
- namespace :compositions do
770
- get do
771
- declared(params)
772
- end
773
- end
774
- end
775
-
776
- route_param :compositor_id, type: Integer do
777
- mount mounted
778
- end
779
- end
780
- end
781
-
782
- it 'return only :id without :artist_id' do
783
- get '/artists/1'
784
- json = JSON.parse(last_response.body, symbolize_names: true)
785
-
786
- expect(json.key?(:id)).to be_truthy
787
- expect(json.key?(:artist_id)).not_to be_truthy
788
- end
789
-
790
- it 'return only :artist_id without :id' do
791
- get '/artists/1/compositions'
792
- json = JSON.parse(last_response.body, symbolize_names: true)
793
-
794
- expect(json.key?(:artist_id)).to be_truthy
795
- expect(json.key?(:id)).not_to be_truthy
796
- end
797
-
798
- it 'return :filter and :id parameters in declared for second enpoint inside route_param' do
799
- get '/artists/1/some_route', filter: 'some_filter'
800
- json = JSON.parse(last_response.body, symbolize_names: true)
801
-
802
- expect(json.key?(:filter)).to be_truthy
803
- expect(json.key?(:id)).to be_truthy
804
- expect(json.key?(:artist_id)).not_to be_truthy
805
- end
806
-
807
- it 'return :compositor_id for mounter in route_param' do
808
- get '/artists/1/albums'
809
- json = JSON.parse(last_response.body, symbolize_names: true)
810
-
811
- expect(json.key?(:compositor_id)).to be_truthy
812
- expect(json.key?(:id)).not_to be_truthy
813
- expect(json.key?(:artist_id)).not_to be_truthy
814
- end
815
- end
816
-
817
283
  describe '#params' do
818
284
  it 'is available to the caller' do
819
285
  subject.get('/hey') do