[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
This commit is contained in:
William Cheng
2023-03-10 15:18:41 +08:00
committed by GitHub
parent c4b404dc2e
commit 140d941da2
11 changed files with 281 additions and 10 deletions

View File

@@ -105,6 +105,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>long</li>
<li>long?</li>
<li>string</li>
<li>uint</li>
<li>uint?</li>
<li>ulong</li>
<li>ulong?</li>
</ul>
## RESERVED WORDS

View File

@@ -98,6 +98,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>long</li>
<li>long?</li>
<li>string</li>
<li>uint</li>
<li>uint?</li>
<li>ulong</li>
<li>ulong?</li>
</ul>
## RESERVED WORDS

View File

@@ -99,6 +99,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>long</li>
<li>long?</li>
<li>string</li>
<li>uint</li>
<li>uint?</li>
<li>ulong</li>
<li>ulong?</li>
</ul>
## RESERVED WORDS

View File

@@ -93,6 +93,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>long</li>
<li>long?</li>
<li>string</li>
<li>uint</li>
<li>uint?</li>
<li>ulong</li>
<li>ulong?</li>
</ul>
## RESERVED WORDS

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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_");

View File

@@ -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_");

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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'