From 140d941da2e48268624cd83dde95492f9294d5a1 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Fri, 10 Mar 2023 15:18:41 +0800 Subject: [PATCH] [csharp-netcore] Add unsigned integer/long support (#14885) * add unsigned integer/long support to c# netcore client * undo change in test spec, samples * new test spec * update doc --- docs/generators/aspnetcore.md | 4 + docs/generators/csharp-netcore-functions.md | 4 + docs/generators/csharp-netcore.md | 4 + docs/generators/csharp.md | 4 + .../openapitools/codegen/DefaultCodegen.java | 12 +- .../languages/AbstractCSharpCodegen.java | 17 +- .../languages/CSharpClientCodegen.java | 7 +- .../languages/CSharpNetCoreClientCodegen.java | 7 +- .../codegen/utils/ModelUtils.java | 18 ++ .../CSharpNetCoreClientCodegenTest.java | 38 ++++ .../src/test/resources/3_0/unsigned-test.yaml | 176 ++++++++++++++++++ 11 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/unsigned-test.yaml diff --git a/docs/generators/aspnetcore.md b/docs/generators/aspnetcore.md index ed22b58027..a5792b6757 100644 --- a/docs/generators/aspnetcore.md +++ b/docs/generators/aspnetcore.md @@ -105,6 +105,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • long
  • long?
  • string
  • +
  • uint
  • +
  • uint?
  • +
  • ulong
  • +
  • ulong?
  • ## RESERVED WORDS diff --git a/docs/generators/csharp-netcore-functions.md b/docs/generators/csharp-netcore-functions.md index a5b638c512..49b53ed219 100644 --- a/docs/generators/csharp-netcore-functions.md +++ b/docs/generators/csharp-netcore-functions.md @@ -98,6 +98,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • long
  • long?
  • string
  • +
  • uint
  • +
  • uint?
  • +
  • ulong
  • +
  • ulong?
  • ## RESERVED WORDS diff --git a/docs/generators/csharp-netcore.md b/docs/generators/csharp-netcore.md index 803611ed31..4f972b041c 100644 --- a/docs/generators/csharp-netcore.md +++ b/docs/generators/csharp-netcore.md @@ -99,6 +99,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • long
  • long?
  • string
  • +
  • uint
  • +
  • uint?
  • +
  • ulong
  • +
  • ulong?
  • ## RESERVED WORDS diff --git a/docs/generators/csharp.md b/docs/generators/csharp.md index 59b874beb4..2dff47951a 100644 --- a/docs/generators/csharp.md +++ b/docs/generators/csharp.md @@ -93,6 +93,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • long
  • long?
  • string
  • +
  • uint
  • +
  • uint?
  • +
  • ulong
  • +
  • ulong?
  • ## RESERVED WORDS diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index aad7d5946d..42d8075983 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1728,9 +1728,11 @@ public class DefaultCodegen implements CodegenConfig { typeMapping.put("DateTime", "Date"); typeMapping.put("long", "Long"); typeMapping.put("short", "Short"); + typeMapping.put("integer", "Integer"); + typeMapping.put("UnsignedInteger", "Integer"); + typeMapping.put("UnsignedLong", "Long"); typeMapping.put("char", "String"); typeMapping.put("object", "Object"); - typeMapping.put("integer", "Integer"); // FIXME: java specific type should be in Java Based Abstract Impl's typeMapping.put("ByteArray", "byte[]"); typeMapping.put("binary", "File"); @@ -2394,8 +2396,14 @@ public class DefaultCodegen implements CodegenConfig { return "number"; } } else if (ModelUtils.isIntegerSchema(schema)) { - if (ModelUtils.isLongSchema(schema)) { + if (ModelUtils.isUnsignedLongSchema(schema)) { + return "UnsignedLong"; + } else if (ModelUtils.isUnsignedIntegerSchema(schema)) { + return "UnsignedInteger"; + } else if (ModelUtils.isLongSchema(schema)) { return "long"; + } else if (ModelUtils.isShortSchema(schema)) {// int32 + return "integer"; } else { return schema.getType(); // integer } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java index 33a13db335..badc5e62e8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java @@ -160,8 +160,12 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co "decimal", "int?", "int", + "uint", + "uint?", "long?", "long", + "ulong", + "ulong?", "float?", "float", "byte[]", @@ -197,8 +201,10 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co typeMapping.put("ByteArray", "byte[]"); typeMapping.put("boolean", "bool?"); typeMapping.put("integer", "int?"); - typeMapping.put("float", "float?"); + typeMapping.put("UnsignedInteger", "uint?"); + typeMapping.put("UnsignedLong", "ulong?"); typeMapping.put("long", "long?"); + typeMapping.put("float", "float?"); typeMapping.put("double", "double?"); typeMapping.put("number", "decimal?"); typeMapping.put("BigDecimal", "decimal?"); @@ -215,11 +221,12 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co // nullable type nullableType = new HashSet<>( - Arrays.asList("decimal", "bool", "int", "float", "long", "double", "DateTime", "DateTimeOffset", "Guid") + Arrays.asList("decimal", "bool", "int", "uint", "long", "ulong", "float", "double", + "DateTime", "DateTimeOffset", "Guid") ); // value Types valueTypes = new HashSet<>( - Arrays.asList("decimal", "bool", "int", "float", "long", "double") + Arrays.asList("decimal", "bool", "int", "uint", "long", "ulong", "float", "double") ); this.setSortParamsByRequiredFlag(true); @@ -1334,7 +1341,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co // Per: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum // The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong. // but we're not supporting unsigned integral types or shorts. - if (datatype.startsWith("int") || datatype.startsWith("long") || datatype.startsWith("byte")) { + if (datatype.startsWith("int") || datatype.startsWith("uint") || + datatype.startsWith("long") || datatype.startsWith("ulong") || + datatype.startsWith("byte")) { return value; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java index 153d9b4b47..4960f7b921 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java @@ -123,8 +123,10 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { typeMapping.put("boolean", "bool"); typeMapping.put("integer", "int"); - typeMapping.put("float", "float"); typeMapping.put("long", "long"); + typeMapping.put("UnsignedInteger", "uint"); + typeMapping.put("UnsignedLong", "ulong"); + typeMapping.put("float", "float"); typeMapping.put("double", "double"); typeMapping.put("number", "decimal"); typeMapping.put("decimal", "decimal"); @@ -789,7 +791,8 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { } // number - if (datatype.startsWith("int") || datatype.startsWith("long") || + if (datatype.startsWith("int") || datatype.startsWith("uint") || + datatype.startsWith("ulong") || datatype.startsWith("long") || datatype.startsWith("double") || datatype.startsWith("float")) { String varName = "NUMBER_" + value; varName = varName.replaceAll("-", "MINUS_"); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index ae7def89f9..757bcae3f1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -153,8 +153,10 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { typeMapping.put("ByteArray", "byte[]"); typeMapping.put("boolean", "bool"); typeMapping.put("integer", "int"); - typeMapping.put("float", "float"); typeMapping.put("long", "long"); + typeMapping.put("UnsignedInteger", "uint"); + typeMapping.put("UnsignedLong", "ulong"); + typeMapping.put("float", "float"); typeMapping.put("double", "double"); typeMapping.put("number", "decimal"); typeMapping.put("decimal", "decimal"); @@ -1176,7 +1178,8 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { } // number - if (datatype.startsWith("int") || datatype.startsWith("long") || + if (datatype.startsWith("int") || datatype.startsWith("uint") || + datatype.startsWith("long") || datatype.startsWith("ulong") || datatype.startsWith("double") || datatype.startsWith("float")) { String varName = "NUMBER_" + value; varName = varName.replaceAll("-", "MINUS_"); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index a392325eb8..974cd2228a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -606,6 +606,15 @@ public class ModelUtils { return false; } + public static boolean isUnsignedIntegerSchema(Schema schema) { + if (SchemaTypeUtil.INTEGER_TYPE.equals(schema.getType()) && // type: integer + ("int32".equals(schema.getFormat()) || schema.getFormat() == null) && // format: int32 + (schema.getExtensions() != null && (Boolean) schema.getExtensions().getOrDefault("x-unsigned", Boolean.FALSE))) { // x-unsigned: true + return true; + } + return false; + } + public static boolean isLongSchema(Schema schema) { if (SchemaTypeUtil.INTEGER_TYPE.equals(schema.getType()) // type: integer && SchemaTypeUtil.INTEGER64_FORMAT.equals(schema.getFormat())) { // format: long (int64) @@ -614,6 +623,15 @@ public class ModelUtils { return false; } + public static boolean isUnsignedLongSchema(Schema schema) { + if (SchemaTypeUtil.INTEGER_TYPE.equals(schema.getType()) && // type: integer + "int64".equals(schema.getFormat()) && // format: int64 + (schema.getExtensions() != null && (Boolean) schema.getExtensions().getOrDefault("x-unsigned", Boolean.FALSE))) { // x-unsigned: true + return true; + } + return false; + } + public static boolean isBooleanSchema(Schema schema) { if (schema instanceof BooleanSchema) { return true; diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpNetCoreClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpNetCoreClientCodegenTest.java index 3a6ce1df37..2f865064b7 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpNetCoreClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpNetCoreClientCodegenTest.java @@ -16,7 +16,13 @@ package org.openapitools.codegen.csharpnetcore; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenProperty; +import org.openapitools.codegen.TestUtils; import org.openapitools.codegen.languages.CSharpNetCoreClientCodegen; +import org.openapitools.codegen.languages.JavaClientCodegen; import org.testng.Assert; import org.testng.annotations.Test; @@ -37,4 +43,36 @@ public class CSharpNetCoreClientCodegenTest { // Assert.assertEquals(codegen.toEnumVarName("FOO-BAR", "string"), "FooBar"); // Assert.assertEquals(codegen.toEnumVarName("FOO_BAR", "string"), "FooBar"); } + + @Test + public void testUnsigned() { + // test unsigned integer/long + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/unsigned-test.yaml"); + CSharpNetCoreClientCodegen codegen = new CSharpNetCoreClientCodegen(); + + Schema test1 = openAPI.getComponents().getSchemas().get("format_test"); + codegen.setOpenAPI(openAPI); + CodegenModel cm1 = codegen.fromModel("format_test", test1); + Assert.assertEquals(cm1.getClassname(), "FormatTest"); + + final CodegenProperty property1 = cm1.allVars.get(2); + Assert.assertEquals(property1.baseName, "unsigned_integer"); + Assert.assertEquals(property1.dataType, "uint"); + Assert.assertEquals(property1.vendorExtensions.get("x-unsigned"), Boolean.TRUE); + Assert.assertTrue(property1.isPrimitiveType); + Assert.assertTrue(property1.isInteger); + Assert.assertFalse(property1.isContainer); + Assert.assertFalse(property1.isFreeFormObject); + Assert.assertFalse(property1.isAnyType); + + final CodegenProperty property2 = cm1.allVars.get(4); + Assert.assertEquals(property2.baseName, "unsigned_long"); + Assert.assertEquals(property2.dataType, "ulong"); + Assert.assertEquals(property2.vendorExtensions.get("x-unsigned"), Boolean.TRUE); + Assert.assertTrue(property2.isPrimitiveType); + Assert.assertTrue(property2.isLong); + Assert.assertFalse(property2.isContainer); + Assert.assertFalse(property2.isFreeFormObject); + Assert.assertFalse(property2.isAnyType); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/unsigned-test.yaml b/modules/openapi-generator/src/test/resources/3_0/unsigned-test.yaml new file mode 100644 index 0000000000..2c7073528d --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/unsigned-test.yaml @@ -0,0 +1,176 @@ +openapi: 3.0.0 +info: + description: >- + This spec is mainly for testing Petstore server and contains fake endpoints, + models. Please do not use this for any other purpose. Special characters: " + \ + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /foo: + get: + responses: + default: + description: response + content: + application/json: + schema: + type: object + properties: + string: + $ref: '#/components/schemas/Foo' +servers: + - url: 'http://{server}.swagger.io:{port}/v2' + description: petstore server + variables: + server: + enum: + - 'petstore' + - 'qa-petstore' + - 'dev-petstore' + default: 'petstore' + port: + enum: + - 80 + - 8080 + default: 80 + - url: https://localhost:8080/{version} + description: The local server + variables: + version: + enum: + - 'v1' + - 'v2' + default: 'v2' + - url: https://127.0.0.1/no_variable + description: The local server without variables +components: + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header + api_key_query: + type: apiKey + name: api_key_query + in: query + http_basic_test: + type: http + scheme: basic + bearer_test: + type: http + scheme: bearer + bearerFormat: JWT + http_signature_test: + # Test the 'HTTP signature' security scheme. + # Each HTTP request is cryptographically signed as specified + # in https://datatracker.ietf.org/doc/draft-cavage-http-signatures/ + type: http + scheme: signature + schemas: + Foo: + type: object + properties: + bar: + $ref: '#/components/schemas/Bar' + format_test: + type: object + required: + - number + - byte + - date + - password + properties: + integer: + type: integer + maximum: 100 + minimum: 10 + multipleOf: 2 + int32: + type: integer + format: int32 + maximum: 200 + minimum: 20 + unsigned_integer: + type: integer + format: int32 + maximum: 200 + minimum: 20 + x-unsigned: true + int64: + type: integer + format: int64 + unsigned_long: + type: integer + format: int64 + x-unsigned: true + number: + maximum: 543.2 + minimum: 32.1 + type: number + multipleOf: 32.5 + float: + type: number + format: float + maximum: 987.6 + minimum: 54.3 + double: + type: number + format: double + maximum: 123.4 + minimum: 67.8 + decimal: + type: string + format: number + string: + type: string + pattern: '/[a-z]/i' + byte: + type: string + format: byte + binary: + type: string + format: binary + date: + type: string + format: date + example: '2020-02-02' + dateTime: + type: string + format: date-time + example: '2007-12-03T10:15:30+01:00' + uuid: + type: string + format: uuid + example: 72f98069-206d-4f12-9f12-3d1e525a8e84 + password: + type: string + format: password + maxLength: 64 + minLength: 10 + pattern_with_digits: + description: A string that is a 10 digit number. Can have leading zeros. + type: string + pattern: '^\d{10}$' + pattern_with_digits_and_delimiter: + description: A string starting with 'image_' (case insensitive) and one to three digits following i.e. Image_01. + type: string + pattern: '/^image_\d{1,3}$/i'