|
1 | 1 | /* eslint-disable max-len */ |
2 | 2 | // Disabling max Length for better visibility of the expectedExtractedTypes |
3 | 3 |
|
4 | | -/* eslint-disable one-var */ |
5 | 4 | /* Disabling as we want the checks to run in order of their declaration as declaring everything as once |
6 | 5 | even though initial declarations fails with test won't do any good */ |
7 | 6 |
|
@@ -764,6 +763,220 @@ describe('convertV2WithTypes', function() { |
764 | 763 | done(); |
765 | 764 | }); |
766 | 765 | }); |
| 766 | + |
| 767 | + it('should extract only first option from composite query parameters, path parameters, request headers, and response headers', function(done) { |
| 768 | + const openApiWithCompositeParams = { |
| 769 | + openapi: '3.0.0', |
| 770 | + info: { title: 'Test API', version: '1.0.0' }, |
| 771 | + paths: { |
| 772 | + '/test/{pathParam}': { |
| 773 | + get: { |
| 774 | + parameters: [ |
| 775 | + // Query parameters with composite schemas |
| 776 | + { |
| 777 | + name: 'status', |
| 778 | + in: 'query', |
| 779 | + schema: { |
| 780 | + oneOf: [ |
| 781 | + { type: 'string', enum: ['active', 'inactive'] }, |
| 782 | + { type: 'integer', minimum: 1, maximum: 10 } |
| 783 | + ] |
| 784 | + } |
| 785 | + }, |
| 786 | + { |
| 787 | + name: 'category', |
| 788 | + in: 'query', |
| 789 | + schema: { |
| 790 | + anyOf: [ |
| 791 | + { type: 'string' }, |
| 792 | + { type: 'number' } |
| 793 | + ] |
| 794 | + } |
| 795 | + }, |
| 796 | + { |
| 797 | + name: 'priority', |
| 798 | + in: 'query', |
| 799 | + schema: { |
| 800 | + allOf: [ |
| 801 | + { type: 'string' }, |
| 802 | + { minLength: 3 } |
| 803 | + ] |
| 804 | + } |
| 805 | + }, |
| 806 | + // Path parameter with composite schema |
| 807 | + { |
| 808 | + name: 'pathParam', |
| 809 | + in: 'path', |
| 810 | + required: true, |
| 811 | + schema: { |
| 812 | + oneOf: [ |
| 813 | + { type: 'string', pattern: '^[a-z]+$' }, |
| 814 | + { type: 'integer', minimum: 100 } |
| 815 | + ] |
| 816 | + } |
| 817 | + }, |
| 818 | + // Header parameters with composite schemas |
| 819 | + { |
| 820 | + name: 'X-Custom-Header', |
| 821 | + in: 'header', |
| 822 | + schema: { |
| 823 | + anyOf: [ |
| 824 | + { type: 'string', format: 'uuid' }, |
| 825 | + { type: 'string', enum: ['default', 'custom'] } |
| 826 | + ] |
| 827 | + } |
| 828 | + }, |
| 829 | + { |
| 830 | + name: 'X-Version', |
| 831 | + in: 'header', |
| 832 | + schema: { |
| 833 | + allOf: [ |
| 834 | + { type: 'string' }, |
| 835 | + { pattern: '^v\\d+\\.\\d+$' } |
| 836 | + ] |
| 837 | + } |
| 838 | + } |
| 839 | + ], |
| 840 | + responses: { |
| 841 | + '200': { |
| 842 | + description: 'Success', |
| 843 | + headers: { |
| 844 | + 'X-Rate-Limit': { |
| 845 | + description: 'Rate limit header with composite schema', |
| 846 | + schema: { |
| 847 | + oneOf: [ |
| 848 | + { type: 'integer', minimum: 1, maximum: 1000 }, |
| 849 | + { type: 'string', enum: ['unlimited', 'blocked'] } |
| 850 | + ] |
| 851 | + } |
| 852 | + }, |
| 853 | + 'X-Response-Type': { |
| 854 | + description: 'Response type header with composite schema', |
| 855 | + schema: { |
| 856 | + anyOf: [ |
| 857 | + { type: 'string', format: 'uri' }, |
| 858 | + { type: 'string', pattern: '^[A-Z_]+$' } |
| 859 | + ] |
| 860 | + } |
| 861 | + }, |
| 862 | + 'X-Content-Version': { |
| 863 | + description: 'Content version header with composite schema', |
| 864 | + schema: { |
| 865 | + allOf: [ |
| 866 | + { type: 'string' }, |
| 867 | + { pattern: '^v\\d+\\.\\d+\\.\\d+$' }, |
| 868 | + { minLength: 5 } |
| 869 | + ] |
| 870 | + } |
| 871 | + } |
| 872 | + }, |
| 873 | + content: { |
| 874 | + 'application/json': { |
| 875 | + schema: { |
| 876 | + anyOf: [ |
| 877 | + { type: 'string' }, |
| 878 | + { type: 'object', properties: { message: { type: 'string' } } } |
| 879 | + ] |
| 880 | + } |
| 881 | + } |
| 882 | + } |
| 883 | + } |
| 884 | + } |
| 885 | + } |
| 886 | + } |
| 887 | + } |
| 888 | + }; |
| 889 | + |
| 890 | + Converter.convertV2WithTypes({ type: 'json', data: openApiWithCompositeParams }, {}, (err, conversionResult) => { |
| 891 | + expect(err).to.be.null; |
| 892 | + expect(conversionResult.extractedTypes).to.be.an('object').that.is.not.empty; |
| 893 | + |
| 894 | + const extractedTypes = conversionResult.extractedTypes['get/test/{pathParam}']; |
| 895 | + |
| 896 | + // Verify query parameters extract only first option |
| 897 | + const queryParams = JSON.parse(extractedTypes.request.queryParam); |
| 898 | + expect(queryParams).to.be.an('array').with.length(3); |
| 899 | + |
| 900 | + // Check oneOf query parameter - should extract first option (string with enum) |
| 901 | + expect(queryParams[0]).to.have.property('keyName', 'status'); |
| 902 | + expect(queryParams[0].properties).to.have.property('type', 'string'); |
| 903 | + expect(queryParams[0].properties).to.have.property('enum'); |
| 904 | + expect(queryParams[0].properties.enum).to.deep.equal(['active', 'inactive']); |
| 905 | + expect(queryParams[0].properties).to.not.have.property('oneOf'); |
| 906 | + |
| 907 | + // Check anyOf query parameter - should extract first option (string) |
| 908 | + expect(queryParams[1]).to.have.property('keyName', 'category'); |
| 909 | + expect(queryParams[1].properties).to.have.property('type', 'string'); |
| 910 | + expect(queryParams[1].properties).to.not.have.property('anyOf'); |
| 911 | + |
| 912 | + // Check allOf query parameter - should merge constraints (string with minLength) |
| 913 | + expect(queryParams[2]).to.have.property('keyName', 'priority'); |
| 914 | + expect(queryParams[2].properties).to.have.property('type', 'string'); |
| 915 | + expect(queryParams[2].properties).to.have.property('minLength', 3); |
| 916 | + expect(queryParams[2].properties).to.not.have.property('allOf'); |
| 917 | + |
| 918 | + // Verify path parameters extract only first option |
| 919 | + const pathParams = JSON.parse(extractedTypes.request.pathParam); |
| 920 | + expect(pathParams).to.be.an('array').with.length(1); |
| 921 | + |
| 922 | + // Check oneOf path parameter - should extract first option (string with pattern) |
| 923 | + expect(pathParams[0]).to.have.property('keyName', 'pathParam'); |
| 924 | + expect(pathParams[0].properties).to.have.property('type', 'string'); |
| 925 | + expect(pathParams[0].properties).to.have.property('pattern', '^[a-z]+$'); |
| 926 | + expect(pathParams[0].properties).to.not.have.property('oneOf'); |
| 927 | + |
| 928 | + // Verify headers extract only first option |
| 929 | + const headers = JSON.parse(extractedTypes.request.headers); |
| 930 | + expect(headers).to.be.an('array').with.length(2); |
| 931 | + |
| 932 | + // Check anyOf header - should extract first option (string with format) |
| 933 | + expect(headers[0]).to.have.property('keyName', 'X-Custom-Header'); |
| 934 | + expect(headers[0].properties).to.have.property('type', 'string'); |
| 935 | + expect(headers[0].properties).to.have.property('format', 'uuid'); |
| 936 | + expect(headers[0].properties).to.not.have.property('anyOf'); |
| 937 | + |
| 938 | + // Check allOf header - should merge constraints (string with pattern) |
| 939 | + expect(headers[1]).to.have.property('keyName', 'X-Version'); |
| 940 | + expect(headers[1].properties).to.have.property('type', 'string'); |
| 941 | + expect(headers[1].properties).to.have.property('pattern', '^v\\d+\\.\\d+$'); |
| 942 | + expect(headers[1].properties).to.not.have.property('allOf'); |
| 943 | + |
| 944 | + // Verify response body preserves full composite schema |
| 945 | + const responseBody = conversionResult.extractedTypes['get/test/{pathParam}'].response['200'].body; |
| 946 | + const parsedResponseBody = JSON.parse(responseBody); |
| 947 | + |
| 948 | + expect(parsedResponseBody).to.have.property('anyOf'); |
| 949 | + expect(parsedResponseBody.anyOf).to.be.an('array').with.length(2); |
| 950 | + expect(parsedResponseBody.anyOf[0]).to.have.property('type', 'string'); |
| 951 | + expect(parsedResponseBody.anyOf[1]).to.have.property('type', 'object'); |
| 952 | + |
| 953 | + // Verify response headers extract only first option |
| 954 | + const responseHeaders = JSON.parse(extractedTypes.response['200'].headers); |
| 955 | + expect(responseHeaders).to.be.an('array').with.length(3); |
| 956 | + |
| 957 | + // Check oneOf response header - should extract first option (integer with constraints) |
| 958 | + expect(responseHeaders[0]).to.have.property('keyName', 'X-Rate-Limit'); |
| 959 | + expect(responseHeaders[0].properties).to.have.property('type', 'integer'); |
| 960 | + expect(responseHeaders[0].properties).to.have.property('minimum', 1); |
| 961 | + expect(responseHeaders[0].properties).to.have.property('maximum', 1000); |
| 962 | + expect(responseHeaders[0].properties).to.not.have.property('oneOf'); |
| 963 | + |
| 964 | + // Check anyOf response header - should extract first option (string with format) |
| 965 | + expect(responseHeaders[1]).to.have.property('keyName', 'X-Response-Type'); |
| 966 | + expect(responseHeaders[1].properties).to.have.property('type', 'string'); |
| 967 | + expect(responseHeaders[1].properties).to.have.property('format', 'uri'); |
| 968 | + expect(responseHeaders[1].properties).to.not.have.property('anyOf'); |
| 969 | + |
| 970 | + // Check allOf response header - should merge constraints (string with pattern and minLength) |
| 971 | + expect(responseHeaders[2]).to.have.property('keyName', 'X-Content-Version'); |
| 972 | + expect(responseHeaders[2].properties).to.have.property('type', 'string'); |
| 973 | + expect(responseHeaders[2].properties).to.have.property('pattern', '^v\\d+\\.\\d+\\.\\d+$'); |
| 974 | + expect(responseHeaders[2].properties).to.have.property('minLength', 5); |
| 975 | + expect(responseHeaders[2].properties).to.not.have.property('allOf'); |
| 976 | + |
| 977 | + done(); |
| 978 | + }); |
| 979 | + }); |
767 | 980 | }); |
768 | 981 |
|
769 | 982 | }); |
0 commit comments