Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4443265
AVRO-2211: Support schema creation
yanivru Mar 12, 2022
43f8160
Add license info to new files
yanivru Mar 12, 2022
13ac5ae
Fix documentation for FixedSchema ctor
yanivru Mar 12, 2022
476a992
Remove and sort using
yanivru Mar 15, 2022
fa87fbf
Add missing brackets and replace var with explicit type
yanivru Mar 15, 2022
37967f3
Fix exception type in case of parsing
yanivru Mar 15, 2022
e42f18b
Rename field to follow conventions
yanivru Mar 15, 2022
05b77ba
AVRO 2211: Inlining temporary variable in linq
yanivru Mar 16, 2022
825d746
AVRO-2211: Change exception type and add missing documentations
yanivru Mar 16, 2022
203f1a5
AVRO-2211: Fix RecordSchema to set the positions of it's fields, inst…
yanivru Mar 16, 2022
42feece
AVRO-2211: Fix RecordSchema fields assignment when creation new Recor…
yanivru Mar 16, 2022
744a338
AVRO-2211: Change constructors of schema classes to factory method
yanivru Mar 20, 2022
a292148
AVRO-2211: Add unit tests for RecordSchema and EnumSchema
yanivru Mar 25, 2022
2bf112c
:AVRO-2211: Remove whitespace
yanivru Mar 30, 2022
7e76def
:AVRO-2211: Add symbol names verification for EnumSchema
yanivru Mar 30, 2022
55fa5de
AVRO-2211: Fix enum name validation
yanivru Apr 2, 2022
2851e5d
AVRO-2211: Throw AvroException consistently
yanivru Apr 2, 2022
fed7a5f
AVRO-2211: Throw AvroException in RecrodSchema consistently
yanivru Apr 2, 2022
0a98e7a
AVRO-2211: Remove duplicate factory methods on MapSchema
yanivru Apr 2, 2022
0e74ea4
AVRO-2211: Remove redundant parameter doc
yanivru Apr 2, 2022
c09b58d
AVRO-2211: Add Schema creation tests
yanivru Apr 2, 2022
3718dc8
AVRO-2211: Change ValidateSymbol to throw exception
yanivru Apr 3, 2022
07b3a94
AVRO-2211: Fix typo
yanivru Apr 3, 2022
3a9d670
AVRO-2211: Fix code QL issues
yanivru Apr 7, 2022
a14ce11
AVRO-2211: Fix typo
yanivru Apr 7, 2022
615147b
Merge branch 'master' into schema-creation
martin-g Apr 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lang/csharp/src/apache/main/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Avro.test, PublicKey=00240000048000009400000006020000002400005253413100040000010001001145636d1b96168c2781abfd60478f45d010fe83dd0f318404cbf67252bca8cd827f24648d47ff682f35e60307c05d3cd89f0b063729cf8d2ebe6510b9e7d295dec6707ec91719d859458981f7ca1cbbea79b702b2fb64d1dbf0881887315345b70fa50fcf91b59e6a937c8d23919d409ee2f1f234cc4c8dbf5a29d3d670f3c9")]
36 changes: 36 additions & 0 deletions lang/csharp/src/apache/main/Schema/Aliases.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove and sort usings. Assuming you are using VS.

using System.Linq;

