avrotize 2.20.3__py3-none-any.whl → 2.20.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- avrotize/__init__.py +2 -0
- avrotize/_version.py +2 -2
- avrotize/avrotocsharp/README.md.jinja +166 -0
- avrotize/avrotocsharp/class_test.cs.jinja +126 -0
- avrotize/avrotocsharp/dataclass_core.jinja +67 -5
- avrotize/avrotocsharp/project.csproj.jinja +6 -0
- avrotize/avrotocsharp/run_coverage.ps1.jinja +98 -0
- avrotize/avrotocsharp/run_coverage.sh.jinja +149 -0
- avrotize/avrotocsharp/testproject.csproj.jinja +4 -0
- avrotize/avrotocsharp.py +121 -16
- avrotize/commands.json +168 -9
- avrotize/constants.py +15 -0
- avrotize/dependencies/cs/net90/dependencies.csproj +2 -0
- avrotize/dependencies/java/jdk21/pom.xml +1 -1
- avrotize/jsonstostructure.py +234 -12
- avrotize/openapitostructure.py +717 -0
- avrotize/structuretojs/class_core.js.jinja +33 -0
- avrotize/structuretojs/enum_core.js.jinja +10 -0
- avrotize/structuretojs/package.json.jinja +12 -0
- avrotize/structuretojs/test_class.js.jinja +84 -0
- avrotize/structuretojs/test_enum.js.jinja +58 -0
- avrotize/structuretojs/test_runner.js.jinja +45 -0
- avrotize/structuretojs.py +657 -0
- avrotize/structuretots.py +26 -2
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/METADATA +66 -1
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/RECORD +29 -18
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/WHEEL +0 -0
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/entry_points.txt +0 -0
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shell script to run tests with code coverage and generate reports
|
|
3
|
+
# Generated for project: {{ project_name }}
|
|
4
|
+
|
|
5
|
+
# Default values
|
|
6
|
+
THRESHOLD=85
|
|
7
|
+
HTML_REPORT=false
|
|
8
|
+
OPEN_REPORT=false
|
|
9
|
+
FAIL_ON_LOW_COVERAGE=true
|
|
10
|
+
|
|
11
|
+
# Parse command line arguments
|
|
12
|
+
while [[ $# -gt 0 ]]; do
|
|
13
|
+
case $1 in
|
|
14
|
+
--threshold)
|
|
15
|
+
THRESHOLD="$2"
|
|
16
|
+
shift 2
|
|
17
|
+
;;
|
|
18
|
+
--html-report)
|
|
19
|
+
HTML_REPORT=true
|
|
20
|
+
shift
|
|
21
|
+
;;
|
|
22
|
+
--open-report)
|
|
23
|
+
OPEN_REPORT=true
|
|
24
|
+
HTML_REPORT=true
|
|
25
|
+
shift
|
|
26
|
+
;;
|
|
27
|
+
--no-fail)
|
|
28
|
+
FAIL_ON_LOW_COVERAGE=false
|
|
29
|
+
shift
|
|
30
|
+
;;
|
|
31
|
+
*)
|
|
32
|
+
echo "Unknown option: $1"
|
|
33
|
+
echo "Usage: $0 [--threshold N] [--html-report] [--open-report] [--no-fail]"
|
|
34
|
+
exit 1
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
echo "🧪 Running tests with code coverage for {{ project_name }}..."
|
|
40
|
+
|
|
41
|
+
# Clean previous coverage results
|
|
42
|
+
COVERAGE_DIR="./coverage"
|
|
43
|
+
if [ -d "$COVERAGE_DIR" ]; then
|
|
44
|
+
rm -rf "$COVERAGE_DIR"
|
|
45
|
+
echo "🧹 Cleaned previous coverage results"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Run tests with coverage collection
|
|
49
|
+
echo "📊 Collecting code coverage..."
|
|
50
|
+
dotnet test --collect:"XPlat Code Coverage" --results-directory:"$COVERAGE_DIR" --logger:"console;verbosity=detailed"
|
|
51
|
+
TEST_EXIT_CODE=$?
|
|
52
|
+
|
|
53
|
+
if [ $TEST_EXIT_CODE -ne 0 ]; then
|
|
54
|
+
echo "❌ Tests failed!"
|
|
55
|
+
exit $TEST_EXIT_CODE
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Find coverage file
|
|
59
|
+
COVERAGE_FILE=$(find "$COVERAGE_DIR" -name "*.cobertura.xml" | head -n 1)
|
|
60
|
+
if [ -z "$COVERAGE_FILE" ]; then
|
|
61
|
+
echo "❌ No coverage file found!"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
echo "📄 Coverage file: $COVERAGE_FILE"
|
|
66
|
+
|
|
67
|
+
# Parse coverage percentage using grep and sed
|
|
68
|
+
COVERAGE_LINE_RATE=$(grep -o 'line-rate="[0-9.]*"' "$COVERAGE_FILE" | head -n 1 | sed 's/line-rate="//;s/"//')
|
|
69
|
+
COVERAGE_PERCENT=$(echo "$COVERAGE_LINE_RATE * 100" | bc -l | xargs printf "%.2f")
|
|
70
|
+
|
|
71
|
+
# Parse branch coverage
|
|
72
|
+
BRANCH_LINE_RATE=$(grep -o 'branch-rate="[0-9.]*"' "$COVERAGE_FILE" | head -n 1 | sed 's/branch-rate="//;s/"//')
|
|
73
|
+
BRANCH_PERCENT=$(echo "$BRANCH_LINE_RATE * 100" | bc -l | xargs printf "%.2f")
|
|
74
|
+
|
|
75
|
+
# Color codes
|
|
76
|
+
GREEN='\033[0;32m'
|
|
77
|
+
RED='\033[0;31m'
|
|
78
|
+
BLUE='\033[0;34m'
|
|
79
|
+
YELLOW='\033[1;33m'
|
|
80
|
+
CYAN='\033[0;36m'
|
|
81
|
+
WHITE='\033[1;37m'
|
|
82
|
+
NC='\033[0m' # No Color
|
|
83
|
+
|
|
84
|
+
# Determine colors based on threshold
|
|
85
|
+
LINE_COLOR=$GREEN
|
|
86
|
+
if (( $(echo "$COVERAGE_PERCENT < $THRESHOLD" | bc -l) )); then
|
|
87
|
+
LINE_COLOR=$RED
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
BRANCH_COLOR=$GREEN
|
|
91
|
+
if (( $(echo "$BRANCH_PERCENT < $THRESHOLD" | bc -l) )); then
|
|
92
|
+
BRANCH_COLOR=$RED
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
echo -e "📈 Line Coverage: ${LINE_COLOR}${COVERAGE_PERCENT}%${NC}"
|
|
96
|
+
echo -e "🌿 Branch Coverage: ${BRANCH_COLOR}${BRANCH_PERCENT}%${NC}"
|
|
97
|
+
|
|
98
|
+
# Generate HTML report if requested
|
|
99
|
+
if [ "$HTML_REPORT" = true ]; then
|
|
100
|
+
echo -e "${BLUE}📊 Generating HTML coverage report...${NC}"
|
|
101
|
+
|
|
102
|
+
# Check if reportgenerator is installed
|
|
103
|
+
if ! command -v reportgenerator &> /dev/null; then
|
|
104
|
+
echo -e "${YELLOW}⬇️ Installing ReportGenerator...${NC}"
|
|
105
|
+
dotnet tool install -g dotnet-reportgenerator-globaltool
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
HTML_REPORT_PATH="$COVERAGE_DIR/html"
|
|
109
|
+
reportgenerator "-reports:$COVERAGE_FILE" "-targetdir:$HTML_REPORT_PATH" "-reporttypes:Html"
|
|
110
|
+
|
|
111
|
+
echo -e "${GREEN}📊 HTML report generated: $HTML_REPORT_PATH/index.html${NC}"
|
|
112
|
+
|
|
113
|
+
if [ "$OPEN_REPORT" = true ]; then
|
|
114
|
+
if command -v xdg-open &> /dev/null; then
|
|
115
|
+
xdg-open "$HTML_REPORT_PATH/index.html"
|
|
116
|
+
elif command -v open &> /dev/null; then
|
|
117
|
+
open "$HTML_REPORT_PATH/index.html"
|
|
118
|
+
else
|
|
119
|
+
echo "Cannot auto-open browser. Please open: $HTML_REPORT_PATH/index.html"
|
|
120
|
+
fi
|
|
121
|
+
echo -e "${GREEN}🌐 Opened coverage report in browser${NC}"
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Coverage summary
|
|
126
|
+
echo ""
|
|
127
|
+
echo -e "${CYAN}📊 Coverage Summary:${NC}"
|
|
128
|
+
echo -e " Line Coverage: ${WHITE}${COVERAGE_PERCENT}%${NC} (Threshold: ${THRESHOLD}%)"
|
|
129
|
+
echo -e " Branch Coverage: ${WHITE}${BRANCH_PERCENT}%${NC}"
|
|
130
|
+
|
|
131
|
+
# Determine status
|
|
132
|
+
if (( $(echo "$COVERAGE_PERCENT >= $THRESHOLD" | bc -l) )); then
|
|
133
|
+
echo -e " Status: ${GREEN}✅ PASSED${NC}"
|
|
134
|
+
STATUS_CODE=0
|
|
135
|
+
else
|
|
136
|
+
echo -e " Status: ${RED}❌ FAILED${NC}"
|
|
137
|
+
STATUS_CODE=1
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Fail if coverage is below threshold and FAIL_ON_LOW_COVERAGE is set
|
|
141
|
+
if [ "$FAIL_ON_LOW_COVERAGE" = true ] && [ $STATUS_CODE -eq 1 ]; then
|
|
142
|
+
echo ""
|
|
143
|
+
echo -e "${RED}❌ Coverage ${COVERAGE_PERCENT}% is below the required threshold of ${THRESHOLD}%${NC}"
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
echo ""
|
|
148
|
+
echo -e "${GREEN}✅ Coverage analysis completed successfully!${NC}"
|
|
149
|
+
exit 0
|
|
@@ -11,5 +11,9 @@
|
|
|
11
11
|
<PackageReference Include="NUnit" Version="{{ NUNIT_VERSION }}" />
|
|
12
12
|
<PackageReference Include="NUnit3TestAdapter" Version="{{ NUNIT_ADAPTER_VERSION }}" />
|
|
13
13
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="{{ MSTEST_SDK_VERSION }}" />
|
|
14
|
+
<PackageReference Include="coverlet.collector" Version="{{ COVERLET_VERSION }}">
|
|
15
|
+
<PrivateAssets>all</PrivateAssets>
|
|
16
|
+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
17
|
+
</PackageReference>
|
|
14
18
|
</ItemGroup>
|
|
15
19
|
</Project>
|
avrotize/avrotocsharp.py
CHANGED
|
@@ -14,9 +14,12 @@ from avrotize.constants import (
|
|
|
14
14
|
NEWTONSOFT_JSON_VERSION,
|
|
15
15
|
SYSTEM_TEXT_JSON_VERSION,
|
|
16
16
|
SYSTEM_MEMORY_DATA_VERSION,
|
|
17
|
+
PROTOBUF_NET_VERSION,
|
|
18
|
+
MSGPACK_VERSION,
|
|
17
19
|
NUNIT_VERSION,
|
|
18
20
|
NUNIT_ADAPTER_VERSION,
|
|
19
21
|
MSTEST_SDK_VERSION,
|
|
22
|
+
COVERLET_VERSION,
|
|
20
23
|
)
|
|
21
24
|
import glob
|
|
22
25
|
|
|
@@ -49,7 +52,9 @@ class AvroToCSharp:
|
|
|
49
52
|
self.system_text_json_annotation = False
|
|
50
53
|
self.newtonsoft_json_annotation = False
|
|
51
54
|
self.system_xml_annotation = False
|
|
55
|
+
self.msgpack_annotation = False
|
|
52
56
|
self.avro_annotation = False
|
|
57
|
+
self.protobuf_net_annotation = False
|
|
53
58
|
self.generated_types: Dict[str,str] = {}
|
|
54
59
|
self.generated_avro_types: Dict[str, Dict[str, Union[str, Dict, List]]] = {}
|
|
55
60
|
self.type_dict: Dict[str, Dict] = {}
|
|
@@ -177,7 +182,14 @@ class AvroToCSharp:
|
|
|
177
182
|
else:
|
|
178
183
|
class_definition += f"[XmlRoot(\"{class_name}\")]\n"
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
if self.protobuf_net_annotation:
|
|
186
|
+
class_definition += "[ProtoContract]\n"
|
|
187
|
+
|
|
188
|
+
# Add MessagePack serialization attribute for the class if enabled
|
|
189
|
+
if self.msgpack_annotation:
|
|
190
|
+
class_definition += "[MessagePackObject]\n"
|
|
191
|
+
|
|
192
|
+
fields_str = [self.generate_property(index + 1, field, class_name, avro_namespace) for index, field in enumerate(avro_schema.get('fields', []))]
|
|
181
193
|
class_body = "\n".join(fields_str)
|
|
182
194
|
class_definition += f"public partial class {class_name}"
|
|
183
195
|
if self.avro_annotation:
|
|
@@ -250,9 +262,11 @@ class AvroToCSharp:
|
|
|
250
262
|
"avrotocsharp/dataclass_core.jinja",
|
|
251
263
|
class_name=class_name,
|
|
252
264
|
avro_annotation=self.avro_annotation,
|
|
265
|
+
protobuf_net_annotation=self.protobuf_net_annotation,
|
|
253
266
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
254
267
|
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
|
|
255
|
-
system_xml_annotation=self.system_xml_annotation,
|
|
268
|
+
system_xml_annotation=self.system_xml_annotation,
|
|
269
|
+
msgpack_annotation=self.msgpack_annotation,
|
|
256
270
|
json_match_clauses=self.create_is_json_match_clauses(avro_schema, avro_namespace, class_name)
|
|
257
271
|
)
|
|
258
272
|
|
|
@@ -516,14 +530,19 @@ class AvroToCSharp:
|
|
|
516
530
|
union_type_name = union_type.rsplit('.', 1)[-1]
|
|
517
531
|
if self.is_csharp_reserved_word(union_type_name):
|
|
518
532
|
union_type_name = f"@{union_type_name}"
|
|
533
|
+
proto_member_name = union_type_name[1:] if union_type_name.startswith("@") else union_type_name
|
|
519
534
|
class_definition_objctr += f"{INDENT*3}if (obj is {union_type})\n{INDENT*3}{{\n{INDENT*4}self.{union_type_name} = ({union_type})obj;\n{INDENT*4}return self;\n{INDENT*3}}}\n"
|
|
520
535
|
if union_type in self.generated_types and self.generated_types[union_type] == "class":
|
|
521
536
|
class_definition_genericrecordctor += f"{INDENT*3}if (obj.Schema.Fullname == {union_type}.AvroSchema.Fullname)\n{INDENT*3}{{\n{INDENT*4}this.{union_type_name} = new {union_type}(obj);\n{INDENT*4}return;\n{INDENT*3}}}\n"
|
|
522
537
|
class_definition_ctors += \
|
|
523
538
|
f"{INDENT*2}/// <summary>\n{INDENT*2}/// Constructor for {union_type_name} values\n{INDENT*2}/// </summary>\n" + \
|
|
524
539
|
f"{INDENT*2}public {union_class_name}({union_type}? {union_type_name})\n{INDENT*2}{{\n{INDENT*3}this.{union_type_name} = {union_type_name};\n{INDENT*2}}}\n"
|
|
540
|
+
# Add Key attribute for MessagePack serialization if enabled
|
|
541
|
+
msgpack_key_attr = f"{INDENT*2}[Key({i})]\n" if self.msgpack_annotation else ""
|
|
525
542
|
class_definition_decls += \
|
|
526
543
|
f"{INDENT*2}/// <summary>\n{INDENT*2}/// Gets the {union_type_name} value\n{INDENT*2}/// </summary>\n" + \
|
|
544
|
+
(f"{INDENT*2}[ProtoMember({i+1}, Name=\"{proto_member_name}\")]\n" if self.protobuf_net_annotation else "") + \
|
|
545
|
+
msgpack_key_attr + \
|
|
527
546
|
f"{INDENT*2}public {union_type}? {union_type_name} {{ get; set; }} = null;\n"
|
|
528
547
|
class_definition_toobject += f"{INDENT*3}if ({union_type_name} != null) {{\n{INDENT*4}return {union_type_name};\n{INDENT*3}}}\n"
|
|
529
548
|
|
|
@@ -570,9 +589,14 @@ class AvroToCSharp:
|
|
|
570
589
|
if self.system_xml_annotation:
|
|
571
590
|
class_definition += \
|
|
572
591
|
f"{INDENT}[XmlRoot(\"{union_class_name}\")]\n"
|
|
592
|
+
if self.msgpack_annotation:
|
|
593
|
+
class_definition += \
|
|
594
|
+
f"{INDENT}[MessagePackObject]\n"
|
|
573
595
|
if self.system_text_json_annotation:
|
|
574
596
|
class_definition += \
|
|
575
597
|
f"{INDENT}[System.Text.Json.Serialization.JsonConverter(typeof({union_class_name}))]\n"
|
|
598
|
+
if self.protobuf_net_annotation:
|
|
599
|
+
class_definition += f"{INDENT}[ProtoContract]\n"
|
|
576
600
|
class_definition += \
|
|
577
601
|
f"{INDENT}public sealed class {union_class_name}"
|
|
578
602
|
if self.system_text_json_annotation:
|
|
@@ -697,6 +721,8 @@ class AvroToCSharp:
|
|
|
697
721
|
def is_enum_type(self, avro_type: Union[str, Dict, List]) -> bool:
|
|
698
722
|
""" Checks if a type is an enum (including nullable enums) """
|
|
699
723
|
if isinstance(avro_type, str):
|
|
724
|
+
if avro_type in ('null', 'boolean', 'int', 'long', 'float', 'double', 'bytes', 'string'):
|
|
725
|
+
return False
|
|
700
726
|
schema = self.schema_doc
|
|
701
727
|
name = avro_type.split('.')[-1]
|
|
702
728
|
namespace = ".".join(avro_type.split('.')[:-1])
|
|
@@ -711,7 +737,7 @@ class AvroToCSharp:
|
|
|
711
737
|
return avro_type.get('type') == 'enum'
|
|
712
738
|
return False
|
|
713
739
|
|
|
714
|
-
def generate_property(self, field: Dict, class_name: str, parent_namespace: str) -> str:
|
|
740
|
+
def generate_property(self, field_index: int, field: Dict, class_name: str, parent_namespace: str) -> str:
|
|
715
741
|
""" Generates a property """
|
|
716
742
|
is_enum_type = self.is_enum_type(field['type'])
|
|
717
743
|
field_type = self.convert_avro_type_to_csharp(
|
|
@@ -727,6 +753,9 @@ class AvroToCSharp:
|
|
|
727
753
|
prop = ''
|
|
728
754
|
prop += f"{INDENT}/// <summary>\n{INDENT}/// { field.get('doc', field_name) }\n{INDENT}/// </summary>\n"
|
|
729
755
|
|
|
756
|
+
if self.protobuf_net_annotation:
|
|
757
|
+
prop += f"{INDENT}[ProtoMember({field_index}, Name=\"{annotation_name}\")]\n"
|
|
758
|
+
|
|
730
759
|
# Add XML serialization attribute if enabled
|
|
731
760
|
if self.system_xml_annotation:
|
|
732
761
|
xmlkind = field.get('xmlkind', 'element')
|
|
@@ -735,6 +764,10 @@ class AvroToCSharp:
|
|
|
735
764
|
elif xmlkind == 'attribute':
|
|
736
765
|
prop += f"{INDENT}[XmlAttribute(\"{annotation_name}\")]\n"
|
|
737
766
|
|
|
767
|
+
# Add MessagePack serialization attribute if enabled
|
|
768
|
+
if self.msgpack_annotation:
|
|
769
|
+
prop += f"{INDENT}[Key({field_index})]\n"
|
|
770
|
+
|
|
738
771
|
if self.system_text_json_annotation:
|
|
739
772
|
prop += f"{INDENT}[System.Text.Json.Serialization.JsonPropertyName(\"{annotation_name}\")]\n"
|
|
740
773
|
if is_enum_type:
|
|
@@ -743,6 +776,8 @@ class AvroToCSharp:
|
|
|
743
776
|
prop += f"{INDENT}[System.Text.Json.Serialization.JsonConverter(typeof({field_type}))]\n"
|
|
744
777
|
if self.newtonsoft_json_annotation:
|
|
745
778
|
prop += f"{INDENT}[Newtonsoft.Json.JsonProperty(\"{annotation_name}\")]\n"
|
|
779
|
+
if is_enum_type:
|
|
780
|
+
prop += f"{INDENT}[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]\n"
|
|
746
781
|
|
|
747
782
|
# Determine initialization value
|
|
748
783
|
initialization = ""
|
|
@@ -785,6 +820,8 @@ class AvroToCSharp:
|
|
|
785
820
|
# Common using statements (add more as needed)
|
|
786
821
|
file_content = "using System;\nusing System.Collections.Generic;\n"
|
|
787
822
|
file_content += "using System.Linq;\n"
|
|
823
|
+
if self.protobuf_net_annotation:
|
|
824
|
+
file_content += "using ProtoBuf;\n"
|
|
788
825
|
if self.system_text_json_annotation:
|
|
789
826
|
file_content += "using System.Text.Json;\n"
|
|
790
827
|
file_content += "using System.Text.Json.Serialization;\n"
|
|
@@ -792,6 +829,8 @@ class AvroToCSharp:
|
|
|
792
829
|
file_content += "using Newtonsoft.Json;\n"
|
|
793
830
|
if self.system_xml_annotation: # Add XML serialization using directive
|
|
794
831
|
file_content += "using System.Xml.Serialization;\n"
|
|
832
|
+
if self.msgpack_annotation: # Add MessagePack serialization using directive
|
|
833
|
+
file_content += "using MessagePack;\n"
|
|
795
834
|
|
|
796
835
|
if namespace:
|
|
797
836
|
# Namespace declaration with correct indentation for the definition
|
|
@@ -833,7 +872,9 @@ class AvroToCSharp:
|
|
|
833
872
|
avro_annotation=self.avro_annotation,
|
|
834
873
|
system_xml_annotation=self.system_xml_annotation,
|
|
835
874
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
836
|
-
newtonsoft_json_annotation=self.newtonsoft_json_annotation
|
|
875
|
+
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
|
|
876
|
+
protobuf_net_annotation=self.protobuf_net_annotation,
|
|
877
|
+
msgpack_annotation=self.msgpack_annotation
|
|
837
878
|
)
|
|
838
879
|
elif type_kind == "enum":
|
|
839
880
|
test_class_definition = process_template(
|
|
@@ -845,7 +886,8 @@ class AvroToCSharp:
|
|
|
845
886
|
avro_annotation=self.avro_annotation,
|
|
846
887
|
system_xml_annotation=self.system_xml_annotation,
|
|
847
888
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
848
|
-
newtonsoft_json_annotation=self.newtonsoft_json_annotation
|
|
889
|
+
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
|
|
890
|
+
protobuf_net_annotation=self.protobuf_net_annotation
|
|
849
891
|
)
|
|
850
892
|
|
|
851
893
|
test_file_path = os.path.join(test_directory_path, f"{test_class_name}.cs")
|
|
@@ -856,13 +898,14 @@ class AvroToCSharp:
|
|
|
856
898
|
""" Retrieves fields for a given class name """
|
|
857
899
|
|
|
858
900
|
class Field:
|
|
859
|
-
def __init__(self, fn: str, ft:str, tv:Any, ct: bool, pm: bool, ie: bool):
|
|
901
|
+
def __init__(self, fn: str, ft:str, tv:Any, ct: bool, pm: bool, ie: bool, iu: bool):
|
|
860
902
|
self.field_name = fn
|
|
861
903
|
self.field_type = ft
|
|
862
904
|
self.test_value = tv
|
|
863
905
|
self.is_const = ct
|
|
864
906
|
self.is_primitive = pm
|
|
865
907
|
self.is_enum = ie
|
|
908
|
+
self.is_union = iu
|
|
866
909
|
|
|
867
910
|
fields: List[Field] = []
|
|
868
911
|
if avro_schema and 'fields' in avro_schema:
|
|
@@ -877,16 +920,19 @@ class AvroToCSharp:
|
|
|
877
920
|
field_type = self.convert_avro_type_to_csharp(class_name, field_name, field['type'], str(avro_schema.get('namespace', '')))
|
|
878
921
|
is_class = field_type in self.generated_types and self.generated_types[field_type] == "class"
|
|
879
922
|
is_enum = self.is_enum_type(field['type'])
|
|
923
|
+
is_union = field_type in self.generated_types and self.generated_types[field_type] == "union"
|
|
924
|
+
test_value = self.get_test_value(field_type, field['type'], str(avro_schema.get('namespace', ''))) if not "const" in field else '\"'+str(field["const"])+'\"'
|
|
880
925
|
f = Field(field_name,
|
|
881
926
|
field_type,
|
|
882
|
-
|
|
927
|
+
test_value,
|
|
883
928
|
"const" in field and field["const"] is not None,
|
|
884
929
|
not is_class,
|
|
885
|
-
is_enum
|
|
930
|
+
is_enum,
|
|
931
|
+
is_union)
|
|
886
932
|
fields.append(f)
|
|
887
933
|
return cast(List[Any], fields)
|
|
888
934
|
|
|
889
|
-
def get_test_value(self, csharp_type: str) -> str:
|
|
935
|
+
def get_test_value(self, csharp_type: str, avro_type: JsonNode = None, parent_namespace: str = '') -> str:
|
|
890
936
|
"""Returns a default test value based on the Avro type"""
|
|
891
937
|
# For nullable object types, return typed null to avoid var issues
|
|
892
938
|
if csharp_type == "object?":
|
|
@@ -908,6 +954,19 @@ class AvroToCSharp:
|
|
|
908
954
|
}
|
|
909
955
|
if csharp_type.endswith('?'):
|
|
910
956
|
csharp_type = csharp_type[:-1]
|
|
957
|
+
|
|
958
|
+
# Check if this is a union type (either by generated_types lookup or by type structure)
|
|
959
|
+
if csharp_type in self.generated_types and self.generated_types[csharp_type] == "union":
|
|
960
|
+
# For union types, we need to initialize with one of the valid options
|
|
961
|
+
# Find the first non-null type in the union and create a value for it
|
|
962
|
+
if isinstance(avro_type, list):
|
|
963
|
+
non_null_types = [t for t in avro_type if t != 'null']
|
|
964
|
+
if non_null_types:
|
|
965
|
+
first_option = non_null_types[0]
|
|
966
|
+
first_option_csharp = self.convert_avro_type_to_csharp('', 'Option0', first_option, parent_namespace)
|
|
967
|
+
first_option_value = self.get_test_value(first_option_csharp, first_option, parent_namespace)
|
|
968
|
+
return f'new {csharp_type}({first_option_value})'
|
|
969
|
+
|
|
911
970
|
return test_values.get(csharp_type, f'new {csharp_type}()')
|
|
912
971
|
|
|
913
972
|
def convert_schema(self, schema: JsonNode, output_dir: str):
|
|
@@ -967,10 +1026,14 @@ class AvroToCSharp:
|
|
|
967
1026
|
system_xml_annotation=self.system_xml_annotation,
|
|
968
1027
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
969
1028
|
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
|
|
1029
|
+
protobuf_net_annotation=self.protobuf_net_annotation,
|
|
1030
|
+
msgpack_annotation=self.msgpack_annotation,
|
|
970
1031
|
CSHARP_AVRO_VERSION=CSHARP_AVRO_VERSION,
|
|
971
1032
|
NEWTONSOFT_JSON_VERSION=NEWTONSOFT_JSON_VERSION,
|
|
972
1033
|
SYSTEM_TEXT_JSON_VERSION=SYSTEM_TEXT_JSON_VERSION,
|
|
973
1034
|
SYSTEM_MEMORY_DATA_VERSION=SYSTEM_MEMORY_DATA_VERSION,
|
|
1035
|
+
PROTOBUF_NET_VERSION=PROTOBUF_NET_VERSION,
|
|
1036
|
+
MSGPACK_VERSION=MSGPACK_VERSION,
|
|
974
1037
|
NUNIT_VERSION=NUNIT_VERSION,
|
|
975
1038
|
NUNIT_ADAPTER_VERSION=NUNIT_ADAPTER_VERSION,
|
|
976
1039
|
MSTEST_SDK_VERSION=MSTEST_SDK_VERSION))
|
|
@@ -988,9 +1051,39 @@ class AvroToCSharp:
|
|
|
988
1051
|
system_xml_annotation=self.system_xml_annotation,
|
|
989
1052
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
990
1053
|
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
|
|
1054
|
+
protobuf_net_annotation=self.protobuf_net_annotation,
|
|
991
1055
|
NUNIT_VERSION=NUNIT_VERSION,
|
|
992
1056
|
NUNIT_ADAPTER_VERSION=NUNIT_ADAPTER_VERSION,
|
|
993
|
-
MSTEST_SDK_VERSION=MSTEST_SDK_VERSION
|
|
1057
|
+
MSTEST_SDK_VERSION=MSTEST_SDK_VERSION,
|
|
1058
|
+
COVERLET_VERSION=COVERLET_VERSION))
|
|
1059
|
+
|
|
1060
|
+
# Generate coverage scripts
|
|
1061
|
+
if not os.path.exists(os.path.join(output_dir, "run_coverage.ps1")):
|
|
1062
|
+
coverage_ps1_file = os.path.join(output_dir, "run_coverage.ps1")
|
|
1063
|
+
with open(coverage_ps1_file, 'w', encoding='utf-8') as file:
|
|
1064
|
+
file.write(process_template(
|
|
1065
|
+
"avrotocsharp/run_coverage.ps1.jinja",
|
|
1066
|
+
project_name=project_name))
|
|
1067
|
+
|
|
1068
|
+
if not os.path.exists(os.path.join(output_dir, "run_coverage.sh")):
|
|
1069
|
+
coverage_sh_file = os.path.join(output_dir, "run_coverage.sh")
|
|
1070
|
+
with open(coverage_sh_file, 'w', encoding='utf-8') as file:
|
|
1071
|
+
file.write(process_template(
|
|
1072
|
+
"avrotocsharp/run_coverage.sh.jinja",
|
|
1073
|
+
project_name=project_name))
|
|
1074
|
+
# Make the shell script executable on Unix-like systems
|
|
1075
|
+
try:
|
|
1076
|
+
os.chmod(coverage_sh_file, 0o755)
|
|
1077
|
+
except:
|
|
1078
|
+
pass # Ignore on Windows
|
|
1079
|
+
|
|
1080
|
+
# Generate README with coverage documentation
|
|
1081
|
+
if not os.path.exists(os.path.join(output_dir, "README.md")):
|
|
1082
|
+
readme_file = os.path.join(output_dir, "README.md")
|
|
1083
|
+
with open(readme_file, 'w', encoding='utf-8') as file:
|
|
1084
|
+
file.write(process_template(
|
|
1085
|
+
"avrotocsharp/README.md.jinja",
|
|
1086
|
+
project_name=project_name))
|
|
994
1087
|
|
|
995
1088
|
self.output_dir = output_dir
|
|
996
1089
|
for avro_schema in (avs for avs in schema if isinstance(avs, dict)):
|
|
@@ -1012,8 +1105,10 @@ def convert_avro_to_csharp(
|
|
|
1012
1105
|
pascal_properties=False,
|
|
1013
1106
|
system_text_json_annotation=False,
|
|
1014
1107
|
newtonsoft_json_annotation=False,
|
|
1015
|
-
system_xml_annotation=False,
|
|
1016
|
-
|
|
1108
|
+
system_xml_annotation=False,
|
|
1109
|
+
msgpack_annotation=False,
|
|
1110
|
+
avro_annotation=False,
|
|
1111
|
+
protobuf_net_annotation=False
|
|
1017
1112
|
):
|
|
1018
1113
|
"""Converts Avro schema to C# classes
|
|
1019
1114
|
|
|
@@ -1026,7 +1121,9 @@ def convert_avro_to_csharp(
|
|
|
1026
1121
|
system_text_json_annotation (bool, optional): Use System.Text.Json annotations. Defaults to False.
|
|
1027
1122
|
newtonsoft_json_annotation (bool, optional): Use Newtonsoft.Json annotations. Defaults to False.
|
|
1028
1123
|
system_xml_annotation (bool, optional): Use System.Xml.Serialization annotations. Defaults to False.
|
|
1124
|
+
msgpack_annotation (bool, optional): Use MessagePack annotations. Defaults to False.
|
|
1029
1125
|
avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
|
|
1126
|
+
protobuf_net_annotation (bool, optional): Use protobuf-net annotations. Defaults to False.
|
|
1030
1127
|
"""
|
|
1031
1128
|
|
|
1032
1129
|
if not base_namespace:
|
|
@@ -1036,8 +1133,10 @@ def convert_avro_to_csharp(
|
|
|
1036
1133
|
avrotocs.pascal_properties = pascal_properties
|
|
1037
1134
|
avrotocs.system_text_json_annotation = system_text_json_annotation
|
|
1038
1135
|
avrotocs.newtonsoft_json_annotation = newtonsoft_json_annotation
|
|
1039
|
-
avrotocs.system_xml_annotation = system_xml_annotation
|
|
1136
|
+
avrotocs.system_xml_annotation = system_xml_annotation
|
|
1137
|
+
avrotocs.msgpack_annotation = msgpack_annotation
|
|
1040
1138
|
avrotocs.avro_annotation = avro_annotation
|
|
1139
|
+
avrotocs.protobuf_net_annotation = protobuf_net_annotation
|
|
1041
1140
|
avrotocs.convert(avro_schema_path, cs_file_path)
|
|
1042
1141
|
|
|
1043
1142
|
|
|
@@ -1049,8 +1148,10 @@ def convert_avro_schema_to_csharp(
|
|
|
1049
1148
|
pascal_properties: bool = False,
|
|
1050
1149
|
system_text_json_annotation: bool = False,
|
|
1051
1150
|
newtonsoft_json_annotation: bool = False,
|
|
1052
|
-
system_xml_annotation: bool = False,
|
|
1053
|
-
|
|
1151
|
+
system_xml_annotation: bool = False,
|
|
1152
|
+
msgpack_annotation: bool = False,
|
|
1153
|
+
avro_annotation: bool = False,
|
|
1154
|
+
protobuf_net_annotation: bool = False
|
|
1054
1155
|
):
|
|
1055
1156
|
"""Converts Avro schema to C# classes
|
|
1056
1157
|
|
|
@@ -1063,13 +1164,17 @@ def convert_avro_schema_to_csharp(
|
|
|
1063
1164
|
system_text_json_annotation (bool, optional): Use System.Text.Json annotations. Defaults to False.
|
|
1064
1165
|
newtonsoft_json_annotation (bool, optional): Use Newtonsoft.Json annotations. Defaults to False.
|
|
1065
1166
|
system_xml_annotation (bool, optional): Use System.Xml.Serialization annotations. Defaults to False.
|
|
1167
|
+
msgpack_annotation (bool, optional): Use MessagePack annotations. Defaults to False.
|
|
1066
1168
|
avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
|
|
1169
|
+
protobuf_net_annotation (bool, optional): Use protobuf-net annotations. Defaults to False.
|
|
1067
1170
|
"""
|
|
1068
1171
|
avrotocs = AvroToCSharp(base_namespace)
|
|
1069
1172
|
avrotocs.project_name = project_name
|
|
1070
1173
|
avrotocs.pascal_properties = pascal_properties
|
|
1071
1174
|
avrotocs.system_text_json_annotation = system_text_json_annotation
|
|
1072
1175
|
avrotocs.newtonsoft_json_annotation = newtonsoft_json_annotation
|
|
1073
|
-
avrotocs.system_xml_annotation = system_xml_annotation
|
|
1176
|
+
avrotocs.system_xml_annotation = system_xml_annotation
|
|
1177
|
+
avrotocs.msgpack_annotation = msgpack_annotation
|
|
1074
1178
|
avrotocs.avro_annotation = avro_annotation
|
|
1179
|
+
avrotocs.protobuf_net_annotation = protobuf_net_annotation
|
|
1075
1180
|
avrotocs.convert_schema(avro_schema, output_dir)
|