impulse-api 3.0.6 → 3.0.8
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.
- package/package.json +1 -1
- package/src/server.js +20 -8
- package/test/custom-jwt-validation.js +3 -2
- package/test/server-test.js +278 -1
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -266,16 +266,23 @@ class Server {
|
|
|
266
266
|
|
|
267
267
|
if (route.inputs) {
|
|
268
268
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
// When rawBody is true, exclude req.body from inputs processing since it's a Buffer
|
|
270
|
+
const paramsForInputs = route.rawBody === true
|
|
271
|
+
? Object.assign(req.query || {}, req.params || {}, req.files || {})
|
|
272
|
+
: Object.assign(req.query || {}, req.body || {}, req.params || {}, req.files || {});
|
|
273
|
+
|
|
274
|
+
data = this.buildParameters(paramsForInputs, route.inputs);
|
|
273
275
|
} catch (error) {
|
|
274
276
|
sendResponse(error);
|
|
275
277
|
return;
|
|
276
278
|
}
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
// Handle rawBody - append the raw Buffer if rawBody flag is set
|
|
282
|
+
if (route.rawBody === true) {
|
|
283
|
+
data.rawBody = req.body; // This will be a Buffer, not a parsed object
|
|
284
|
+
}
|
|
285
|
+
|
|
279
286
|
if (route.headers) {
|
|
280
287
|
try {
|
|
281
288
|
data.headers = this.buildParameters(req.headers, route.headers);
|
|
@@ -474,10 +481,15 @@ class Server {
|
|
|
474
481
|
method = route.method.toLowerCase();
|
|
475
482
|
verb = (verbMap[method]) ? verbMap[method] : method;
|
|
476
483
|
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
484
|
+
// For routes that need raw body (e.g., webhooks), use express.raw() instead of JSON
|
|
485
|
+
if (route.rawBody === true) {
|
|
486
|
+
this.http[verb](route.endpoint, express.raw({ type: 'application/json' }), this.preprocessor.bind(this, route));
|
|
487
|
+
} else {
|
|
488
|
+
// express-fileupload handles multipart/form-data (including files)
|
|
489
|
+
// express.json() handles JSON bodies
|
|
490
|
+
// multer().none() is not needed and conflicts with file uploads
|
|
491
|
+
this.http[verb](route.endpoint, this.preprocessor.bind(this, route));
|
|
492
|
+
}
|
|
481
493
|
|
|
482
494
|
});
|
|
483
495
|
});
|
|
@@ -253,12 +253,13 @@ exports.testRoute = {
|
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
it('should validate custom validator function type', () => {
|
|
256
|
+
const auth = new Auth('test-secret');
|
|
256
257
|
assert.throws(() => {
|
|
257
|
-
|
|
258
|
+
auth.createCustomValidator('not-a-function');
|
|
258
259
|
}, /Custom validator must be a function/);
|
|
259
260
|
|
|
260
261
|
assert.doesNotThrow(() => {
|
|
261
|
-
|
|
262
|
+
auth.createCustomValidator(() => {});
|
|
262
263
|
});
|
|
263
264
|
});
|
|
264
265
|
});
|
package/test/server-test.js
CHANGED
|
@@ -449,7 +449,7 @@ describe('server-test', () => {
|
|
|
449
449
|
auth.createCustomValidator('not-a-function');
|
|
450
450
|
assert.fail('Should have thrown an error');
|
|
451
451
|
} catch (e) {
|
|
452
|
-
assert.contains(e.message, 'must be a function');
|
|
452
|
+
assert.contains(e.message, 'Custom validator must be a function');
|
|
453
453
|
}
|
|
454
454
|
});
|
|
455
455
|
|
|
@@ -499,4 +499,281 @@ describe('server-test', () => {
|
|
|
499
499
|
assert.strictEqual(server.auth, null);
|
|
500
500
|
});
|
|
501
501
|
});
|
|
502
|
+
|
|
503
|
+
describe('rawBody functionality', () => {
|
|
504
|
+
it('should set rawBody as Buffer when route.rawBody is true', async () => {
|
|
505
|
+
const Api = new Server({
|
|
506
|
+
name: 'test-Server',
|
|
507
|
+
routeDir: './test-routes',
|
|
508
|
+
port: 4000,
|
|
509
|
+
env: 'test',
|
|
510
|
+
services: {}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data' }));
|
|
514
|
+
const req = {
|
|
515
|
+
body: testBody,
|
|
516
|
+
query: {},
|
|
517
|
+
params: {},
|
|
518
|
+
files: {},
|
|
519
|
+
headers: {},
|
|
520
|
+
get: (header) => {
|
|
521
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
522
|
+
if (header === 'host') return 'localhost:4000';
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
const res = {
|
|
527
|
+
status: (code) => {
|
|
528
|
+
res.statusCode = code;
|
|
529
|
+
return res;
|
|
530
|
+
},
|
|
531
|
+
send: (data) => {
|
|
532
|
+
res.sentData = data;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const route = {
|
|
537
|
+
name: 'test-raw-body',
|
|
538
|
+
method: 'post',
|
|
539
|
+
endpoint: '/test',
|
|
540
|
+
rawBody: true,
|
|
541
|
+
run: (services, inputs, next) => {
|
|
542
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
543
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
544
|
+
next(200, { success: true });
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
await Api.preprocessor(route, req, res);
|
|
549
|
+
assert.strictEqual(res.statusCode, 200);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should not set rawBody when route.rawBody is false', async () => {
|
|
553
|
+
const Api = new Server({
|
|
554
|
+
name: 'test-Server',
|
|
555
|
+
routeDir: './test-routes',
|
|
556
|
+
port: 4000,
|
|
557
|
+
env: 'test',
|
|
558
|
+
services: {}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const testBody = { test: 'data' };
|
|
562
|
+
const req = {
|
|
563
|
+
body: testBody,
|
|
564
|
+
query: {},
|
|
565
|
+
params: {},
|
|
566
|
+
files: {},
|
|
567
|
+
headers: {},
|
|
568
|
+
get: (header) => {
|
|
569
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
570
|
+
if (header === 'host') return 'localhost:4000';
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const res = {
|
|
575
|
+
status: (code) => {
|
|
576
|
+
res.statusCode = code;
|
|
577
|
+
return res;
|
|
578
|
+
},
|
|
579
|
+
send: (data) => {
|
|
580
|
+
res.sentData = data;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const route = {
|
|
585
|
+
name: 'test-no-raw-body',
|
|
586
|
+
method: 'post',
|
|
587
|
+
endpoint: '/test',
|
|
588
|
+
rawBody: false,
|
|
589
|
+
run: (services, inputs, next) => {
|
|
590
|
+
assert.strictEqual(inputs.rawBody, undefined);
|
|
591
|
+
next(200, { success: true });
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
await Api.preprocessor(route, req, res);
|
|
596
|
+
assert.strictEqual(res.statusCode, 200);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('should process inputs alongside rawBody when both are present', async () => {
|
|
600
|
+
const Api = new Server({
|
|
601
|
+
name: 'test-Server',
|
|
602
|
+
routeDir: './test-routes',
|
|
603
|
+
port: 4000,
|
|
604
|
+
env: 'test',
|
|
605
|
+
services: {}
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data' }));
|
|
609
|
+
const req = {
|
|
610
|
+
body: testBody,
|
|
611
|
+
query: { param1: 'value1' },
|
|
612
|
+
params: { id: '123' },
|
|
613
|
+
files: {},
|
|
614
|
+
headers: {},
|
|
615
|
+
get: (header) => {
|
|
616
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
617
|
+
if (header === 'host') return 'localhost:4000';
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
const res = {
|
|
622
|
+
status: (code) => {
|
|
623
|
+
res.statusCode = code;
|
|
624
|
+
return res;
|
|
625
|
+
},
|
|
626
|
+
send: (data) => {
|
|
627
|
+
res.sentData = data;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const route = {
|
|
632
|
+
name: 'test-raw-body-with-inputs',
|
|
633
|
+
method: 'post',
|
|
634
|
+
endpoint: '/test/:id',
|
|
635
|
+
rawBody: true,
|
|
636
|
+
inputs: {
|
|
637
|
+
param1: {
|
|
638
|
+
required: true
|
|
639
|
+
},
|
|
640
|
+
id: {
|
|
641
|
+
required: true
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
run: (services, inputs, next) => {
|
|
645
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
646
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
647
|
+
assert.strictEqual(inputs.param1, 'value1');
|
|
648
|
+
assert.strictEqual(inputs.id, '123');
|
|
649
|
+
next(200, { success: true });
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
await Api.preprocessor(route, req, res);
|
|
654
|
+
assert.strictEqual(res.statusCode, 200);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('should handle rawBody without inputs', async () => {
|
|
658
|
+
const Api = new Server({
|
|
659
|
+
name: 'test-Server',
|
|
660
|
+
routeDir: './test-routes',
|
|
661
|
+
port: 4000,
|
|
662
|
+
env: 'test',
|
|
663
|
+
services: {}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
const testBody = Buffer.from('raw string data');
|
|
667
|
+
const req = {
|
|
668
|
+
body: testBody,
|
|
669
|
+
query: {},
|
|
670
|
+
params: {},
|
|
671
|
+
files: {},
|
|
672
|
+
headers: {},
|
|
673
|
+
get: (header) => {
|
|
674
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
675
|
+
if (header === 'host') return 'localhost:4000';
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
const res = {
|
|
680
|
+
status: (code) => {
|
|
681
|
+
res.statusCode = code;
|
|
682
|
+
return res;
|
|
683
|
+
},
|
|
684
|
+
send: (data) => {
|
|
685
|
+
res.sentData = data;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const route = {
|
|
690
|
+
name: 'test-raw-body-only',
|
|
691
|
+
method: 'post',
|
|
692
|
+
endpoint: '/test',
|
|
693
|
+
rawBody: true,
|
|
694
|
+
run: (services, inputs, next) => {
|
|
695
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
696
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
697
|
+
next(200, { success: true });
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
await Api.preprocessor(route, req, res);
|
|
702
|
+
assert.strictEqual(res.statusCode, 200);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it('should not include Buffer body in inputs processing when rawBody is true', async () => {
|
|
706
|
+
const Api = new Server({
|
|
707
|
+
name: 'test-Server',
|
|
708
|
+
routeDir: './test-routes',
|
|
709
|
+
port: 4000,
|
|
710
|
+
env: 'test',
|
|
711
|
+
services: {}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
const testBody = Buffer.from(JSON.stringify({ test: 'data', shouldNotBeParsed: true }));
|
|
715
|
+
const req = {
|
|
716
|
+
body: testBody,
|
|
717
|
+
query: { param1: 'value1' },
|
|
718
|
+
params: { id: '123' },
|
|
719
|
+
files: {},
|
|
720
|
+
headers: {},
|
|
721
|
+
get: (header) => {
|
|
722
|
+
if (header === 'origin') return 'http://localhost:4000';
|
|
723
|
+
if (header === 'host') return 'localhost:4000';
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const res = {
|
|
728
|
+
status: (code) => {
|
|
729
|
+
res.statusCode = code;
|
|
730
|
+
return res;
|
|
731
|
+
},
|
|
732
|
+
send: (data) => {
|
|
733
|
+
res.sentData = data;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const route = {
|
|
738
|
+
name: 'test-raw-body-excluded-from-inputs',
|
|
739
|
+
method: 'post',
|
|
740
|
+
endpoint: '/test/:id',
|
|
741
|
+
rawBody: true,
|
|
742
|
+
inputs: {
|
|
743
|
+
param1: {
|
|
744
|
+
required: true
|
|
745
|
+
},
|
|
746
|
+
id: {
|
|
747
|
+
required: true
|
|
748
|
+
},
|
|
749
|
+
// This should NOT be found in inputs even though it's in the Buffer body
|
|
750
|
+
test: {
|
|
751
|
+
required: false
|
|
752
|
+
},
|
|
753
|
+
shouldNotBeParsed: {
|
|
754
|
+
required: false
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
run: (services, inputs, next) => {
|
|
758
|
+
// Verify rawBody is still a Buffer
|
|
759
|
+
assert.strictEqual(Buffer.isBuffer(inputs.rawBody), true);
|
|
760
|
+
assert.deepStrictEqual(inputs.rawBody, testBody);
|
|
761
|
+
|
|
762
|
+
// Verify inputs from query/params work
|
|
763
|
+
assert.strictEqual(inputs.param1, 'value1');
|
|
764
|
+
assert.strictEqual(inputs.id, '123');
|
|
765
|
+
|
|
766
|
+
// Verify that parsed body fields are NOT in inputs (because Buffer was excluded)
|
|
767
|
+
// buildParameters sets missing optional params to empty string, not undefined
|
|
768
|
+
assert.strictEqual(inputs.test, "");
|
|
769
|
+
assert.strictEqual(inputs.shouldNotBeParsed, "");
|
|
770
|
+
|
|
771
|
+
next(200, { success: true });
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
await Api.preprocessor(route, req, res);
|
|
776
|
+
assert.strictEqual(res.statusCode, 200);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
502
779
|
});
|