ch-robinson / dotnet-avro

An Avro implementation for .NET
https://engineering.chrobinson.com/dotnet-avro/
MIT License
132 stars 49 forks source link

Namespace is not generated. #312

Closed andrewBezerra closed 2 weeks ago

andrewBezerra commented 3 weeks ago

In the folowing code are generated .avsc files, but the namespace field with value 'AvroAttribute' for records was not generated. Can someone help me?

using System.ComponentModel;
using System.Reflection;
using Chr.Avro.Abstract;
using Chr.Avro.Representation;

namespace AvroAttribute
{

    [AvroSchema("Adiciona cliente")]
    public record AddCustomerCommand
    {
        [AvroDoc("Identifica o cliente")] 
        public Guid Id { get; set; } = Guid.Empty;

        [AvroDoc("Nome do cliente")]
        public string Name { get; init; } = string.Empty;

        [AvroDoc("Email do cliente")]
        public string Email { get; init; }
        [AvroDoc("Telefone do cliente")]
        public string Phone { get; init; }
        [AvroDoc("Data de criacao do registro do cliente")]
        public DateTime CreatedAt { get; set; }
        [AvroDoc("Limite padrao do cliente")]
        public decimal Limite { get; set; } =0M;
        [AvroDoc("Opcao nulavel padrao do cliente")]
        public int? Nulavel { get; set; } 
    }

    [AvroSchema("Atualiza cliente")]
    public record UpdateCustomerCommand([property: AvroDoc("ID do cliente")] Guid Id, [property: AvroDoc("Nome do cliente")] string Nome, [property: AvroDoc("E-mail do cliente")] string Email, [property: AvroDoc("Telefone do cliente")] string Phone);

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
    public class AvroSchemaAttribute : DescriptionAttribute

    {
        public AvroSchemaAttribute(string doc) : base(doc) { }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class AvroDocAttribute : DescriptionAttribute
    {
        public AvroDocAttribute(string doc) : base(doc) { }
    }

    public static class AvroSchemaGenerator
    {
        public static void GenerateSchemas()
        {
            var assembly = Assembly.GetExecutingAssembly();
            var typesWithAvroSchemaAttribute = assembly.GetTypes()
                .Where(type => type.GetCustomAttribute<AvroSchemaAttribute>() != null);

            var schemaFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AvroSchemas");
            Directory.Delete(schemaFolderPath, true);

            Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AvroSchemas"));

            foreach (var type in typesWithAvroSchemaAttribute)
            {

                var builder = new SchemaBuilder();
                var writer = new JsonSchemaWriter();
                var schemaGenerated = builder.BuildSchema(type);

                var schema = writer.Write(schemaGenerated,canonical:true);

                var schemaFileName = $"{type.Name}.avsc";
                var schemaFilePath = Path.Combine("AvroSchemas", schemaFileName);

                File.WriteAllText(schemaFilePath, schema);
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            AvroSchemaGenerator.GenerateSchemas();
        }
    }
}

Resulting File

{
   "name":"AvroAttribute.AddCustomerCommand",
   "type":"record",
   "fields":[
      {
         "name":"CreatedAt",
         "type":"string"
      },
      {
         "name":"Email",
         "type":"string"
      },
      {
         "name":"Id",
         "type":"string"
      },
      {
         "name":"Limite",
         "type":"bytes"
      },
      {
         "name":"Name",
         "type":"string"
      },
      {
         "name":"Nulavel",
         "type":[
            "null",
            "int"
         ]
      },
      {
         "name":"Phone",
         "type":"string"
      }
   ]
}
dstelljes commented 2 weeks ago

Hey Andrew, this is expected behavior; Chr.Avro prefers to write a fully-qualified name (here AvroAttribute.AddCustomerCommand) rather than separate name/namespace fields. This is to avoid a separate code path for parsing canonical form.