namespace Avro
{
internal static class Aliases
{
internal static IList<SchemaName> GetSchemaNames(IEnumerable<string> aliases, string enclosingTypeName, string enclosingTypeNamespace)
{
if (aliases == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add brackets?

{
return null;
}

SchemaName enclosingSchemaName = new SchemaName(enclosingTypeName, enclosingTypeNamespace, null, null);
return aliases.Select(alias => new SchemaName(alias, enclosingSchemaName.Namespace, null, null)).ToList();
}
}
}
22 changes: 17 additions & 5 deletions lang/csharp/src/apache/main/Schema/ArraySchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class ArraySchema : UnnamedSchema
/// <summary>
/// Schema for the array 'type' attribute
/// </summary>
public Schema ItemSchema { get; set; }
public Schema ItemSchema { get; set; }

/// <summary>
/// Static class to return a new instance of ArraySchema
Expand All @@ -48,11 +48,23 @@ internal static ArraySchema NewInstance(JToken jtok, PropertyMap props, SchemaNa
}

/// <summary>
/// Constructor
/// Creates a new <see cref="ArraySchema"/>
/// </summary>
/// <param name="items">schema for the array items type</param>
/// <param name="props">dictionary that provides access to custom properties</param>
private ArraySchema(Schema items, PropertyMap props) : base(Type.Array, props)
/// <param name="items">Schema for the array items type</param>
/// <param name="customAttributes">Dictionary that provides access to custom properties</param>
/// <returns></returns>
public static ArraySchema Create(Schema items, PropertyMap customAttributes = null)
{
return new ArraySchema(items, customAttributes);
}

/// <summary>
/// Initializes a new instance of the <see cref="ArraySchema"/> class.
/// </summary>
/// <param name="items">Schema for the array items type</param>
/// <param name="customAttributes">Dictionary that provides access to custom properties</param>
private ArraySchema(Schema items, PropertyMap customAttributes)
: base(Type.Array, customAttributes)
{
if (null == items) throw new ArgumentNullException(nameof(items));
this.ItemSchema = items;
Expand Down
75 changes: 69 additions & 6 deletions lang/csharp/src/apache/main/Schema/EnumSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;

namespace Avro
Expand All @@ -30,7 +31,7 @@ public class EnumSchema : NamedSchema
/// <summary>
/// List of strings representing the enum symbols
/// </summary>
public IList<string> Symbols { get; private set; }
public IList<string> Symbols { get; private set; }

/// <summary>
/// The default token to use when deserializing an enum when the provided token is not found
Expand All @@ -47,6 +48,34 @@ public class EnumSchema : NamedSchema
/// </summary>
public int Count { get { return Symbols.Count; } }

/// <summary>
/// Initializes a new instance of the <see cref="EnumSchema"/> class.
/// </summary>
/// <param name="name">Name of enum</param>
/// <param name="space">Namespace of enum</param>
/// <param name="aliases">List of aliases for the name</param>
/// <param name="symbols">List of enum symbols</param>
/// <param name="customProperties">Custom properties on this schema</param>
/// <param name="doc">Documentation for this named schema</param>
/// <param name="defaultSymbol"></param>
public static EnumSchema Create(string name,
IEnumerable<string> symbols,
string space = null,
IEnumerable<string> aliases = null,
PropertyMap customProperties = null,
string doc = null,
string defaultSymbol = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have an addition parameter at the end? bool shouldThrowParseException = false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public method should have this parameter, it should always throw AvroException (and not ParseException. The internal constructor can be used by this method (public factory method) or by the NewInstance method (the json parsing method). So I add this parameter to it to choose which exception to throw.

But if we don't care about backward compatibility of the inner exception (till now there was SchemaParseException inside SchemaParseException, I can change to SchemaParseException the contains AvroException), I can remove this kinda ugly code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the change to AvroException (however not the message if possible). I dont think an inner exception of SchemaParse Exception makes too much difference, it can be just a asimple AvroException.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, changed to AvroException (only the inner exception). Not sure how to add the release note :-)

{
return new EnumSchema(new SchemaName(name, space, null, doc),
Aliases.GetSchemaNames(aliases, name, space),
symbols.ToList(),
CreateSymbolsMap(symbols),
customProperties,
new SchemaNames(),
doc,
defaultSymbol);
}

/// <summary>
/// Static function to return new instance of EnumSchema
/// </summary>
Expand Down Expand Up @@ -81,7 +110,7 @@ internal static EnumSchema NewInstance(JToken jtok, PropertyMap props, SchemaNam
return new EnumSchema(name, aliases, symbols, symbolMap, props, names,
JsonHelper.GetOptionalString(jtok, "doc"), JsonHelper.GetOptionalString(jtok, "default"));
}
catch (SchemaParseException e)
catch (AvroException e)
{
throw new SchemaParseException($"{e.Message} at '{jtok.Path}'", e);
}
Expand All @@ -103,15 +132,49 @@ private EnumSchema(SchemaName name, IList<SchemaName> aliases, List<string> symb
string doc, string defaultSymbol)
: base(Type.Enumeration, name, aliases, props, names, doc)
{
if (null == name.Name) throw new SchemaParseException("name cannot be null for enum schema.");
if (null == name.Name) throw new AvroException("name cannot be null for enum schema.");
this.Symbols = symbols;
this.symbolMap = symbolMap;

if (null != defaultSymbol && !symbolMap.ContainsKey(defaultSymbol))
throw new SchemaParseException($"Default symbol: {defaultSymbol} not found in symbols");
throw new AvroException($"Default symbol: {defaultSymbol} not found in symbols");
Default = defaultSymbol;
}

/// <summary>
/// Creates symbols map from specified list of symbols.
/// Symbol map contains the names of the symbols and their index.
/// </summary>
/// <param name="symbols">List of symbols</param>
/// <returns>Symbol map</returns>
/// <exception cref="AvroException">Is thrown if the symbols list contains invalid symbol name or duplicate symbols</exception>
private static IDictionary<string, int> CreateSymbolsMap(IEnumerable<string> symbols)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document usage and exception case in method summary, params, and exceptions

{
IDictionary<string, int> symbolMap = new Dictionary<string, int>();
int i = 0;
foreach (var symbol in symbols)
{
ValidateSymbolName(symbol);

if (symbolMap.ContainsKey(symbol))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add brackets

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are null or empty strings allowed as a symbol? Should it throw ArgumentException if string.IsNullOrEmpty(symbol)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArgumentException or AvroException? The convention everywhere in the code is to throw AvroException even in cases that ArgumentException seems more appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AvroException makes sense. Is there any other criteria for a symbol name? Maybe add a ValidateSymbol(string symbol) which checks a symbol and throws the exception? As a first implementation it is a simple string.IsNullOrEmpty()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, just seen in spec that "Every symbol must match the regular expression [A-Za-z_][A-Za-z0-9_]*"
So added the regex to the name verification.

{
throw new AvroException($"Duplicate symbol: {symbol}");
}

symbolMap[symbol] = i++;
}

return symbolMap;
}

private static void ValidateSymbolName(string symbol)
{
if(string.IsNullOrEmpty(symbol) || !Regex.IsMatch(symbol, "^([A-Za-z_][A-Za-z0-9_]*)$"))
{
throw new AvroException($"Invalid symbol name: {symbol}");
}
}

/// <summary>
/// Writes enum schema in JSON format
/// </summary>
Expand All @@ -127,7 +190,7 @@ protected internal override void WriteJsonFields(Newtonsoft.Json.JsonTextWriter
foreach (string s in this.Symbols)
writer.WriteValue(s);
writer.WriteEndArray();
if (null != Default)
if (null != Default)
{
writer.WritePropertyName("default");
writer.WriteValue(Default);
Expand Down
34 changes: 33 additions & 1 deletion lang/csharp/src/apache/main/Schema/Field.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,39 @@ public enum SortOrder
/// <summary>
/// Static comparer object for JSON objects such as the fields default value
/// </summary>
internal static JTokenEqualityComparer JtokenEqual = new JTokenEqualityComparer();
internal readonly static JTokenEqualityComparer JtokenEqual = new JTokenEqualityComparer();

/// <summary>
/// Initializes a new instance of the <see cref="Field"/> class.
/// </summary>
/// <param name="schema">schema for the field type.</param>
/// <param name="name">name of the field.</param>
/// <param name="aliases">list of aliases for the name of the field.</param>
/// <param name="pos">position of the field.</param>
/// <param name="doc">documentation for the field.</param>
/// <param name="defaultValue">field's default value if it exists.</param>
/// <param name="sortorder">sort order of the field.</param>
/// <param name="customProperties">dictionary that provides access to custom properties.</param>
public Field(Schema schema,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you didn't modify the existing constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to add default values to non mandatory parameters. I had to change their order (switch between aliases and pos), and didn't want to make a breaking change.

string name,
int pos,
IList<string> aliases = null,
string doc = null,
JToken defaultValue = null,
SortOrder sortorder = SortOrder.ignore,
PropertyMap customProperties = null)
: this(schema, name, aliases, pos, doc, defaultValue, sortorder, customProperties)
{
}

/// <summary>
/// Creates a new field based on the specified field, with a different position.
/// </summary>
/// <returns>A clone of this field with new position.</returns>
internal Field ChangePosition(int newPosition)
{
return new Field(Schema, Name, newPosition, Aliases, Documentation, DefaultValue, Ordering ?? SortOrder.ignore, Props);
}

/// <summary>
/// A flag to indicate if reader schema has a field that is missing from writer schema and has a default value
Expand Down
14 changes: 14 additions & 0 deletions lang/csharp/src/apache/main/Schema/FixedSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ public class FixedSchema : NamedSchema
/// </summary>
public int Size { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="FixedSchema"/> class.
/// </summary>
/// <param name="name">Name of the fixed schema</param>
/// <param name="aliases">List of aliases for the name</param>
/// <param name="size">Fixed size</param>
/// <param name="space">Namespace of fixed</param>
/// <param name="customProperties">Custom properties on this schema</param>
/// <param name="doc">Documentation for this named schema</param>
public static FixedSchema Create(string name, int size, string space = null, IEnumerable<string> aliases = null, PropertyMap customProperties = null, string doc = null)
{
return new FixedSchema(new SchemaName(name, space, null, doc), Aliases.GetSchemaNames(aliases, name, space), size, customProperties, new SchemaNames(), doc);
}

/// <summary>
/// Static function to return new instance of the fixed schema class
/// </summary>
Expand Down
12 changes: 7 additions & 5 deletions lang/csharp/src/apache/main/Schema/MapSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ public class MapSchema : UnnamedSchema
/// Creates a new <see cref="MapSchema"/> from the given schema.
/// </summary>
/// <param name="type">Schema to create the map schema from.</param>
/// <param name="customProperties">Dictionary that provides access to custom properties</param>
/// <returns>A new <see cref="MapSchema"/>.</returns>
public static MapSchema CreateMap(Schema type)
public static MapSchema CreateMap(Schema type, PropertyMap customProperties = null)
{
return new MapSchema(type,null);
return new MapSchema(type, customProperties);
}

/// <summary>
Expand Down Expand Up @@ -67,9 +68,10 @@ internal static MapSchema NewInstance(JToken jtok, PropertyMap props, SchemaName
/// <summary>
/// Constructor for map schema class
/// </summary>
/// <param name="valueSchema">schema for map values type</param>
/// <param name="props">dictionary that provides access to custom properties</param>
private MapSchema(Schema valueSchema, PropertyMap props) : base(Type.Map, props)
/// <param name="valueSchema">Schema for map values type</param>
/// <param name="cutsomProperties">Dictionary that provides access to custom properties</param>
private MapSchema(Schema valueSchema, PropertyMap cutsomProperties)
: base(Type.Map, cutsomProperties)
{
if (null == valueSchema) throw new ArgumentNullException(nameof(valueSchema), "valueSchema cannot be null.");
this.ValueSchema = valueSchema;
Expand Down
16 changes: 14 additions & 2 deletions lang/csharp/src/apache/main/Schema/PrimitiveSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,21 @@ public sealed class PrimitiveSchema : UnnamedSchema
/// Constructor for primitive schema
/// </summary>
/// <param name="type"></param>
/// <param name="props">dictionary that provides access to custom properties</param>
private PrimitiveSchema(Type type, PropertyMap props) : base(type, props)
/// <param name="customProperties">dictionary that provides access to custom properties</param>
private PrimitiveSchema(Type type, PropertyMap customProperties)
: base(type, customProperties)
{
}

/// <summary>
/// Creates a new instance of <see cref="PrimitiveSchema"/>
/// </summary>
/// <param name="type">The primitive type to create</param>
/// <param name="customProperties">Dictionary that provides access to custom properties</param>
/// <returns></returns>
public static PrimitiveSchema Create(Type type, PropertyMap customProperties = null)
{
return new PrimitiveSchema(type, customProperties);
}

/// <summary>
Expand Down
Loading