@modernpoacher/halacious 7.0.1 → 7.0.3

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.
@@ -1,2605 +0,0 @@
1
- import path from 'path'
2
-
3
- import * as chai from 'chai'
4
-
5
- import hapi from '@hapi/hapi'
6
- import vision from '@hapi/vision'
7
- import sinon from 'sinon'
8
- import sinonChai from '@sequencemedia/sinon-chai'
9
- import halacious from '#halacious'
10
- import {
11
- plugin
12
- } from '#halacious/plugin'
13
- import _ from 'lodash'
14
- import url from 'url'
15
- import IAM from '#where-am-i'
16
-
17
- const should = chai.should()
18
-
19
- chai.use(sinonChai)
20
-
21
- const PLUGIN = 'halacious' // '@modernpoacher/halacious'
22
-
23
- describe('Halacious Plugin', () => {
24
- let server
25
-
26
- beforeEach((done) => {
27
- server = hapi.server({ port: 9191 })
28
-
29
- done()
30
- })
31
-
32
- afterEach((done) => {
33
- server.stop().then(done).catch(done)
34
- })
35
-
36
- xit('should have a registration function', () => {
37
- plugin.should.have.property('register')
38
- plugin.register.should.be.a('function')
39
- })
40
-
41
- it('should expose a namespace function', (done) => {
42
- server
43
- .register(halacious)
44
- .then(() => {
45
- const {
46
- plugins: { [PLUGIN]: plugin }
47
- } = server
48
-
49
- plugin.should.have.property('namespaces')
50
- plugin.namespace.should.be.a('function')
51
- })
52
- .then(done)
53
- .catch(done)
54
- })
55
-
56
- it('should create a namespace', (done) => {
57
- server
58
- .register(halacious)
59
- .then(() => {
60
- const {
61
- plugins: { [PLUGIN]: plugin }
62
- } = server
63
-
64
- const namespace = plugin.namespaces.add({
65
- name: 'mycompany',
66
- prefix: 'mco'
67
- })
68
-
69
- should.exist(namespace)
70
- namespace.should.have.property('name', 'mycompany')
71
- namespace.should.have.property('prefix', 'mco')
72
- namespace.should.have.property('rel')
73
- namespace.rel.should.be.a('function')
74
- })
75
- .then(done)
76
- .catch(done)
77
- })
78
-
79
- it('should look up a namespace', (done) => {
80
- server
81
- .register(halacious)
82
- .then(() => {
83
- const {
84
- plugins: { [PLUGIN]: plugin }
85
- } = server
86
-
87
- plugin.namespaces.add({
88
- name: 'mycompany',
89
- prefix: 'mco'
90
- })
91
-
92
- const namespace = plugin.namespace('mycompany')
93
- namespace.rel({ name: 'boss', description: 'An employees boss' })
94
- namespace.rels.should.have.property('boss')
95
- namespace.rels.boss.should.have.property('name', 'boss')
96
- namespace.rels.boss.should.have.property(
97
- 'description',
98
- 'An employees boss'
99
- )
100
- })
101
- .then(done)
102
- .catch(done)
103
- })
104
-
105
- it('should return a sorted array of namespaces', (done) => {
106
- server
107
- .register(halacious)
108
- .then(() => {
109
- const {
110
- plugins: { [PLUGIN]: plugin }
111
- } = server
112
-
113
- plugin.namespaces.add({
114
- name: 'yourcompany',
115
- prefix: 'yco'
116
- })
117
-
118
- plugin.namespaces.add({
119
- name: 'mycompany',
120
- prefix: 'mco'
121
- })
122
-
123
- plugin.namespaces.add({
124
- name: 'ourcompany',
125
- prefix: 'oco'
126
- })
127
-
128
- const namespaces = plugin.namespaces()
129
- namespaces.should.have.length(3)
130
- namespaces[0].should.have.property('name', 'mycompany')
131
- namespaces[1].should.have.property('name', 'ourcompany')
132
- namespaces[2].should.have.property('name', 'yourcompany')
133
- })
134
- .then(done)
135
- .catch(done)
136
- })
137
-
138
- it('should fail when registering an invalid namespace', (done) => {
139
- server
140
- .register(halacious)
141
- .then(() => {
142
- const {
143
- plugins: { [PLUGIN]: plugin }
144
- } = server
145
-
146
- plugin.namespaces.add
147
- .bind(plugin.namespaces, {
148
- name: 'mycompany',
149
- prefirx: 'mco'
150
- })
151
- .should.throw('"prefirx" is not allowed')
152
- })
153
- .then(done)
154
- .catch(done) // shouldn't be called
155
- })
156
-
157
- it('should add a rel to a namespace', (done) => {
158
- server
159
- .register(halacious)
160
- .then(() => {
161
- const {
162
- plugins: { [PLUGIN]: plugin }
163
- } = server
164
-
165
- const namespace = plugin.namespaces.add({
166
- name: 'mycompany',
167
- prefix: 'mco'
168
- })
169
-
170
- namespace.rel({ name: 'boss', description: 'An employees boss' })
171
- namespace.rels.should.have.property('boss')
172
- namespace.rels.boss.should.have.property('name', 'boss')
173
- namespace.rels.boss.should.have.property(
174
- 'description',
175
- 'An employees boss'
176
- )
177
- })
178
- .then(done)
179
- .catch(done)
180
- })
181
-
182
- it('should look up a rel by prefix:name', (done) => {
183
- server
184
- .register(halacious)
185
- .then(() => {
186
- const {
187
- plugins: { [PLUGIN]: plugin }
188
- } = server
189
-
190
- const namespace = plugin.namespaces.add({
191
- name: 'mycompany',
192
- prefix: 'mco'
193
- })
194
-
195
- namespace.rel({
196
- name: 'datasources',
197
- description: 'A list of datasources'
198
- })
199
-
200
- const rel = plugin.rel('mco:datasources')
201
-
202
- should.exist(rel)
203
- rel.should.have.property('name', 'datasources')
204
- rel.should.have.property('description', 'A list of datasources')
205
- })
206
- .then(done)
207
- .catch(done)
208
- })
209
-
210
- it('should remove a namespace', (done) => {
211
- server
212
- .register(halacious)
213
- .then(() => {
214
- const {
215
- plugins: { [PLUGIN]: plugin }
216
- } = server
217
-
218
- plugin.namespaces.add({
219
- name: 'mycompany',
220
- prefix: 'mco'
221
- })
222
-
223
- plugin.namespaces.add({
224
- name: 'yourcompany',
225
- prefix: 'yco'
226
- })
227
-
228
- plugin.namespaces().should.have.length(2)
229
-
230
- plugin.namespaces.remove('yourcompany')
231
-
232
- plugin.namespaces().should.have.length(1)
233
-
234
- plugin.namespaces()[0].should.have.property('name', 'mycompany')
235
- })
236
- .then(done)
237
- .catch(done)
238
- })
239
-
240
- it('should look up a rel by namespace / name', (done) => {
241
- server
242
- .register(halacious)
243
- .then(() => {
244
- const {
245
- plugins: { [PLUGIN]: plugin }
246
- } = server
247
-
248
- const namespace = plugin.namespaces.add({
249
- name: 'mycompany',
250
- prefix: 'mco'
251
- })
252
-
253
- namespace.rel({
254
- name: 'datasources',
255
- description: 'A list of datasources'
256
- })
257
-
258
- const rel = plugin.rel('mycompany', 'datasources')
259
-
260
- should.exist(rel)
261
- rel.should.have.property('name', 'datasources')
262
- rel.should.have.property('description', 'A list of datasources')
263
- })
264
- .then(done)
265
- .catch(done)
266
- })
267
-
268
- it('should add a rel to a specified namespace', (done) => {
269
- server
270
- .register(halacious)
271
- .then(() => {
272
- const {
273
- plugins: { [PLUGIN]: plugin }
274
- } = server
275
-
276
- plugin.namespaces.add({ name: 'thiscompany', prefix: 'tco' })
277
-
278
- plugin.rels.add('thiscompany', 'a_rel')
279
-
280
- plugin.rels.add('thiscompany', { name: 'b_rel' })
281
-
282
- const rels = _.values(plugin.namespace('thiscompany').rels)
283
-
284
- rels.should.have.length(2)
285
- _.map(rels, 'name').should.deep.equal(['a_rel', 'b_rel'])
286
- })
287
- .then(done)
288
- .catch(done)
289
- })
290
-
291
- it('should return a sorted list of rels', (done) => {
292
- server
293
- .register(halacious)
294
- .then(() => {
295
- const {
296
- plugins: { [PLUGIN]: plugin }
297
- } = server
298
-
299
- plugin.namespaces
300
- .add({ name: 'mycompany', prefix: 'mco' })
301
- .rel('a_rel')
302
- .rel('c_rel')
303
-
304
- plugin.namespaces
305
- .add({ name: 'yourcompany', prefix: 'yco' })
306
- .rel('b_rel')
307
- .rel('d_rel')
308
-
309
- const rels = plugin.rels()
310
-
311
- rels.should.have.length(4)
312
- _.map(rels, 'name').should.deep.equal([
313
- 'a_rel',
314
- 'b_rel',
315
- 'c_rel',
316
- 'd_rel'
317
- ])
318
- })
319
- .then(done)
320
- .catch(done)
321
- })
322
-
323
- it('should bomb on a bad rel in strict mode', (done) => {
324
- server.route({
325
- method: 'get',
326
- path: '/foo',
327
- config: {
328
- handler () {
329
- return { name: 'Billy Bob' }
330
- },
331
- plugins: {
332
- hal: {
333
- links: {
334
- 'mco:badRel': './badRel'
335
- }
336
- }
337
- }
338
- }
339
- })
340
-
341
- server
342
- .register({ plugin: halacious, options: { strict: true } })
343
- .then(() => {
344
- const {
345
- plugins: {
346
- [PLUGIN]: { namespaces }
347
- }
348
- } = server
349
-
350
- namespaces.add({
351
- dir: path.join(IAM, './test/rels/mycompany'),
352
- prefix: 'mco'
353
- })
354
- })
355
- .then(() =>
356
- server.inject({
357
- method: 'get',
358
- url: '/foo',
359
- headers: { Accept: 'application/hal+json' }
360
- })
361
- )
362
- .then((res) => {
363
- res.statusCode.should.equal(500)
364
- })
365
- .then(done)
366
- .catch(done)
367
- })
368
-
369
- it('should install a directory-style namespace', (done) => {
370
- server
371
- .register(halacious)
372
- .then(() => {
373
- const {
374
- plugins: { [PLUGIN]: plugin }
375
- } = server
376
-
377
- const namespace = plugin.namespaces.add({
378
- dir: path.join(IAM, './test/rels/mycompany'),
379
- prefix: 'mco'
380
- })
381
-
382
- const rel1 = plugin.rel('mco:datasources')
383
-
384
- const rel2 = plugin.rel('mco:datasource')
385
-
386
- should.exist(namespace)
387
- should.exist(rel1)
388
- should.exist(rel2)
389
-
390
- rel1.should.have.property('name', 'datasources')
391
- rel2.should.have.property('name', 'datasource')
392
- })
393
- .then(done)
394
- .catch(done)
395
- })
396
-
397
- it('should route rel documentation', (done) => {
398
- server
399
- .register(vision)
400
- .then(() => server.register(halacious))
401
- .then(() => {
402
- const {
403
- plugins: { [PLUGIN]: plugin }
404
- } = server
405
-
406
- plugin.namespaces.add({
407
- dir: path.join(IAM, './test/rels/mycompany'),
408
- prefix: 'mco'
409
- })
410
- })
411
- .then(() => server.start())
412
- .then(() =>
413
- server.inject({
414
- method: 'get',
415
- url: '/rels/mycompany/boss'
416
- })
417
- )
418
- .then((res) => {
419
- res.statusCode.should.equal(200)
420
-
421
- res.payload.should.not.be.empty // eslint-disable-line
422
- })
423
- .then(done)
424
- .catch(done)
425
- })
426
-
427
- it('should resolve a named route path', (done) => {
428
- server.route({
429
- method: 'get',
430
- path: '/{a}/{b}/{c}',
431
- config: {
432
- handler (req) {
433
- return { a: req.params.a, b: req.params.b, c: req.params.c }
434
- },
435
- plugins: {
436
- hal: {
437
- name: 'test-route'
438
- }
439
- }
440
- }
441
- })
442
-
443
- server
444
- .register(halacious)
445
- .then(() => {
446
- const {
447
- plugins: { [PLUGIN]: plugin }
448
- } = server
449
-
450
- const path = plugin.route('test-route', {
451
- a: 'i',
452
- b: 'aint',
453
- c: 'fack'
454
- })
455
-
456
- path.should.equal('/i/aint/fack')
457
- })
458
- .then(done)
459
- .catch(done)
460
- })
461
-
462
- it('should encode parameter values when resolving a named route', (done) => {
463
- server.route({
464
- method: 'get',
465
- path: '/deez/treez/{foo}/{bar}',
466
- config: {
467
- handler (req) {
468
- return { foo: req.params.foo, bar: req.params.bar }
469
- },
470
- plugins: {
471
- hal: {
472
- name: 'deez-treez'
473
- }
474
- }
475
- }
476
- })
477
-
478
- server
479
- .register(halacious)
480
- .then(() => {
481
- const {
482
- plugins: { [PLUGIN]: plugin }
483
- } = server
484
-
485
- const path = plugin.route('deez-treez', {
486
- foo: 'are/fire',
487
- bar: 'proof'
488
- })
489
-
490
- path.should.not.equal('/deez/treez/are/fire/proof')
491
- path.should.equal('/deez/treez/are%2Ffire/proof')
492
- })
493
- .then(done)
494
- .catch(done)
495
- })
496
-
497
- it('should passively ignore child objects in parameter hash when resolving a named route', (done) => {
498
- server.route({
499
- method: 'get',
500
- path: '/deez/treez/{foo}/{bar}',
501
- config: {
502
- handler (req) {
503
- return { foo: req.params.foo, bar: req.params.bar }
504
- },
505
- plugins: {
506
- hal: {
507
- name: 'deez-treez'
508
- }
509
- }
510
- }
511
- })
512
-
513
- server
514
- .register(halacious)
515
- .then(() => {
516
- const {
517
- plugins: { [PLUGIN]: plugin }
518
- } = server
519
-
520
- plugin.route.bind(halacious, 'deez-treez', { // eslint-disable-line
521
- foo: 'are',
522
- bar: 'fire/proof',
523
- things: { should: 'not break' }
524
- }).should.not.throw
525
-
526
- const path = plugin.route('deez-treez', {
527
- foo: 'are',
528
- bar: 'fire/proof',
529
- things: { should: 'not break' }
530
- })
531
-
532
- path.should.not.equal('/deez/treez/are/fire/proof')
533
- path.should.equal('/deez/treez/are/fire%2Fproof')
534
- })
535
- .then(done)
536
- .catch(done)
537
- })
538
-
539
- it('should handle presence of optional Hapi route parameters in a named route', (done) => {
540
- server.route({
541
- method: 'get',
542
- path: '/deez/treez/{are?}',
543
- config: {
544
- handler (req) {
545
- return { foo: req.params.foo }
546
- },
547
- plugins: {
548
- hal: {
549
- name: 'deez-treez'
550
- }
551
- }
552
- }
553
- })
554
-
555
- server
556
- .register(halacious)
557
- .then(() => {
558
- let path = null
559
- const fn = function () {
560
- const {
561
- plugins: { [PLUGIN]: plugin }
562
- } = server
563
-
564
- path = plugin.route('deez-treez', {
565
- are: 'fireproof'
566
- })
567
- }
568
-
569
- fn.should.not.throw(Error)
570
-
571
- should.exist(path)
572
- path.should.equal('/deez/treez/fireproof')
573
- })
574
- .then(done)
575
- .catch(done)
576
- })
577
-
578
- it('should convert a json entity into a HAL representation with self and a simple link', (done) => {
579
- server.route({
580
- method: 'get',
581
- path: '/people/{id}',
582
- config: {
583
- handler () {
584
- return { firstName: 'Bob', lastName: 'Smith' }
585
- },
586
- plugins: {
587
- hal: {
588
- links: {
589
- 'mco:boss': './boss'
590
- }
591
- }
592
- }
593
- }
594
- })
595
-
596
- server
597
- .register(halacious)
598
- .then(() => {
599
- const {
600
- plugins: {
601
- [PLUGIN]: { namespaces }
602
- }
603
- } = server
604
-
605
- namespaces
606
- .add({ name: 'mycompany', prefix: 'mco' })
607
- .rel({ name: 'boss' })
608
- })
609
- .then(() =>
610
- server.inject({
611
- method: 'get',
612
- url: '/people/100',
613
- headers: { Accept: 'application/hal+json' }
614
- })
615
- )
616
- .then((res) => {
617
- res.statusCode.should.equal(200)
618
-
619
- const result = JSON.parse(res.payload)
620
-
621
- result.should.deep.equal({
622
- _links: {
623
- self: { href: '/people/100' },
624
- curies: [
625
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
626
- ],
627
- 'mco:boss': { href: '/people/100/boss' }
628
- },
629
- firstName: 'Bob',
630
- lastName: 'Smith'
631
- })
632
- })
633
- .then(done)
634
- .catch(done)
635
- })
636
-
637
- it('should convert a json entity into a HAL representation with self and a templated link', (done) => {
638
- server.route({
639
- method: 'get',
640
- path: '/people/{id}',
641
- config: {
642
- handler () {
643
- return { firstName: 'Bob', lastName: 'Smith', bossId: '1234' }
644
- },
645
- plugins: {
646
- hal: {
647
- links: {
648
- 'mco:boss': { href: '../{bossId}', title: 'Boss' }
649
- }
650
- }
651
- }
652
- }
653
- })
654
-
655
- server
656
- .register(halacious)
657
- .then(() => {
658
- const {
659
- plugins: {
660
- [PLUGIN]: { namespaces }
661
- }
662
- } = server
663
-
664
- namespaces
665
- .add({ name: 'mycompany', prefix: 'mco' })
666
- .rel({ name: 'boss' })
667
- })
668
- .then(() =>
669
- server.inject({
670
- method: 'get',
671
- url: '/people/100',
672
- headers: { Accept: 'application/hal+json' }
673
- })
674
- )
675
- .then((res) => {
676
- res.statusCode.should.equal(200)
677
-
678
- const result = JSON.parse(res.payload)
679
-
680
- result.should.deep.equal({
681
- _links: {
682
- self: { href: '/people/100' },
683
- curies: [
684
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
685
- ],
686
- 'mco:boss': { href: '/people/1234', title: 'Boss' }
687
- },
688
- firstName: 'Bob',
689
- lastName: 'Smith',
690
- bossId: '1234'
691
- })
692
- })
693
- .then(done)
694
- .catch(done)
695
- })
696
-
697
- it('should allow for programmatic population of a hal entity', (done) => {
698
- server.route({
699
- method: 'get',
700
- path: '/people/{id}',
701
- config: {
702
- handler () {
703
- return { firstName: 'Bob', lastName: 'Smith', bossId: '1234' }
704
- },
705
- plugins: {
706
- hal: {
707
- prepare (rep, done) {
708
- rep.link('mco:boss', 'http://www.whitehouse.gov')
709
- done()
710
- }
711
- }
712
- }
713
- }
714
- })
715
-
716
- server
717
- .register(halacious)
718
- .then(() => {
719
- const {
720
- plugins: {
721
- [PLUGIN]: { namespaces }
722
- }
723
- } = server
724
-
725
- namespaces
726
- .add({ name: 'mycompany', prefix: 'mco' })
727
- .rel({ name: 'boss' })
728
- })
729
- .then(() =>
730
- server.inject({
731
- method: 'get',
732
- url: '/people/100',
733
- headers: { Accept: 'application/hal+json' }
734
- })
735
- )
736
- .then((res) => {
737
- res.statusCode.should.equal(200)
738
-
739
- const result = JSON.parse(res.payload)
740
-
741
- result.should.deep.equal({
742
- _links: {
743
- self: { href: '/people/100' },
744
- curies: [
745
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
746
- ],
747
- 'mco:boss': { href: 'http://www.whitehouse.gov' }
748
- },
749
- firstName: 'Bob',
750
- lastName: 'Smith',
751
- bossId: '1234'
752
- })
753
- })
754
- .then(done)
755
- .catch(done)
756
- })
757
-
758
- it('should support a hal configuration function', (done) => {
759
- server.route({
760
- method: 'get',
761
- path: '/people/{id}',
762
- config: {
763
- handler () {
764
- return { firstName: 'Bob', lastName: 'Smith', bossId: '1234' }
765
- },
766
- plugins: {
767
- hal (rep, done) {
768
- rep.link('mco:boss', 'http://www.whitehouse.gov')
769
- done()
770
- }
771
- }
772
- }
773
- })
774
-
775
- server
776
- .register(halacious)
777
- .then(() => {
778
- const {
779
- plugins: {
780
- [PLUGIN]: { namespaces }
781
- }
782
- } = server
783
-
784
- namespaces
785
- .add({ name: 'mycompany', prefix: 'mco' })
786
- .rel({ name: 'boss' })
787
- })
788
- .then(() =>
789
- server.inject({
790
- method: 'get',
791
- url: '/people/100',
792
- headers: { Accept: 'application/hal+json' }
793
- })
794
- )
795
- .then((res) => {
796
- res.statusCode.should.equal(200)
797
-
798
- const result = JSON.parse(res.payload)
799
-
800
- result.should.deep.equal({
801
- _links: {
802
- self: { href: '/people/100' },
803
- curies: [
804
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
805
- ],
806
- 'mco:boss': { href: 'http://www.whitehouse.gov' }
807
- },
808
- firstName: 'Bob',
809
- lastName: 'Smith',
810
- bossId: '1234'
811
- })
812
- })
813
- .then(done)
814
- .catch(done)
815
- })
816
-
817
- it('should embed an object property', (done) => {
818
- server.route({
819
- method: 'get',
820
- path: '/people/{id}',
821
- config: {
822
- handler () {
823
- return {
824
- firstName: 'Bob',
825
- lastName: 'Smith',
826
- boss: { firstName: 'Boss', lastName: 'Man' }
827
- }
828
- },
829
- plugins: {
830
- hal: {
831
- embedded: {
832
- 'mco:boss': {
833
- path: 'boss',
834
- href: './boss'
835
- }
836
- }
837
- }
838
- }
839
- }
840
- })
841
-
842
- server
843
- .register(halacious)
844
- .then(() => {
845
- const {
846
- plugins: {
847
- [PLUGIN]: { namespaces }
848
- }
849
- } = server
850
-
851
- namespaces
852
- .add({ name: 'mycompany', prefix: 'mco' })
853
- .rel({ name: 'boss' })
854
- })
855
- .then(() =>
856
- server.inject({
857
- method: 'get',
858
- url: '/people/100',
859
- headers: { Accept: 'application/hal+json' }
860
- })
861
- )
862
- .then((res) => {
863
- res.statusCode.should.equal(200)
864
-
865
- const result = JSON.parse(res.payload)
866
-
867
- result.should.deep.equal({
868
- _links: {
869
- self: { href: '/people/100' },
870
- curies: [
871
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
872
- ]
873
- },
874
- firstName: 'Bob',
875
- lastName: 'Smith',
876
- _embedded: {
877
- 'mco:boss': {
878
- _links: { self: { href: '/people/100/boss' } },
879
- firstName: 'Boss',
880
- lastName: 'Man'
881
- }
882
- }
883
- })
884
- })
885
- .then(done)
886
- .catch(done)
887
- })
888
-
889
- it('should support embedded url templates', (done) => {
890
- server.route({
891
- method: 'get',
892
- path: '/people/{id}',
893
- config: {
894
- handler () {
895
- return {
896
- id: 100,
897
- firstName: 'Bob',
898
- lastName: 'Smith',
899
- boss: { id: 200, firstName: 'Boss', lastName: 'Man' }
900
- }
901
- },
902
- plugins: {
903
- hal: {
904
- embedded: {
905
- 'mco:boss': {
906
- path: 'boss',
907
- href: '/people/{self.id}/{item.id}'
908
- }
909
- }
910
- }
911
- }
912
- }
913
- })
914
-
915
- server
916
- .register(halacious)
917
- .then(() => {
918
- const {
919
- plugins: {
920
- [PLUGIN]: { namespaces }
921
- }
922
- } = server
923
-
924
- namespaces
925
- .add({ name: 'mycompany', prefix: 'mco' })
926
- .rel({ name: 'boss' })
927
- })
928
- .then(() =>
929
- server.inject({
930
- method: 'get',
931
- url: '/people/100',
932
- headers: { Accept: 'application/hal+json' }
933
- })
934
- )
935
- .then((res) => {
936
- res.statusCode.should.equal(200)
937
-
938
- const result = JSON.parse(res.payload)
939
-
940
- result.should.deep.equal({
941
- _links: {
942
- self: { href: '/people/100' },
943
- curies: [
944
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
945
- ]
946
- },
947
- id: 100,
948
- firstName: 'Bob',
949
- lastName: 'Smith',
950
- _embedded: {
951
- 'mco:boss': {
952
- _links: { self: { href: '/people/100/200' } },
953
- id: 200,
954
- firstName: 'Boss',
955
- lastName: 'Man'
956
- }
957
- }
958
- })
959
- })
960
- .then(done)
961
- .catch(done)
962
- })
963
-
964
- it('should provide embedded collection support', (done) => {
965
- server.route({
966
- method: 'get',
967
- path: '/people',
968
- config: {
969
- handler () {
970
- return {
971
- start: 0,
972
- count: 2,
973
- total: 2,
974
- items: [
975
- { id: 100, firstName: 'Bob', lastName: 'Smith' },
976
- { id: 200, firstName: 'Boss', lastName: 'Man' }
977
- ]
978
- }
979
- },
980
- plugins: {
981
- hal: {
982
- embedded: {
983
- 'mco:person': {
984
- path: 'items',
985
- href: './{item.id}',
986
- links: {
987
- 'mco:boss': './boss'
988
- }
989
- }
990
- }
991
- }
992
- }
993
- }
994
- })
995
-
996
- server
997
- .register(halacious)
998
- .then(() => {
999
- const {
1000
- plugins: {
1001
- [PLUGIN]: { namespaces }
1002
- }
1003
- } = server
1004
-
1005
- namespaces
1006
- .add({ name: 'mycompany', prefix: 'mco' })
1007
- .rel({ name: 'boss' })
1008
- })
1009
- .then(() =>
1010
- server.inject({
1011
- method: 'get',
1012
- url: '/people',
1013
- headers: { Accept: 'application/hal+json' }
1014
- })
1015
- )
1016
- .then((res) => {
1017
- res.statusCode.should.equal(200)
1018
-
1019
- const result = JSON.parse(res.payload)
1020
-
1021
- result.should.deep.equal({
1022
- _links: {
1023
- self: { href: '/people' },
1024
- curies: [
1025
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1026
- ]
1027
- },
1028
- start: 0,
1029
- count: 2,
1030
- total: 2,
1031
- _embedded: {
1032
- 'mco:person': [
1033
- {
1034
- _links: {
1035
- self: { href: '/people/100' },
1036
- 'mco:boss': { href: '/people/100/boss' }
1037
- },
1038
- id: 100,
1039
- firstName: 'Bob',
1040
- lastName: 'Smith'
1041
- },
1042
- {
1043
- _links: {
1044
- self: { href: '/people/200' },
1045
- 'mco:boss': { href: '/people/200/boss' }
1046
- },
1047
- id: 200,
1048
- firstName: 'Boss',
1049
- lastName: 'Man'
1050
- }
1051
- ]
1052
- }
1053
- })
1054
- })
1055
- .then(done)
1056
- .catch(done)
1057
- })
1058
-
1059
- it('should invoke an optional toHal() method on the source entity', (done) => {
1060
- server.route({
1061
- method: 'get',
1062
- path: '/people/{id}',
1063
- config: {
1064
- handler () {
1065
- return {
1066
- firstName: 'Bob',
1067
- lastName: 'Smith',
1068
- bossId: '1234',
1069
- toHal (rep, done) {
1070
- rep.link('mco:boss', './boss')
1071
- done()
1072
- }
1073
- }
1074
- }
1075
- }
1076
- })
1077
-
1078
- server
1079
- .register(halacious)
1080
- .then(() => {
1081
- const {
1082
- plugins: {
1083
- [PLUGIN]: { namespaces }
1084
- }
1085
- } = server
1086
-
1087
- namespaces
1088
- .add({ name: 'mycompany', prefix: 'mco' })
1089
- .rel({ name: 'boss' })
1090
- })
1091
- .then(() =>
1092
- server.inject({
1093
- method: 'get',
1094
- url: '/people/100',
1095
- headers: { Accept: 'application/hal+json' }
1096
- })
1097
- )
1098
- .then((res) => {
1099
- res.statusCode.should.equal(200)
1100
-
1101
- const result = JSON.parse(res.payload)
1102
-
1103
- result.should.deep.equal({
1104
- _links: {
1105
- self: { href: '/people/100' },
1106
- curies: [
1107
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1108
- ],
1109
- 'mco:boss': { href: '/people/100/boss' }
1110
- },
1111
- firstName: 'Bob',
1112
- lastName: 'Smith',
1113
- bossId: '1234'
1114
- })
1115
- })
1116
- .then(done)
1117
- .catch(done)
1118
- })
1119
-
1120
- it("should allow for programmatic population of a hal entity and it's configured embedded entities", (done) => {
1121
- server.route({
1122
- method: 'get',
1123
- path: '/people/{id}',
1124
- config: {
1125
- handler () {
1126
- return {
1127
- firstName: 'Bob',
1128
- lastName: 'Smith',
1129
- bossId: '1234',
1130
- foo: { id: '5678' }
1131
- }
1132
- },
1133
- plugins: {
1134
- hal: {
1135
- prepare (rep, done) {
1136
- rep.link('mco:boss', 'http://www.whitehouse.gov')
1137
- done()
1138
- },
1139
- embedded: {
1140
- foo: {
1141
- path: 'foo',
1142
- href: '/foo/{item.id}',
1143
- prepare (rep, next) {
1144
- setTimeout(() => {
1145
- rep.link('foo:bar', 'http://www.foo.com')
1146
- next()
1147
- }, 500)
1148
- }
1149
- }
1150
- }
1151
- }
1152
- }
1153
- }
1154
- })
1155
-
1156
- server
1157
- .register(halacious)
1158
- .then(() => {
1159
- const {
1160
- plugins: {
1161
- [PLUGIN]: { namespaces }
1162
- }
1163
- } = server
1164
-
1165
- namespaces
1166
- .add({ name: 'mycompany', prefix: 'mco' })
1167
- .rel({ name: 'boss' })
1168
- })
1169
- .then(() =>
1170
- server.inject({
1171
- method: 'get',
1172
- url: '/people/100',
1173
- headers: { Accept: 'application/hal+json' }
1174
- })
1175
- )
1176
- .then((res) => {
1177
- res.statusCode.should.equal(200)
1178
-
1179
- const result = JSON.parse(res.payload)
1180
-
1181
- result.should.deep.equal({
1182
- _links: {
1183
- self: { href: '/people/100' },
1184
- curies: [
1185
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1186
- ],
1187
- 'mco:boss': { href: 'http://www.whitehouse.gov' }
1188
- },
1189
- firstName: 'Bob',
1190
- lastName: 'Smith',
1191
- bossId: '1234',
1192
- _embedded: {
1193
- foo: {
1194
- _links: {
1195
- self: { href: '/foo/5678' },
1196
- 'foo:bar': { href: 'http://www.foo.com' }
1197
- },
1198
- id: '5678'
1199
- }
1200
- }
1201
- })
1202
- })
1203
- .then(done)
1204
- .catch(done)
1205
- })
1206
-
1207
- it('should omit missing configured embedded entities', (done) => {
1208
- server.route({
1209
- method: 'get',
1210
- path: '/people/{id}',
1211
- config: {
1212
- handler () {
1213
- return {
1214
- firstName: 'Bob',
1215
- lastName: 'Smith',
1216
- bossId: '1234',
1217
- foo: { id: '5678' }
1218
- }
1219
- },
1220
- plugins: {
1221
- hal: {
1222
- prepare (rep, done) {
1223
- rep.link('mco:boss', 'http://www.whitehouse.gov')
1224
- done()
1225
- },
1226
- embedded: {
1227
- foo: {
1228
- path: 'foo',
1229
- href: '/foo/{item.id}',
1230
- prepare (rep, next) {
1231
- rep.link('foo:bar', 'http://www.foo.com')
1232
- next()
1233
- }
1234
- },
1235
- bar: {
1236
- path: 'notthere',
1237
- href: '/bar/{item.id}'
1238
- }
1239
- }
1240
- }
1241
- }
1242
- }
1243
- })
1244
-
1245
- server
1246
- .register(halacious)
1247
- .then(() => {
1248
- const {
1249
- plugins: {
1250
- [PLUGIN]: { namespaces }
1251
- }
1252
- } = server
1253
-
1254
- namespaces
1255
- .add({ name: 'mycompany', prefix: 'mco' })
1256
- .rel({ name: 'boss' })
1257
- })
1258
- .then(() =>
1259
- server.inject({
1260
- method: 'get',
1261
- url: '/people/100',
1262
- headers: { Accept: 'application/hal+json' }
1263
- })
1264
- )
1265
- .then((res) => {
1266
- res.statusCode.should.equal(200)
1267
-
1268
- const result = JSON.parse(res.payload)
1269
-
1270
- result.should.deep.equal({
1271
- _links: {
1272
- self: { href: '/people/100' },
1273
- curies: [
1274
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1275
- ],
1276
- 'mco:boss': { href: 'http://www.whitehouse.gov' }
1277
- },
1278
- firstName: 'Bob',
1279
- lastName: 'Smith',
1280
- bossId: '1234',
1281
- _embedded: {
1282
- foo: {
1283
- _links: {
1284
- self: { href: '/foo/5678' },
1285
- 'foo:bar': { href: 'http://www.foo.com' }
1286
- },
1287
- id: '5678'
1288
- }
1289
- }
1290
- })
1291
- })
1292
- .then(done)
1293
- .catch(done)
1294
- })
1295
-
1296
- it('should allow an embedded entity to be forced to be a single element array', (done) => {
1297
- server.route({
1298
- method: 'get',
1299
- path: '/people/{id}',
1300
- config: {
1301
- handler () {
1302
- return {
1303
- firstName: 'Bob',
1304
- lastName: 'Smith',
1305
- bossId: '1234',
1306
- foo: [{ id: '5678' }]
1307
- }
1308
- },
1309
- plugins: {
1310
- hal: {
1311
- prepare (rep, done) {
1312
- rep.link('mco:boss', 'http://www.whitehouse.gov')
1313
- done()
1314
- },
1315
- embedded: {
1316
- foo: {
1317
- path: 'foo',
1318
- href: '/foo/{item.id}',
1319
- prepare (rep, next) {
1320
- rep.link('foo:bar', 'http://www.foo.com')
1321
- next()
1322
- }
1323
- }
1324
- }
1325
- }
1326
- }
1327
- }
1328
- })
1329
-
1330
- server
1331
- .register(halacious)
1332
- .then(() => {
1333
- const {
1334
- plugins: {
1335
- [PLUGIN]: { namespaces }
1336
- }
1337
- } = server
1338
-
1339
- namespaces
1340
- .add({ name: 'mycompany', prefix: 'mco' })
1341
- .rel({ name: 'boss' })
1342
- })
1343
- .then(() =>
1344
- server.inject({
1345
- method: 'get',
1346
- url: '/people/100',
1347
- headers: { Accept: 'application/hal+json' }
1348
- })
1349
- )
1350
- .then((res) => {
1351
- res.statusCode.should.equal(200)
1352
-
1353
- const result = JSON.parse(res.payload)
1354
-
1355
- result.should.deep.equal({
1356
- _links: {
1357
- self: { href: '/people/100' },
1358
- curies: [
1359
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1360
- ],
1361
- 'mco:boss': { href: 'http://www.whitehouse.gov' }
1362
- },
1363
- firstName: 'Bob',
1364
- lastName: 'Smith',
1365
- bossId: '1234',
1366
- _embedded: {
1367
- foo: [
1368
- {
1369
- _links: {
1370
- self: { href: '/foo/5678' },
1371
- 'foo:bar': { href: 'http://www.foo.com' }
1372
- },
1373
- id: '5678'
1374
- }
1375
- ]
1376
- }
1377
- })
1378
- })
1379
- .then(done)
1380
- .catch(done)
1381
- })
1382
-
1383
- it('should preserve 201 status code and use the location header when an entity has been POSTed', (done) => {
1384
- server.route({
1385
- method: 'post',
1386
- path: '/people',
1387
- config: {
1388
- handler (req, h) {
1389
- return h
1390
- .response({ id: 100, firstName: 'Bob', lastName: 'Smith' })
1391
- .created('/people/100')
1392
- }
1393
- }
1394
- })
1395
-
1396
- server
1397
- .register(halacious)
1398
- .then(() =>
1399
- server.inject({
1400
- method: 'post',
1401
- url: '/people',
1402
- headers: { Accept: 'application/hal+json' }
1403
- })
1404
- )
1405
- .then((res) => {
1406
- res.statusCode.should.equal(201)
1407
-
1408
- const result = JSON.parse(res.payload)
1409
-
1410
- result.should.deep.equal({
1411
- _links: {
1412
- self: { href: '/people/100' }
1413
- },
1414
- id: 100,
1415
- firstName: 'Bob',
1416
- lastName: 'Smith'
1417
- })
1418
- })
1419
- .then(done)
1420
- .catch(done)
1421
- })
1422
-
1423
- it('use of location header for absolute link generation should not break url search', (done) => {
1424
- server.route({
1425
- method: 'post',
1426
- path: '/people',
1427
- config: {
1428
- handler (req, h) {
1429
- return h
1430
- .response({ id: 100, firstName: 'Bob', lastName: 'Smith' })
1431
- .created('/people/100?donotbreakthis=true')
1432
- }
1433
- }
1434
- })
1435
-
1436
- server
1437
- .register({
1438
- plugin: halacious,
1439
- options: {
1440
- absolute: true
1441
- }
1442
- })
1443
- .then(() =>
1444
- server.inject({
1445
- method: 'post',
1446
- url: '/people',
1447
- headers: { Accept: 'application/hal+json' }
1448
- })
1449
- )
1450
- .then((res) => {
1451
- res.statusCode.should.equal(201)
1452
-
1453
- const result = JSON.parse(res.payload)
1454
-
1455
- result.should.have.a
1456
- .property('_links')
1457
- .that.has.a.property('self')
1458
- .that.has.a.property('href')
1459
- .and.satisfies(href => href.endsWith('/people/100?donotbreakthis=true'))
1460
- })
1461
- .then(done)
1462
- .catch(done)
1463
- })
1464
-
1465
- it('should support an array of acceptable media types', (done) => {
1466
- server.route({
1467
- method: 'get',
1468
- path: '/people/{id}',
1469
- config: {
1470
- handler () {
1471
- return { firstName: 'Bob', lastName: 'Smith' }
1472
- }
1473
- }
1474
- })
1475
-
1476
- server
1477
- .register({
1478
- plugin: halacious,
1479
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1480
- })
1481
- .then(() =>
1482
- server.inject({
1483
- method: 'get',
1484
- url: '/people/100'
1485
- })
1486
- )
1487
- .then((res) => {
1488
- res.statusCode.should.equal(200)
1489
-
1490
- res.headers['content-type'].should.contain('application/json')
1491
-
1492
- const result = JSON.parse(res.payload)
1493
- result.should.deep.equal({
1494
- _links: {
1495
- self: { href: '/people/100' }
1496
- },
1497
- firstName: 'Bob',
1498
- lastName: 'Smith'
1499
- })
1500
- })
1501
- .then(() =>
1502
- server.inject({
1503
- method: 'get',
1504
- url: '/people/100',
1505
- headers: { Accept: 'application/hal+json' }
1506
- })
1507
- )
1508
- .then((res) => {
1509
- res.statusCode.should.equal(200)
1510
-
1511
- res.headers['content-type'].should.contain('application/hal+json')
1512
-
1513
- const result = JSON.parse(res.payload)
1514
- result.should.deep.equal({
1515
- _links: {
1516
- self: { href: '/people/100' }
1517
- },
1518
- firstName: 'Bob',
1519
- lastName: 'Smith'
1520
- })
1521
- })
1522
- .then(done)
1523
- .catch(done)
1524
- })
1525
-
1526
- it('should regurgitate known query parameters in the self link', (done) => {
1527
- server.route({
1528
- method: 'get',
1529
- path: '/people',
1530
- config: {
1531
- handler () {
1532
- return { items: [{ id: 100, firstName: 'Louis', lastName: 'CK' }] }
1533
- },
1534
- plugins: {
1535
- hal: {
1536
- embedded: {
1537
- items: {
1538
- path: 'items',
1539
- href: './{item.id}'
1540
- }
1541
- },
1542
- query: '{?q*,start,limit}'
1543
- }
1544
- }
1545
- }
1546
- })
1547
-
1548
- server
1549
- .register({
1550
- plugin: halacious,
1551
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1552
- })
1553
- .then(() =>
1554
- server.inject({
1555
- method: 'get',
1556
- url: '/people?q=funny&start=1&token=12345',
1557
- headers: { Accept: 'application/hal+json' }
1558
- })
1559
- )
1560
- .then((res) => {
1561
- res.statusCode.should.equal(200)
1562
-
1563
- const result = JSON.parse(res.payload)
1564
-
1565
- result.should.deep.equal({
1566
- _links: {
1567
- self: { href: '/people?q=funny&start=1' }
1568
- },
1569
- _embedded: {
1570
- items: [
1571
- {
1572
- _links: { self: { href: '/people/100' } },
1573
- id: 100,
1574
- firstName: 'Louis',
1575
- lastName: 'CK'
1576
- }
1577
- ]
1578
- }
1579
- })
1580
- })
1581
- .then(done)
1582
- .catch(done)
1583
- })
1584
-
1585
- it('should resolve relative locations', (done) => {
1586
- server.route({
1587
- method: 'post',
1588
- path: '/api/people',
1589
- config: {
1590
- handler (req, h) {
1591
- return h
1592
- .response({ id: 100, firstName: 'Louis', lastName: 'CK' })
1593
- .created('api/people/100')
1594
- }
1595
- }
1596
- })
1597
-
1598
- server
1599
- .register({
1600
- plugin: halacious,
1601
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1602
- })
1603
- .then(() =>
1604
- server.inject({
1605
- method: 'post',
1606
- url: '/api/people',
1607
- headers: { Accept: 'application/hal+json' }
1608
- })
1609
- )
1610
- .then((res) => {
1611
- res.statusCode.should.equal(201)
1612
-
1613
- const result = JSON.parse(res.payload)
1614
-
1615
- result.should.deep.equal({
1616
- _links: {
1617
- self: { href: '/api/people/100' }
1618
- },
1619
- id: 100,
1620
- firstName: 'Louis',
1621
- lastName: 'CK'
1622
- })
1623
- })
1624
- .then(done)
1625
- .catch(done)
1626
- })
1627
-
1628
- it('should preserve response headers', (done) => {
1629
- server.route({
1630
- method: 'get',
1631
- path: '/api/people/100',
1632
- config: {
1633
- handler (req, h) {
1634
- return h
1635
- .response({ id: 100, firstName: 'Louis', lastName: 'CK' })
1636
- .header('Last-Modified', new Date())
1637
- }
1638
- }
1639
- })
1640
-
1641
- server
1642
- .register({
1643
- plugin: halacious,
1644
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1645
- })
1646
- .then(() =>
1647
- server.inject({
1648
- method: 'get',
1649
- url: '/api/people/100',
1650
- headers: { Accept: 'application/hal+json' }
1651
- })
1652
- )
1653
- .then((res) => {
1654
- res.statusCode.should.equal(200)
1655
-
1656
- res.headers['content-type'].should.equal('application/hal+json')
1657
-
1658
- should.exist(res.headers['last-modified'])
1659
- })
1660
- .then(done)
1661
- .catch(done)
1662
- })
1663
-
1664
- describe('when the absolute flag is turned on', () => {
1665
- it('should create an absolute self link', (done) => {
1666
- server.route({
1667
- method: 'get',
1668
- path: '/api/people/100',
1669
- config: {
1670
- handler () {
1671
- return { id: 100, firstName: 'Louis', lastName: 'CK' }
1672
- },
1673
- plugins: {
1674
- hal: {
1675
- absolute: true
1676
- }
1677
- }
1678
- }
1679
- })
1680
-
1681
- server
1682
- .register({
1683
- plugin: halacious,
1684
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1685
- })
1686
- .then(() =>
1687
- server.inject({
1688
- method: 'get',
1689
- url: 'http://localhost:9191/api/people/100',
1690
- headers: { Accept: 'application/hal+json' }
1691
- })
1692
- )
1693
- .then((res) => {
1694
- const result = JSON.parse(res.payload)
1695
- result._links.self.should.have.property(
1696
- 'href',
1697
- 'http://localhost:9191/api/people/100'
1698
- )
1699
- })
1700
- .then(done)
1701
- .catch(done)
1702
- })
1703
-
1704
- it('should create an absolute non-self link', (done) => {
1705
- server.route({
1706
- method: 'get',
1707
- path: '/api/people/100',
1708
- config: {
1709
- handler () {
1710
- return { id: 100, firstName: 'Louis', lastName: 'CK' }
1711
- },
1712
- plugins: {
1713
- hal: {
1714
- absolute: true,
1715
- links: {
1716
- schedule: './schedule'
1717
- }
1718
- }
1719
- }
1720
- }
1721
- })
1722
-
1723
- server
1724
- .register({
1725
- plugin: halacious,
1726
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1727
- })
1728
- .then(() =>
1729
- server.inject({
1730
- method: 'get',
1731
- url: 'http://localhost:9191/api/people/100',
1732
- headers: { Accept: 'application/hal+json' }
1733
- })
1734
- )
1735
- .then((res) => {
1736
- const result = JSON.parse(res.payload)
1737
- result._links.schedule.should.have.property(
1738
- 'href',
1739
- 'http://localhost:9191/api/people/100/schedule'
1740
- )
1741
- })
1742
- .then(done)
1743
- .catch(done)
1744
- })
1745
-
1746
- it('should embed an object with an absolute link', (done) => {
1747
- server.route({
1748
- method: 'get',
1749
- path: '/api/people/100',
1750
- config: {
1751
- handler () {
1752
- return {
1753
- firstName: 'Bob',
1754
- lastName: 'Smith',
1755
- boss: { firstName: 'Boss', lastName: 'Man' }
1756
- }
1757
- },
1758
- plugins: {
1759
- hal: {
1760
- absolute: true,
1761
- embedded: {
1762
- 'mco:boss': {
1763
- path: 'boss',
1764
- href: './boss'
1765
- }
1766
- }
1767
- }
1768
- }
1769
- }
1770
- })
1771
-
1772
- server
1773
- .register({
1774
- plugin: halacious,
1775
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1776
- })
1777
- .then(() =>
1778
- server.inject({
1779
- method: 'get',
1780
- url: 'http://localhost:9191/api/people/100',
1781
- headers: { Accept: 'application/hal+json' }
1782
- })
1783
- )
1784
- .then((res) => {
1785
- const result = JSON.parse(res.payload)
1786
- result._embedded['mco:boss']._links.self.should.have.property(
1787
- 'href',
1788
- 'http://localhost:9191/api/people/100/boss'
1789
- )
1790
- })
1791
- .then(done)
1792
- .catch(done)
1793
- })
1794
-
1795
- it('should handle created entities', (done) => {
1796
- server.route({
1797
- method: 'post',
1798
- path: '/api/people',
1799
- config: {
1800
- handler (req, h) {
1801
- return h
1802
- .response({ firstName: 'Bob', lastName: 'Smith' })
1803
- .created('/api/people/100')
1804
- },
1805
- plugins: {
1806
- hal: {
1807
- absolute: true
1808
- }
1809
- }
1810
- }
1811
- })
1812
-
1813
- server
1814
- .register({
1815
- plugin: halacious,
1816
- options: { mediaTypes: ['application/json', 'application/hal+json'] }
1817
- })
1818
- .then(() =>
1819
- server.inject({
1820
- method: 'post',
1821
- url: 'http://localhost:9191/api/people',
1822
- headers: { Accept: 'application/hal+json' }
1823
- })
1824
- )
1825
- .then((res) => {
1826
- const result = JSON.parse(res.payload)
1827
- result._links.self.should.have.property(
1828
- 'href',
1829
- 'http://localhost:9191/api/people/100'
1830
- )
1831
- })
1832
- .then(done)
1833
- .catch(done)
1834
- })
1835
-
1836
- it('should make configured links absolute', (done) => {
1837
- server.route({
1838
- method: 'post',
1839
- path: '/api/people',
1840
- config: {
1841
- handler () {
1842
- return { firstName: 'Bob', lastName: 'Smith' }
1843
- },
1844
- plugins: {
1845
- hal: {
1846
- absolute: true,
1847
- prepare (rep, done) {
1848
- rep.link('mco:boss', '/api/people/101')
1849
- done()
1850
- }
1851
- }
1852
- }
1853
- }
1854
- })
1855
-
1856
- server
1857
- .register({
1858
- plugin: halacious,
1859
- options: {
1860
- mediaTypes: ['application/json', 'application/hal+json'],
1861
- absolute: true
1862
- }
1863
- })
1864
- .then(() =>
1865
- server.inject({
1866
- method: 'post',
1867
- url: 'http://localhost:9191/api/people',
1868
- headers: { Accept: 'application/hal+json' }
1869
- })
1870
- )
1871
- .then((res) => {
1872
- const result = JSON.parse(res.payload)
1873
- result.should.have
1874
- .property('_links')
1875
- .that.has.property('mco:boss')
1876
- .that.has.property('href', 'http://localhost:9191/api/people/101')
1877
- })
1878
- .then(done)
1879
- .catch(done)
1880
- })
1881
- })
1882
-
1883
- it('should support resolving embedded hrefs by ids', (done) => {
1884
- server.route({
1885
- method: 'get',
1886
- path: '/people/{id}',
1887
- config: {
1888
- id: 'person',
1889
- handler (req) {
1890
- return {
1891
- id: req.params.id,
1892
- firstName: 'Bob',
1893
- lastName: 'Smith',
1894
- bossId: '1234'
1895
- }
1896
- },
1897
- plugins: {
1898
- hal: {
1899
- query: '{?full}'
1900
- }
1901
- }
1902
- }
1903
- })
1904
-
1905
- server.route({
1906
- method: 'get',
1907
- path: '/people',
1908
- handler () {
1909
- return {
1910
- items: [{ id: 100 }, { id: 200 }]
1911
- }
1912
- },
1913
- config: {
1914
- plugins: {
1915
- hal: {
1916
- embedded: {
1917
- 'mco:person': {
1918
- path: 'items',
1919
- href (rep, ctx) {
1920
- return rep.route('person', { id: ctx.item.id })
1921
- }
1922
- }
1923
- }
1924
- }
1925
- }
1926
- }
1927
- })
1928
-
1929
- server
1930
- .register(halacious)
1931
- .then(() => {
1932
- const {
1933
- plugins: {
1934
- [PLUGIN]: { namespaces }
1935
- }
1936
- } = server
1937
-
1938
- namespaces
1939
- .add({ name: 'mycompany', prefix: 'mco' })
1940
- .rel({ name: 'person' })
1941
- })
1942
- .then(() =>
1943
- server.inject({
1944
- method: 'get',
1945
- url: '/people',
1946
- headers: { Accept: 'application/hal+json' }
1947
- })
1948
- )
1949
- .then((res) => {
1950
- res.statusCode.should.equal(200)
1951
-
1952
- const result = JSON.parse(res.payload)
1953
-
1954
- result.should.deep.equal({
1955
- _links: {
1956
- self: { href: '/people' },
1957
- curies: [
1958
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
1959
- ]
1960
- },
1961
- _embedded: {
1962
- 'mco:person': [
1963
- {
1964
- _links: {
1965
- self: { href: '/people/100{?full}' }
1966
- },
1967
- id: 100
1968
- },
1969
- {
1970
- _links: {
1971
- self: { href: '/people/200{?full}' }
1972
- },
1973
- id: 200
1974
- }
1975
- ]
1976
- }
1977
- })
1978
- })
1979
- .then(done)
1980
- .catch(done)
1981
- })
1982
-
1983
- it('should support resolving link hrefs by ids', (done) => {
1984
- server.route({
1985
- method: 'get',
1986
- path: '/people/{id}',
1987
- config: {
1988
- id: 'person',
1989
- handler (req) {
1990
- return {
1991
- id: req.params.id,
1992
- firstName: 'Bob',
1993
- lastName: 'Smith',
1994
- bossId: '1234'
1995
- }
1996
- },
1997
- plugins: {
1998
- hal: {
1999
- query: '{?full}',
2000
- links: {
2001
- 'mco:boss': function (rep, entity) {
2002
- return rep.route('person', { id: entity.bossId })
2003
- }
2004
- }
2005
- }
2006
- }
2007
- }
2008
- })
2009
-
2010
- server
2011
- .register(halacious)
2012
- .then(() => {
2013
- const {
2014
- plugins: {
2015
- [PLUGIN]: { namespaces }
2016
- }
2017
- } = server
2018
-
2019
- namespaces
2020
- .add({ name: 'mycompany', prefix: 'mco' })
2021
- .rel({ name: 'person' })
2022
- })
2023
- .then(() =>
2024
- server.inject({
2025
- method: 'get',
2026
- url: '/people/100',
2027
- headers: { Accept: 'application/hal+json' }
2028
- })
2029
- )
2030
- .then((res) => {
2031
- res.statusCode.should.equal(200)
2032
-
2033
- const result = JSON.parse(res.payload)
2034
-
2035
- result.should.deep.equal({
2036
- _links: {
2037
- curies: [
2038
- { name: 'mco', href: '/rels/mycompany/{rel}', templated: true }
2039
- ],
2040
- self: { href: '/people/100' },
2041
- 'mco:boss': { href: '/people/1234{?full}', templated: true }
2042
- },
2043
- id: '100',
2044
- firstName: 'Bob',
2045
- lastName: 'Smith',
2046
- bossId: '1234'
2047
- })
2048
- })
2049
- .then(done)
2050
- .catch(done)
2051
- })
2052
-
2053
- it('should support absolute api root hrefs', (done) => {
2054
- server = new hapi.Server({
2055
- debug: { request: ['*'], log: ['*'] },
2056
- port: 9191
2057
- })
2058
-
2059
- server.route({
2060
- method: 'get',
2061
- path: '/people',
2062
- config: {
2063
- id: 'person',
2064
- handler () {
2065
- return []
2066
- },
2067
- plugins: {
2068
- hal: {
2069
- api: 'mco:people',
2070
- query: '{?full}'
2071
- }
2072
- }
2073
- }
2074
- })
2075
-
2076
- server
2077
- .register({ plugin: halacious, options: { absolute: true } })
2078
- .then(() => {
2079
- const {
2080
- plugins: {
2081
- [PLUGIN]: { namespaces }
2082
- }
2083
- } = server
2084
-
2085
- namespaces
2086
- .add({ name: 'mycompany', prefix: 'mco' })
2087
- .rel({ name: 'person' })
2088
- })
2089
- .then(() =>
2090
- server.inject({
2091
- method: 'get',
2092
- url: '/api/'
2093
- })
2094
- )
2095
- .then((res) => {
2096
- res.statusCode.should.equal(200)
2097
-
2098
- const result = JSON.parse(res.payload)
2099
-
2100
- result.should.deep.equal({
2101
- _links: {
2102
- curies: [
2103
- {
2104
- name: 'mco',
2105
- href: `${server.info.uri}/rels/mycompany/{rel}`,
2106
- templated: true
2107
- }
2108
- ],
2109
- self: { href: `${server.info.uri}/api/` },
2110
- 'mco:people': {
2111
- href: `${server.info.uri}/people{?full}`,
2112
- templated: true
2113
- }
2114
- }
2115
- })
2116
- })
2117
- .then(done)
2118
- .catch(done)
2119
- })
2120
-
2121
- it('should embed an empty representation', (done) => {
2122
- server.route({
2123
- method: 'get',
2124
- path: '/people',
2125
- config: {
2126
- id: 'person',
2127
- handler () {
2128
- return { employees: [] }
2129
- },
2130
- plugins: {
2131
- hal: {
2132
- api: 'mco:person',
2133
- embedded: {
2134
- 'mco:person': {
2135
- path: 'employees',
2136
- href: '../{item.id}'
2137
- }
2138
- }
2139
- }
2140
- }
2141
- }
2142
- })
2143
-
2144
- server
2145
- .register({ plugin: halacious, options: { absolute: true } })
2146
- .then(() => {
2147
- const {
2148
- plugins: {
2149
- [PLUGIN]: { namespaces }
2150
- }
2151
- } = server
2152
-
2153
- namespaces
2154
- .add({ name: 'mycompany', prefix: 'mco' })
2155
- .rel({ name: 'person' })
2156
- })
2157
- .then(() =>
2158
- server.inject({
2159
- method: 'get',
2160
- url: '/people'
2161
- })
2162
- )
2163
- .then((res) => {
2164
- res.statusCode.should.equal(200)
2165
-
2166
- const result = JSON.parse(res.payload)
2167
-
2168
- result.should.deep.equal({
2169
- _links: {
2170
- curies: [
2171
- {
2172
- name: 'mco',
2173
- href: `${server.info.uri}/rels/mycompany/{rel}`,
2174
- templated: true
2175
- }
2176
- ],
2177
- self: { href: `${server.info.uri}/people` }
2178
- },
2179
- _embedded: {
2180
- 'mco:person': []
2181
- }
2182
- })
2183
- })
2184
- .then(done)
2185
- .catch(done)
2186
- })
2187
-
2188
- it('should not mess with array responses', (done) => {
2189
- server.route({
2190
- method: 'get',
2191
- path: '/people',
2192
- config: {
2193
- id: 'person',
2194
- handler () {
2195
- return [{ name: 'Dick' }, { name: 'Jane' }, { name: 'Spot' }]
2196
- }
2197
- }
2198
- })
2199
-
2200
- server
2201
- .register({ plugin: halacious, options: { absolute: true } })
2202
- .then(() =>
2203
- server.inject({
2204
- method: 'get',
2205
- url: '/people'
2206
- })
2207
- )
2208
- .then((res) => {
2209
- res.statusCode.should.equal(200)
2210
-
2211
- const result = JSON.parse(res.payload)
2212
-
2213
- result.should.be.an.instanceOf(Array)
2214
- result.should.have.deep.members([
2215
- { name: 'Dick' },
2216
- { name: 'Jane' },
2217
- { name: 'Spot' }
2218
- ])
2219
- })
2220
- .then(done)
2221
- .catch(done)
2222
- })
2223
-
2224
- it('should not process internal routes', (done) => {
2225
- const employee = { first: 'John', last: 'Doe' }
2226
-
2227
- server.route({
2228
- method: 'get',
2229
- path: '/people',
2230
- config: {
2231
- id: 'person',
2232
- handler () {
2233
- return employee
2234
- },
2235
- isInternal: true
2236
- }
2237
- })
2238
-
2239
- server
2240
- .register({ plugin: halacious, options: { absolute: true } })
2241
- .then(() =>
2242
- server.inject({
2243
- method: 'get',
2244
- url: '/people',
2245
- allowInternals: true
2246
- })
2247
- )
2248
- .then((res) => {
2249
- res.statusCode.should.equal(200)
2250
-
2251
- res.result.should.equal(employee)
2252
- })
2253
- .then(done)
2254
- .catch(done)
2255
- })
2256
-
2257
- it('should support external filtering of requests', (done) => {
2258
- const employee = { first: 'John', last: 'Doe' }
2259
-
2260
- server.route({
2261
- method: 'get',
2262
- path: '/people',
2263
- config: {
2264
- id: 'person',
2265
- handler () {
2266
- return employee
2267
- }
2268
- }
2269
- })
2270
-
2271
- server
2272
- .register({ plugin: halacious, options: { absolute: true } })
2273
- .then(() => {
2274
- const {
2275
- plugins: { [PLUGIN]: plugin }
2276
- } = server
2277
-
2278
- plugin.should.respondTo('filter')
2279
-
2280
- plugin.filter((request) => {
2281
- should.exist(request)
2282
- return false
2283
- })
2284
- })
2285
- .then(() =>
2286
- server.inject({
2287
- method: 'get',
2288
- url: '/people'
2289
- })
2290
- )
2291
- .then((res) => {
2292
- res.statusCode.should.equal(200)
2293
-
2294
- res.result.should.equal(employee)
2295
- })
2296
- .then(done)
2297
- .catch(done)
2298
- })
2299
-
2300
- it('should support overriding the url protocol', (done) => {
2301
- const employee = { first: 'John', last: 'Doe' }
2302
-
2303
- server.route({
2304
- method: 'get',
2305
- path: '/people',
2306
- config: {
2307
- id: 'person',
2308
- handler () {
2309
- return employee
2310
- }
2311
- }
2312
- })
2313
-
2314
- server
2315
- .register({
2316
- plugin: halacious,
2317
- options: { absolute: true, protocol: 'https' }
2318
- })
2319
- .then(() =>
2320
- server.inject({
2321
- method: 'get',
2322
- url: '/people'
2323
- })
2324
- )
2325
- .then((res) => {
2326
- res.statusCode.should.equal(200)
2327
-
2328
- const result = JSON.parse(res.payload)
2329
-
2330
- result._links.self.href.should.match(/https/)
2331
- })
2332
- .then(done)
2333
- .catch(done)
2334
- })
2335
-
2336
- it('should support overriding the hostname', (done) => {
2337
- const employee = { first: 'John', last: 'Doe' }
2338
-
2339
- server.route({
2340
- method: 'get',
2341
- path: '/people',
2342
- config: {
2343
- id: 'person',
2344
- handler () {
2345
- return employee
2346
- }
2347
- }
2348
- })
2349
-
2350
- server
2351
- .register({
2352
- plugin: halacious,
2353
- options: { absolute: true, host: 'www.cloud.com' }
2354
- })
2355
- .then(() =>
2356
- server.inject({
2357
- method: 'get',
2358
- headers: { host: null },
2359
- url: '/people'
2360
- })
2361
- )
2362
- .then((res) => {
2363
- res.statusCode.should.equal(200)
2364
-
2365
- const result = JSON.parse(res.payload)
2366
-
2367
- result._links.self.href.should.match(/http:\/\/www.cloud.com/)
2368
- })
2369
- .then(done)
2370
- .catch(done)
2371
- })
2372
-
2373
- it('should support overriding the url builder', (done) => {
2374
- const employee = { first: 'John', last: 'Doe' }
2375
-
2376
- server.route({
2377
- method: 'get',
2378
- path: '/people',
2379
- config: {
2380
- id: 'person',
2381
- handler () {
2382
- return employee
2383
- }
2384
- }
2385
- })
2386
-
2387
- server
2388
- .register({ plugin: halacious, options: { absolute: true } })
2389
- .then(() => {
2390
- const {
2391
- plugins: { [PLUGIN]: plugin }
2392
- } = server
2393
-
2394
- plugin.should.respondTo('urlBuilder')
2395
-
2396
- plugin.urlBuilder((request, path, search) =>
2397
- url.format({
2398
- hostname: 'www.myapp.com',
2399
- port: 12345,
2400
- pathname: path,
2401
- protocol: 'https',
2402
- search
2403
- })
2404
- )
2405
- })
2406
- .then(() =>
2407
- server.inject({
2408
- method: 'get',
2409
- url: '/people'
2410
- })
2411
- )
2412
- .then((res) => {
2413
- res.statusCode.should.equal(200)
2414
-
2415
- const result = JSON.parse(res.payload)
2416
-
2417
- result._links.self.href.should.match(/https:\/\/www.myapp.com:12345/)
2418
- })
2419
- .then(done)
2420
- .catch(done)
2421
- })
2422
-
2423
- it('should not HALify when another media type is preferred by default', (done) => {
2424
- server.route({
2425
- method: 'get',
2426
- path: '/people/{id}',
2427
- config: {
2428
- handler () {
2429
- return { firstName: 'Bob', lastName: 'Smith' }
2430
- }
2431
- }
2432
- })
2433
-
2434
- server
2435
- .register({
2436
- plugin: halacious,
2437
- options: { requireHalJsonAcceptHeader: true }
2438
- })
2439
- .then(() =>
2440
- server.inject({
2441
- method: 'get',
2442
- url: '/people/100'
2443
- })
2444
- )
2445
- .then((res) => {
2446
- res.statusCode.should.equal(200)
2447
-
2448
- res.headers['content-type'].should.contain('application/json')
2449
-
2450
- const result = JSON.parse(res.payload)
2451
- result.should.deep.equal({
2452
- firstName: 'Bob',
2453
- lastName: 'Smith'
2454
- })
2455
- })
2456
- .then(() =>
2457
- server.inject({
2458
- method: 'get',
2459
- url: '/people/100',
2460
- headers: { Accept: 'application/json' }
2461
- })
2462
- )
2463
- .then((res) => {
2464
- res.statusCode.should.equal(200)
2465
-
2466
- res.headers['content-type'].should.contain('application/json')
2467
-
2468
- const result = JSON.parse(res.payload)
2469
- result.should.deep.equal({
2470
- firstName: 'Bob',
2471
- lastName: 'Smith'
2472
- })
2473
- })
2474
- .then(() =>
2475
- server.inject({
2476
- method: 'get',
2477
- url: '/people/100',
2478
- headers: { Accept: 'application/hal+json' }
2479
- })
2480
- )
2481
- .then((res) => {
2482
- res.statusCode.should.equal(200)
2483
-
2484
- res.headers['content-type'].should.contain('application/hal+json')
2485
-
2486
- const result = JSON.parse(res.payload)
2487
- result.should.deep.equal({
2488
- _links: {
2489
- self: { href: '/people/100' }
2490
- },
2491
- firstName: 'Bob',
2492
- lastName: 'Smith'
2493
- })
2494
- })
2495
- .then(done)
2496
- .catch(done)
2497
- })
2498
-
2499
- it('should HALify when application/hal+json is explicitly asked for', (done) => {
2500
- server.route({
2501
- method: 'get',
2502
- path: '/people/{id}',
2503
- config: {
2504
- handler () {
2505
- return { firstName: 'Bob', lastName: 'Smith' }
2506
- }
2507
- }
2508
- })
2509
-
2510
- server
2511
- .register({
2512
- plugin: halacious,
2513
- options: { requireHalJsonAcceptHeader: true }
2514
- })
2515
- .then(() =>
2516
- server.inject({
2517
- method: 'get',
2518
- url: '/people/100',
2519
- headers: { accept: 'application/hal+json' }
2520
- })
2521
- )
2522
- .then((res) => {
2523
- res.statusCode.should.equal(200)
2524
-
2525
- res.headers['content-type'].should.contain('application/hal+json')
2526
-
2527
- const result = JSON.parse(res.payload)
2528
- result.should.deep.equal({
2529
- _links: { self: { href: '/people/100' } },
2530
- firstName: 'Bob',
2531
- lastName: 'Smith'
2532
- })
2533
- })
2534
- .then(done)
2535
- .catch(done)
2536
- })
2537
-
2538
- it('should not replace the original successful response to allow to modify it by other plugins', (done) => {
2539
- server.route({
2540
- method: 'get',
2541
- path: '/people/{id}',
2542
- config: {
2543
- handler () {
2544
- return { firstName: 'Bob', lastName: 'Smith' }
2545
- }
2546
- }
2547
- })
2548
-
2549
- const callback = sinon.spy()
2550
-
2551
- const anotherPlugin = {
2552
- name: 'anotherPlugin',
2553
- version: '1.0.0',
2554
-
2555
- async register (server) {
2556
- server.ext({
2557
- type: 'onPostHandler',
2558
- method (request, h) {
2559
- callback()
2560
- return h.continue
2561
- }
2562
- })
2563
- }
2564
- }
2565
-
2566
- const plugins = [
2567
- {
2568
- plugin: halacious,
2569
- options: {
2570
- requireHalJsonAcceptHeader: true
2571
- }
2572
- },
2573
- {
2574
- plugin: anotherPlugin
2575
- }
2576
- ]
2577
-
2578
- server
2579
- .register(plugins)
2580
- .then(() =>
2581
- server.inject({
2582
- method: 'get',
2583
- url: '/people/100',
2584
- headers: { accept: 'application/hal+json' }
2585
- })
2586
- )
2587
- .then((res) => {
2588
- res.statusCode.should.equal(200)
2589
-
2590
- res.headers['content-type'].should.contain('application/hal+json')
2591
-
2592
- const result = JSON.parse(res.payload)
2593
-
2594
- callback.should.be.called // eslint-disable-line
2595
-
2596
- result.should.deep.equal({
2597
- _links: { self: { href: '/people/100' } },
2598
- firstName: 'Bob',
2599
- lastName: 'Smith'
2600
- })
2601
- })
2602
- .then(done)
2603
- .catch(done)
2604
- })
2605
- })