Skip to content

Commit

Permalink
Adding fixes for null values
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekctek committed Oct 5, 2023
1 parent 29dfc69 commit e758da2
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 53 deletions.
12 changes: 4 additions & 8 deletions src/Candid/CandidConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,15 @@ public CandidValue FromObject(object obj)
public CandidTypedValue FromTypedObject<T>(T obj)
where T : notnull
{
CandidValue value = this.FromObjectInternal(obj, out ICandidValueMapper mapper);
CandidType type = mapper.GetMappedCandidType(obj.GetType()) ?? throw new InvalidOperationException("Type does not map");
CandidValue value = this.FromObjectInternal<T>(obj, out ICandidValueMapper mapper);
CandidType type = mapper.GetMappedCandidType(typeof(T)) ?? throw new InvalidOperationException("Type does not map");
return new CandidTypedValue(value, type);
}


private CandidValue FromObjectInternal(object obj, out ICandidValueMapper mapper)
private CandidValue FromObjectInternal<T>(object obj, out ICandidValueMapper mapper)
{
if (ReferenceEquals(obj, null))
{
throw new ArgumentNullException(nameof(obj));
}
mapper = this.ResolveMapper(obj.GetType());
mapper = this.ResolveMapper(typeof(T));

return mapper!.Map(obj, this);
}
Expand Down
34 changes: 0 additions & 34 deletions src/Candid/Mapping/Mappers/RawCandidMapper.cs

This file was deleted.

25 changes: 20 additions & 5 deletions src/Candid/Mapping/Mappers/RecordMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using EdjCase.ICP.Candid.Models;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace EdjCase.ICP.Candid.Mapping.Mappers
{

internal class RecordMapper : ICandidValueMapper
{
public CandidType CandidType { get; }
public CandidRecordType CandidType { get; }
public Type Type { get; }
public Dictionary<CandidTag, PropertyMetaData> Properties { get; }

Expand All @@ -27,11 +26,23 @@ public object Map(CandidValue value, CandidConverter converter)
object obj = Activator.CreateInstance(this.Type);
foreach ((CandidTag tag, PropertyMetaData property) in this.Properties)
{
object? fieldValue;
if (!record.Fields.TryGetValue(tag, out CandidValue fieldCandidValue))
{
throw new Exception($"Could not map candid record to type '{this.Type}'. Record is missing field '{tag}'");
if (!this.CandidType.Fields.TryGetValue(tag, out CandidType fieldType))
{
// Record has property that is not in the candid type, skip
continue;
}
if (fieldType is not CandidOptionalType)
{
// Only throw if fieldType is not an opt value since the value is unset (null)
// or has an extra
throw new Exception($"Could not map candid record to type '{this.Type}'. Record is missing field '{tag}'");
}
// Set to optional value if not specified in record
fieldCandidValue = new CandidOptional(null);
}
object? fieldValue;
if (property.CustomMapper != null)
{
fieldValue = property.CustomMapper.Map(fieldCandidValue, converter);
Expand All @@ -52,7 +63,11 @@ public CandidValue Map(object value, CandidConverter converter)
{
object propValue = property.PropertyInfo.GetValue(value);
CandidValue v;
if (property.CustomMapper != null)
if (propValue == null)
{
v = new CandidOptional(null);
}
else if (property.CustomMapper != null)
{
v = property.CustomMapper.Map(propValue, converter);
}
Expand Down
8 changes: 6 additions & 2 deletions src/Candid/Mapping/Mappers/VariantMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ public CandidValue Map(object obj, CandidConverter converter)


CandidValue innerValue;
if (optionInfo.Type != null)
if (innerObj == null)
{
innerValue = new CandidOptional(null);
}
else if (optionInfo.Type != null)
{
innerValue = converter.FromObject(innerObj!);
innerValue = converter.FromObject(innerObj);
}
else
{
Expand Down
37 changes: 33 additions & 4 deletions test/Candid.Tests/CandidConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public void Text_From_String()
this.Test(value, expected, (x, y) => x == y);
}


[Fact]
public void Vector_From_List()
{
Expand Down Expand Up @@ -85,7 +86,7 @@ public void Vector_From_Dict()
public class RecordClass
{
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public string StringField { get; set; }
public OptionalValue<string> StringField { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public int IntField { get; set; }

Expand All @@ -108,21 +109,21 @@ public void Record_From_Class()
{
var values = new RecordClass
{
StringField = "StringValue",
StringField = OptionalValue<string>.WithValue("StringValue"),
IntField = 2
};
CandidTag stringFieldName = CandidTag.FromName("StringField");
CandidTag intFieldName = CandidTag.FromName("IntField");
var fields = new Dictionary<CandidTag, CandidValue>
{
{stringFieldName, CandidValue.Text("StringValue")},
{stringFieldName, new CandidOptional(CandidValue.Text("StringValue"))},
{intFieldName, CandidValue.Int32(2)}
};
CandidValue expectedValue = new CandidRecord(fields);

var fieldTypes = new Dictionary<CandidTag, CandidType>
{
{stringFieldName, new CandidPrimitiveType(PrimitiveType.Text)},
{stringFieldName, new CandidOptionalType(new CandidPrimitiveType(PrimitiveType.Text))},
{intFieldName, new CandidPrimitiveType(PrimitiveType.Int32)}
};
CandidType expectedType = new CandidRecordType(fieldTypes);
Expand All @@ -136,6 +137,34 @@ public void Record_From_Class()
}


[Fact]
public void Parital_Record_From_Class()
{
// Deserialize a record with a missing opt field, the value should be a 'NoValue' opt
CandidTag stringFieldName = CandidTag.FromName("StringField");
CandidTag intFieldName = CandidTag.FromName("IntField");
var fields = new Dictionary<CandidTag, CandidValue>
{
// Missing string field
{intFieldName, CandidValue.Int32(2)}
};
CandidValue value = new CandidRecord(fields);

var fieldTypes = new Dictionary<CandidTag, CandidType>
{
{stringFieldName, new CandidOptionalType(new CandidPrimitiveType(PrimitiveType.Text))},
{intFieldName, new CandidPrimitiveType(PrimitiveType.Int32)}
};
CandidType type = new CandidRecordType(fieldTypes);
CandidTypedValue typedValue = CandidTypedValue.FromValueAndType(value, type);


RecordClass obj = CandidConverter.Default.ToObject<RecordClass>(value);
Assert.NotNull(obj);
Assert.Equal(new OptionalValue<string>(), obj.StringField);
}



[Variant]
public class VariantValueClass
Expand Down

0 comments on commit e758da2

Please sign in to comment.