@@ -353,6 +353,363 @@ testCase('route with unknown unions', ROUTE_WITH_UNKNOWN_UNIONS, {
353353 } ,
354354} ) ;
355355
356+ const ROUTE_WITH_PATH_PARAMS_IN_UNION_NOT_FIRST = `
357+ import * as t from 'io-ts';
358+ import * as h from '@api-ts/io-ts-http';
359+
360+ export const route = h.httpRoute({
361+ path: '/internal/api/policy/v1/{applicationName}/touchpoints/{touchpoint}/rules/evaluation',
362+ method: 'POST',
363+ request: t.union([
364+ h.httpRequest({
365+ body: { emptyRequest: t.boolean }
366+ }),
367+ h.httpRequest({
368+ params: {
369+ applicationName: t.string,
370+ touchpoint: t.string,
371+ },
372+ body: { requestWithParams: t.string }
373+ }),
374+ ]),
375+ response: {
376+ 200: t.string,
377+ },
378+ });
379+ ` ;
380+
381+ testCase (
382+ 'route with path params in union second schema (regression test)' ,
383+ ROUTE_WITH_PATH_PARAMS_IN_UNION_NOT_FIRST ,
384+ {
385+ info : {
386+ title : 'Test' ,
387+ version : '1.0.0' ,
388+ } ,
389+ openapi : '3.0.3' ,
390+ paths : {
391+ '/internal/api/policy/v1/{applicationName}/touchpoints/{touchpoint}/rules/evaluation' :
392+ {
393+ post : {
394+ parameters : [
395+ {
396+ in : 'path' ,
397+ name : 'applicationName' ,
398+ required : true ,
399+ schema : { type : 'string' } ,
400+ } ,
401+ {
402+ in : 'path' ,
403+ name : 'touchpoint' ,
404+ required : true ,
405+ schema : { type : 'string' } ,
406+ } ,
407+ ] ,
408+ requestBody : {
409+ content : {
410+ 'application/json' : {
411+ schema : {
412+ oneOf : [
413+ {
414+ properties : {
415+ emptyRequest : { type : 'boolean' } ,
416+ } ,
417+ required : [ 'emptyRequest' ] ,
418+ type : 'object' ,
419+ } ,
420+ {
421+ properties : {
422+ requestWithParams : { type : 'string' } ,
423+ } ,
424+ required : [ 'requestWithParams' ] ,
425+ type : 'object' ,
426+ } ,
427+ ] ,
428+ } ,
429+ } ,
430+ } ,
431+ } ,
432+ responses : {
433+ '200' : {
434+ description : 'OK' ,
435+ content : {
436+ 'application/json' : {
437+ schema : {
438+ type : 'string' ,
439+ } ,
440+ } ,
441+ } ,
442+ } ,
443+ } ,
444+ } ,
445+ } ,
446+ } ,
447+ components : {
448+ schemas : { } ,
449+ } ,
450+ } ,
451+ ) ;
452+
453+ const ROUTE_WITH_PATH_PARAMS_ONLY_IN_THIRD_SCHEMA = `
454+ import * as t from 'io-ts';
455+ import * as h from '@api-ts/io-ts-http';
456+
457+ export const route = h.httpRoute({
458+ path: '/api/{userId}/posts/{postId}',
459+ method: 'GET',
460+ request: t.union([
461+ // First: empty request
462+ h.httpRequest({}),
463+ // Second: only query params
464+ h.httpRequest({
465+ query: { filter: t.string }
466+ }),
467+ // Third: has the path params
468+ h.httpRequest({
469+ params: {
470+ userId: t.string,
471+ postId: t.string,
472+ },
473+ query: { details: t.boolean }
474+ }),
475+ ]),
476+ response: {
477+ 200: t.string,
478+ },
479+ });
480+ ` ;
481+
482+ testCase (
483+ 'route with path params only in third schema' ,
484+ ROUTE_WITH_PATH_PARAMS_ONLY_IN_THIRD_SCHEMA ,
485+ {
486+ info : {
487+ title : 'Test' ,
488+ version : '1.0.0' ,
489+ } ,
490+ openapi : '3.0.3' ,
491+ paths : {
492+ '/api/{userId}/posts/{postId}' : {
493+ get : {
494+ parameters : [
495+ {
496+ in : 'query' ,
497+ name : 'union' ,
498+ required : true ,
499+ explode : true ,
500+ style : 'form' ,
501+ schema : {
502+ oneOf : [
503+ {
504+ properties : { filter : { type : 'string' } } ,
505+ required : [ 'filter' ] ,
506+ type : 'object' ,
507+ } ,
508+ {
509+ properties : { details : { type : 'boolean' } } ,
510+ required : [ 'details' ] ,
511+ type : 'object' ,
512+ } ,
513+ ] ,
514+ } ,
515+ } ,
516+ { in : 'path' , name : 'userId' , required : true , schema : { type : 'string' } } ,
517+ { in : 'path' , name : 'postId' , required : true , schema : { type : 'string' } } ,
518+ ] ,
519+ responses : {
520+ '200' : {
521+ description : 'OK' ,
522+ content : {
523+ 'application/json' : {
524+ schema : {
525+ type : 'string' ,
526+ } ,
527+ } ,
528+ } ,
529+ } ,
530+ } ,
531+ } ,
532+ } ,
533+ } ,
534+ components : {
535+ schemas : { } ,
536+ } ,
537+ } ,
538+ ) ;
539+
540+ const ROUTE_WITH_FULLY_DEFINED_PARAMS = `
541+ import * as t from 'io-ts';
542+ import * as h from '@api-ts/io-ts-http';
543+
544+ const AddressBookConnectionSides = t.union([t.literal('send'), t.literal('receive')]);
545+
546+ /**
547+ * Create policy evaluation definition
548+ * @operationId v1.post.policy.evaluation.definition
549+ * @tag Policy Builder
550+ * @private
551+ */
552+ export const route = h.httpRoute({
553+ path: '/internal/api/policy/v1/{applicationName}/touchpoints/{touchpoint}/rules/evaluations',
554+ method: 'POST',
555+ request: t.union([
556+ h.httpRequest({
557+ params: {
558+ applicationName: t.string,
559+ touchpoint: t.string,
560+ },
561+ body: t.type({
562+ approvalRequestId: t.string,
563+ counterPartyId: t.string,
564+ description: h.optional(t.string),
565+ enterpriseId: t.string,
566+ grossAmount: h.optional(t.number),
567+ idempotencyKey: t.string,
568+ isFirstTimeCounterParty: t.boolean,
569+ isMutualConnection: t.boolean,
570+ netAmount: h.optional(t.number),
571+ settlementId: t.string,
572+ userId: t.string,
573+ walletId: t.string,
574+ })
575+ }),
576+ h.httpRequest({
577+ params: {
578+ applicationName: t.string,
579+ touchpoint: t.string,
580+ },
581+ body: t.type({
582+ connectionId: t.string,
583+ description: h.optional(t.string),
584+ enterpriseId: t.string,
585+ idempotencyKey: t.string,
586+ side: AddressBookConnectionSides,
587+ walletId: t.string,
588+ })
589+ }),
590+ ]),
591+ response: {
592+ 200: t.string,
593+ },
594+ });
595+ ` ;
596+
597+ testCase (
598+ 'union request with consistently defined path parameters' ,
599+ ROUTE_WITH_FULLY_DEFINED_PARAMS ,
600+ {
601+ info : {
602+ title : 'Test' ,
603+ version : '1.0.0' ,
604+ } ,
605+ openapi : '3.0.3' ,
606+ paths : {
607+ '/internal/api/policy/v1/{applicationName}/touchpoints/{touchpoint}/rules/evaluations' :
608+ {
609+ post : {
610+ summary : 'Create policy evaluation definition' ,
611+ operationId : 'v1.post.policy.evaluation.definition' ,
612+ tags : [ 'Policy Builder' ] ,
613+ 'x-internal' : true ,
614+ parameters : [
615+ {
616+ in : 'path' ,
617+ name : 'applicationName' ,
618+ required : true ,
619+ schema : { type : 'string' } ,
620+ } ,
621+ {
622+ in : 'path' ,
623+ name : 'touchpoint' ,
624+ required : true ,
625+ schema : { type : 'string' } ,
626+ } ,
627+ ] ,
628+ requestBody : {
629+ content : {
630+ 'application/json' : {
631+ schema : {
632+ oneOf : [
633+ {
634+ type : 'object' ,
635+ properties : {
636+ approvalRequestId : { type : 'string' } ,
637+ counterPartyId : { type : 'string' } ,
638+ description : { type : 'string' } ,
639+ enterpriseId : { type : 'string' } ,
640+ grossAmount : { type : 'number' } ,
641+ idempotencyKey : { type : 'string' } ,
642+ isFirstTimeCounterParty : { type : 'boolean' } ,
643+ isMutualConnection : { type : 'boolean' } ,
644+ netAmount : { type : 'number' } ,
645+ settlementId : { type : 'string' } ,
646+ userId : { type : 'string' } ,
647+ walletId : { type : 'string' } ,
648+ } ,
649+ required : [
650+ 'approvalRequestId' ,
651+ 'counterPartyId' ,
652+ 'enterpriseId' ,
653+ 'idempotencyKey' ,
654+ 'isFirstTimeCounterParty' ,
655+ 'isMutualConnection' ,
656+ 'settlementId' ,
657+ 'userId' ,
658+ 'walletId' ,
659+ ] ,
660+ } ,
661+ {
662+ type : 'object' ,
663+ properties : {
664+ connectionId : { type : 'string' } ,
665+ description : { type : 'string' } ,
666+ enterpriseId : { type : 'string' } ,
667+ idempotencyKey : { type : 'string' } ,
668+ side : {
669+ $ref : '#/components/schemas/AddressBookConnectionSides' ,
670+ } ,
671+ walletId : { type : 'string' } ,
672+ } ,
673+ required : [
674+ 'connectionId' ,
675+ 'enterpriseId' ,
676+ 'idempotencyKey' ,
677+ 'side' ,
678+ 'walletId' ,
679+ ] ,
680+ } ,
681+ ] ,
682+ } ,
683+ } ,
684+ } ,
685+ } ,
686+ responses : {
687+ '200' : {
688+ description : 'OK' ,
689+ content : {
690+ 'application/json' : {
691+ schema : {
692+ type : 'string' ,
693+ } ,
694+ } ,
695+ } ,
696+ } ,
697+ } ,
698+ } ,
699+ } ,
700+ } ,
701+ components : {
702+ schemas : {
703+ AddressBookConnectionSides : {
704+ enum : [ 'send' , 'receive' ] ,
705+ title : 'AddressBookConnectionSides' ,
706+ type : 'string' ,
707+ } ,
708+ } ,
709+ } ,
710+ } ,
711+ ) ;
712+
356713const ROUTE_WITH_DUPLICATE_HEADERS = `
357714import * as t from 'io-ts';
358715import * as h from '@api-ts/io-ts-http';
0 commit comments