From e0afcf26372b940de4b4dfe225dbbfcb1c3bcae7 Mon Sep 17 00:00:00 2001 From: Andrey Zaytsev Date: Sun, 23 Oct 2022 18:32:59 +0400 Subject: [PATCH 1/3] Update tests to verify the bug --- .../__snapshots__/index.spec.ts.snap | 9 ++++++--- .../index.ts | 20 ++++++++++++++++--- .../__snapshots__/index.spec.ts.snap | 8 ++++---- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap b/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap index 0ffc62e3a2..86109e7473 100644 --- a/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-auto-implemented-properties/__snapshots__/index.spec.ts.snap @@ -4,9 +4,12 @@ exports[`Should be able to render auto-implemented properties in CSharp and shou Array [ "public class Root { - public dynamic[]? Emails { get; set; } - public string? StringProp { get; set; } - public double? NumberProp { get; set; } + public dynamic[]? NullArray { get; set; } + public string? NullString { get; set; } + public double? NullNumber { get; set; } + public dynamic[] NonNullArray { get; set; } + public string NonNullString { get; set; } + public double NonNullNumber { get; set; } }", ] `; diff --git a/examples/csharp-auto-implemented-properties/index.ts b/examples/csharp-auto-implemented-properties/index.ts index 3ef98ef54c..46269bc865 100644 --- a/examples/csharp-auto-implemented-properties/index.ts +++ b/examples/csharp-auto-implemented-properties/index.ts @@ -14,20 +14,34 @@ const jsonSchemaDraft7 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', additionalProperties: false, + required: ['nonNullNumber', 'nonNullString', 'nonNullArray'], properties: { - emails: { + nullArray: { type: 'array', items: { type: 'string', format: 'email' } }, - stringProp: { + nullString: { type: 'string' }, - numberProp: { + nullNumber: { type: 'number' }, + nonNullArray: { + type: 'array', + items: { + type: 'string', + format: 'email' + } + }, + nonNullString: { + type: 'string' + }, + nonNullNumber: { + type: 'number' + } } }; diff --git a/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap index 28eb56e326..f94d17bb7f 100644 --- a/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap +++ b/examples/csharp-generate-required-properties/__snapshots__/index.spec.ts.snap @@ -5,9 +5,9 @@ Array [ "public class Root { private bool requiredBoolean; - private bool notRequiredBoolean; + private bool? notRequiredBoolean; private string requiredString; - private string notRequiredString; + private string? notRequiredString; public bool RequiredBoolean { @@ -15,7 +15,7 @@ Array [ set { requiredBoolean = value; } } - public bool NotRequiredBoolean + public bool? NotRequiredBoolean { get { return notRequiredBoolean; } set { notRequiredBoolean = value; } @@ -27,7 +27,7 @@ Array [ set { requiredString = value; } } - public string NotRequiredString + public string? NotRequiredString { get { return notRequiredString; } set { notRequiredString = value; } From 02bcd6c8cd97bc8c99718c4460546dad32d1f983 Mon Sep 17 00:00:00 2001 From: Andrey Zaytsev Date: Mon, 24 Oct 2022 11:56:34 +0400 Subject: [PATCH 2/3] Moving propertyType into a separate preset function. Could be expanded later to support pre-CSharp8 nullable style (`Nullable`) --- src/generators/csharp/CSharpPreset.ts | 1 + .../csharp/renderers/ClassRenderer.ts | 24 ++++++++-- .../CSharpGenerator.spec.ts.snap | 48 +++++++++---------- .../__snapshots__/CommonPreset.spec.ts.snap | 40 ++++++++-------- .../JsonSerializerPreset.spec.ts.snap | 24 +++++----- 5 files changed, 76 insertions(+), 61 deletions(-) diff --git a/src/generators/csharp/CSharpPreset.ts b/src/generators/csharp/CSharpPreset.ts index 9904008921..a8f53204d6 100644 --- a/src/generators/csharp/CSharpPreset.ts +++ b/src/generators/csharp/CSharpPreset.ts @@ -6,6 +6,7 @@ import { CSHARP_DEFAULT_ENUM_PRESET, EnumRenderer } from './renderers/EnumRender // Our class preset uses custom `accessor` hook to craft getter and setters. export interface CsharpClassPreset extends ClassPreset { accessor?: (args: PresetArgs & PropertyArgs) => Promise | string; + propertyType?: (args: PresetArgs & PropertyArgs) => Promise | string; } export type ClassPresetType = CsharpClassPreset; diff --git a/src/generators/csharp/renderers/ClassRenderer.ts b/src/generators/csharp/renderers/ClassRenderer.ts index 60a23315ef..671c16e4cc 100644 --- a/src/generators/csharp/renderers/ClassRenderer.ts +++ b/src/generators/csharp/renderers/ClassRenderer.ts @@ -1,5 +1,5 @@ import { CSharpRenderer } from '../CSharpRenderer'; -import { ConstrainedDictionaryModel, ConstrainedObjectModel, ConstrainedObjectPropertyModel} from '../../../models'; +import { ConstrainedDictionaryModel, ConstrainedObjectModel, ConstrainedObjectPropertyModel } from '../../../models'; import { pascalCase } from 'change-case'; import { CsharpClassPreset } from '../CSharpPreset'; import { CSharpOptions } from '../CSharpGenerator'; @@ -71,6 +71,10 @@ ${this.indent(this.renderBlock(content, 2))} runSetterPreset(property: ConstrainedObjectPropertyModel): Promise { return this.runPreset('setter', { property, options: this.options, renderer: this }); } + + runPropertyTypePreset(property: ConstrainedObjectPropertyModel): Promise { + return this.runPreset('propertyType', { property, options: this.options, renderer: this }); + } } export const CSHARP_DEFAULT_CLASS_PRESET: CsharpClassPreset = { @@ -78,20 +82,25 @@ export const CSHARP_DEFAULT_CLASS_PRESET: CsharpClassPreset = { return renderer.defaultSelf(); }, async property({ renderer, property, options }) { + const type = await renderer.runPropertyTypePreset(property); + if (options?.autoImplementedProperties) { const getter = await renderer.runGetterPreset(property); const setter = await renderer.runSetterPreset(property); - return `public ${property.property.type}${property.required === false && '?'} ${pascalCase(property.propertyName)} { ${getter} ${setter} }`; + return `public ${type} ${pascalCase(property.propertyName)} { ${getter} ${setter} }`; } - return `private ${property.property.type} ${property.propertyName};`; + return `private ${type} ${property.propertyName};`; }, async accessor({ renderer, options, property }) { - const formattedAccessorName = pascalCase(property.propertyName); + const type = await renderer.runPropertyTypePreset(property); + if (options?.autoImplementedProperties) { return ''; } - return `public ${property.property.type} ${formattedAccessorName} + const formattedAccessorName = pascalCase(property.propertyName); + + return `public ${type} ${formattedAccessorName} { ${await renderer.runGetterPreset(property)} ${await renderer.runSetterPreset(property)} @@ -108,5 +117,10 @@ export const CSHARP_DEFAULT_CLASS_PRESET: CsharpClassPreset = { return 'set;'; } return `set { ${property.propertyName} = value; }`; + }, + propertyType({ property }) { + const isNullable = property.required === false; + + return `${property.property.type}${isNullable ? '?' : ''}`; } }; diff --git a/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap b/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap index 75ff1b3e1a..f98aca036d 100644 --- a/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap +++ b/test/generators/csharp/__snapshots__/CSharpGenerator.spec.ts.snap @@ -3,8 +3,8 @@ exports[`CSharpGenerator class renderer should be able to overwrite accessor preset hook 1`] = ` "public class CustomClass { - private string property; - private Dictionary additionalProperties; + private string? property; + private Dictionary? additionalProperties; my own custom factory @@ -18,13 +18,13 @@ exports[`CSharpGenerator class renderer should be able to overwrite property pre my own property my own property - public string Property + public string? Property { get { return property; } set { property = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -39,11 +39,11 @@ exports[`CSharpGenerator should render \`class\` type 1`] = ` private string city; private string state; private double houseNumber; - private bool marriage; - private dynamic members; - private dynamic[] tupleType; + private bool? marriage; + private dynamic? members; + private dynamic[]? tupleType; private dynamic[] arrayType; - private Dictionary additionalProperties; + private Dictionary? additionalProperties; public string StreetName { @@ -69,19 +69,19 @@ exports[`CSharpGenerator should render \`class\` type 1`] = ` set { houseNumber = value; } } - public bool Marriage + public bool? Marriage { get { return marriage; } set { marriage = value; } } - public dynamic Members + public dynamic? Members { get { return members; } set { members = value; } } - public dynamic[] TupleType + public dynamic[]? TupleType { get { return tupleType; } set { tupleType = value; } @@ -93,7 +93,7 @@ exports[`CSharpGenerator should render \`class\` type 1`] = ` set { arrayType = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -194,11 +194,11 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` private string city; private string state; private double houseNumber; - private bool marriage; - private dynamic members; + private bool? marriage; + private dynamic? members; private dynamic[] arrayType; - private OtherModel otherModel; - private Dictionary additionalProperties; + private OtherModel? otherModel; + private Dictionary? additionalProperties; public string StreetName { @@ -224,13 +224,13 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` set { houseNumber = value; } } - public bool Marriage + public bool? Marriage { get { return marriage; } set { marriage = value; } } - public dynamic Members + public dynamic? Members { get { return members; } set { members = value; } @@ -242,13 +242,13 @@ exports[`CSharpGenerator should render models and their dependencies 1`] = ` set { arrayType = value; } } - public OtherModel OtherModel + public OtherModel? OtherModel { get { return otherModel; } set { otherModel = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -264,16 +264,16 @@ exports[`CSharpGenerator should render models and their dependencies 2`] = ` public class OtherModel { - private string streetName; - private Dictionary additionalProperties; + private string? streetName; + private Dictionary? additionalProperties; - public string StreetName + public string? StreetName { get { return streetName; } set { streetName = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } diff --git a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap index 8f892f52b3..dd35db1ca1 100644 --- a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap +++ b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap @@ -4,9 +4,9 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` "public class Test { private string stringProp; - private double numberProp; - private NestedTest objectProp; - private Dictionary additionalProperties; + private double? numberProp; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -14,19 +14,19 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` set { stringProp = value; } } - public double NumberProp + public double? NumberProp { get { return numberProp; } set { numberProp = value; } } - public NestedTest ObjectProp + public NestedTest? ObjectProp { get { return objectProp; } set { objectProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -61,16 +61,16 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = ` exports[`CSHARP_COMMON_PRESET should render Equals support function 2`] = ` "public class NestedTest { - private string stringProp; - private Dictionary additionalProperties; + private string? stringProp; + private Dictionary? additionalProperties; - public string StringProp + public string? StringProp { get { return stringProp; } set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -102,9 +102,9 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` "public class Test { private string stringProp; - private double numberProp; - private NestedTest objectProp; - private Dictionary additionalProperties; + private double? numberProp; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -112,19 +112,19 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` set { stringProp = value; } } - public double NumberProp + public double? NumberProp { get { return numberProp; } set { numberProp = value; } } - public NestedTest ObjectProp + public NestedTest? ObjectProp { get { return objectProp; } set { objectProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -145,16 +145,16 @@ exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 1`] = ` exports[`CSHARP_COMMON_PRESET should render GetHashCode support function 2`] = ` "public class NestedTest { - private string stringProp; - private Dictionary additionalProperties; + private string? stringProp; + private Dictionary? additionalProperties; - public string StringProp + public string? StringProp { get { return stringProp; } set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } diff --git a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap index 59feeffb2c..35c28952d9 100644 --- a/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap +++ b/test/generators/csharp/presets/__snapshots__/JsonSerializerPreset.spec.ts.snap @@ -5,10 +5,10 @@ exports[`JSON serializer preset should render serialize and deserialize converte public class Test { private string stringProp; - private double numberProp; - private EnumTest enumProp; - private NestedTest objectProp; - private Dictionary additionalProperties; + private double? numberProp; + private EnumTest? enumProp; + private NestedTest? objectProp; + private Dictionary? additionalProperties; public string StringProp { @@ -16,25 +16,25 @@ public class Test set { stringProp = value; } } - public double NumberProp + public double? NumberProp { get { return numberProp; } set { numberProp = value; } } - public EnumTest EnumProp + public EnumTest? EnumProp { get { return enumProp; } set { enumProp = value; } } - public NestedTest ObjectProp + public NestedTest? ObjectProp { get { return objectProp; } set { objectProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } @@ -203,16 +203,16 @@ exports[`JSON serializer preset should render serialize and deserialize converte "[JsonConverter(typeof(NestedTestConverter))] public class NestedTest { - private string stringProp; - private Dictionary additionalProperties; + private string? stringProp; + private Dictionary? additionalProperties; - public string StringProp + public string? StringProp { get { return stringProp; } set { stringProp = value; } } - public Dictionary AdditionalProperties + public Dictionary? AdditionalProperties { get { return additionalProperties; } set { additionalProperties = value; } From 8ebaaecdc6f6bf9a8260f8dcbc652cb219595e79 Mon Sep 17 00:00:00 2001 From: Andrey Zaytsev Date: Mon, 24 Oct 2022 15:58:08 +0400 Subject: [PATCH 3/3] CSharp documentation on preset updated --- docs/presets.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/presets.md b/docs/presets.md index d32d6ca108..6012f54704 100644 --- a/docs/presets.md +++ b/docs/presets.md @@ -418,6 +418,7 @@ This preset is a generator for the meta model `ConstrainedObjectModel` and [can | `accessor` | A method to extend rendered given property accessor. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | | `setter` | A method to extend setter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | | `getter` | A method to extend getter for a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | +| `propertyType` | A method to extend type definition of a given property. | `property` object as a [`ConstrainedObjectPropertyModel`](./internal-model.md#the-constrained-meta-model) instance. | #### **Enum**