guibranco / Sankhya-SDK-dotnet

📊 ⚙️ Sankhya platform .NET SDK
https://guibranco.github.io/Sankhya-SDK-dotnet/
MIT License
4 stars 3 forks source link

[FEATURE] Add Entity Validation and Testing #348

Open guibranco opened 1 day ago

guibranco commented 1 day ago

Feature Request: Add Entity Validation and Testing

Description

Implement a validator for entities in the Sankhya-SDK-dotnet repository to ensure compliance with the following constraints:

  1. Entity Class Requirements:

    • Must be a public class.
    • Must have an EntityAttribute specifying the entity name (mapping to the Sankhya service entity).
    • Must inherit from the IEntity interface.
    • The IEquatable interface must be implemented.
  2. Property Constraints:

    • Every public property must have one of the following attributes:
      • EntityElementAttribute
      • EntityIgnoreAttribute
      • EntityReferenceAttribute
    • For each property with the EntityElementAttribute, a corresponding ShouldSerialize{PropertyName} method must exist. This method (with a bool return type) determines if the property has been modified and needs serialization in the HTTP request payload.

Feature Scope

Deliverables

  1. A utility to search and validate entity classes in an assembly.
  2. Unit tests to confirm the validator identifies compliant and non-compliant entities.
  3. Documentation explaining how to use and integrate the validator into workflows.

Example Entity Class

Below is an example of a valid entity class that adheres to the described rules:

[Entity("Customer")]
public class Customer : IEntity, IEquatable<Customer>
{
    [EntityElement("name")]
    public string Name { get; set; }

    public bool ShouldSerializeName() => !string.IsNullOrEmpty(Name);

    public bool Equals(Customer other)
    {
        if (other == null) return false;
        return Name == other.Name;
    }
}

Example Validator Code

Here is an example implementation for the validator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Sankhya
{
    public static class EntityValidator
    {
        public static void ValidateEntities(Assembly assembly)
        {
            // Find all types in the assembly
            var types = assembly.GetTypes();

            // Filter types with EntityAttribute or inheriting IEntity
            var entityTypes = types.Where(t =>
                t.GetCustomAttribute<EntityAttribute>() != null ||
                typeof(IEntity).IsAssignableFrom(t));

            foreach (var type in entityTypes)
            {
                ValidateEntityType(type);
            }
        }

        private static void ValidateEntityType(Type type)
        {
            // Ensure the type has both EntityAttribute and inherits from IEntity
            var hasEntityAttribute = type.GetCustomAttribute<EntityAttribute>() != null;
            var inheritsIEntity = typeof(IEntity).IsAssignableFrom(type);

            if (!hasEntityAttribute || !inheritsIEntity)
            {
                throw new InvalidOperationException(
                    $"The type {type.FullName} must have both EntityAttribute and implement IEntity.");
            }

            // Ensure the type implements IEquatable
            if (!ImplementsIEquatable(type))
            {
                throw new InvalidOperationException(
                    $"The type {type.FullName} must implement IEquatable<{type.Name}>.");
            }

            // Validate properties
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                ValidateProperty(type, property);
            }
        }

        private static void ValidateProperty(Type type, PropertyInfo property)
        {
            // Ensure property has one of the valid attributes
            var hasValidAttribute = property.GetCustomAttributes()
                .Any(attr => attr is EntityElementAttribute ||
                             attr is EntityIgnoreAttribute ||
                             attr is EntityReferenceAttribute);

            if (!hasValidAttribute)
            {
                throw new InvalidOperationException(
                    $"The property {type.FullName}.{property.Name} must have one of the valid attributes: " +
                    $"EntityElementAttribute, EntityIgnoreAttribute, or EntityReferenceAttribute.");
            }

            // Ensure ShouldSerialize{PropertyName} method exists if the property has EntityElementAttribute
            if (property.GetCustomAttribute<EntityElementAttribute>() != null)
            {
                var methodName = $"ShouldSerialize{property.Name}";
                var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);

                if (method == null || method.ReturnType != typeof(bool) || method.GetParameters().Length != 0)
                {
                    throw new InvalidOperationException(
                        $"The type {type.FullName} must have a method {methodName} with the signature 'bool {methodName}()'.");
                }
            }
        }

        private static bool ImplementsIEquatable(Type type)
        {
            var equatableInterface = typeof(IEquatable<>).MakeGenericType(type);
            return equatableInterface.IsAssignableFrom(type);
        }
    }
}
gitauto-ai[bot] commented 1 day ago

Click the checkbox below to generate a PR!

@guibranco, You have 3 requests left in this cycle which refreshes on 2024-11-21 10:07:38+00:00. If you have any questions or concerns, please contact us at info@gitauto.ai.

gitauto-ai[bot] commented 19 hours ago

Hey, I'm a bit lost here! Not sure which file I should be fixing. Could you give me a bit more to go on? Maybe add some details to the issue or drop a comment with some extra hints? Thanks!

Have feedback or need help? Feel free to email info@gitauto.ai.