Skip to content

Commit

Permalink
rename adapter to adapter provider, and write some help docs
Browse files Browse the repository at this point in the history
  • Loading branch information
hahn-kev committed Oct 9, 2024
1 parent 260bd5c commit 8077a68
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 34 deletions.
22 changes: 14 additions & 8 deletions src/SIL.Harmony/Adapters/CustomAdapterProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,34 @@

namespace SIL.Harmony.Adapters;

public class CustomAdapterProvider<TCommonInterface, TCustomAdapter> : IObjectAdapter
public class CustomAdapterProvider<TCommonInterface, TCustomAdapter> : IObjectAdapterProvider
where TCommonInterface : class
where TCustomAdapter : class, ICustomAdapter<TCustomAdapter, TCommonInterface>, IPolyType
{
public CustomAdapterProvider()
private readonly ObjectTypeListBuilder _objectTypeListBuilder;
private readonly List<AdapterRegistration> _objectTypes = new();
private Dictionary<Type, List<JsonDerivedType>> JsonTypes { get; } = [];
Dictionary<Type, List<JsonDerivedType>> IObjectAdapterProvider.JsonTypes => JsonTypes;

public CustomAdapterProvider(ObjectTypeListBuilder objectTypeListBuilder)
{
_objectTypeListBuilder = objectTypeListBuilder;
JsonTypes.AddDerivedType(typeof(IObjectBase), typeof(TCustomAdapter), TCustomAdapter.TypeName);
}

private readonly List<AdapterRegistration> _objectTypes = new();
public Dictionary<Type, List<JsonDerivedType>> JsonTypes { get; } = [];


public CustomAdapterProvider<TCommonInterface, TCustomAdapter> AddWithCustomPolymorphicMapping<T>(string typeName,
Action<EntityTypeBuilder<T>>? configureEntry = null
) where T : class, TCommonInterface
{
JsonTypes.AddDerivedType(typeof(TCommonInterface), typeof(T), typeName);
return Add(configureEntry);
}

public CustomAdapterProvider<TCommonInterface, TCustomAdapter> Add<T>(
Action<EntityTypeBuilder<T>>? configureEntry = null
) where T : class, TCommonInterface
{
_objectTypeListBuilder.CheckFrozen();
_objectTypes.Add(
new AdapterRegistration(typeof(T),
builder =>
Expand All @@ -41,17 +46,18 @@ public CustomAdapterProvider<TCommonInterface, TCustomAdapter> Add<T>(
return this;
}

public IEnumerable<AdapterRegistration> GetRegistrations()
IEnumerable<AdapterRegistration> IObjectAdapterProvider.GetRegistrations()
{
return _objectTypes;
}

public IObjectBase Adapt(object obj)
IObjectBase IObjectAdapterProvider.Adapt(object obj)
{
return TCustomAdapter.Create((TCommonInterface)obj);
}
}

// it's possible to implement this without a Common interface, but it would require the adapter to have 1 property for each object type
public interface ICustomAdapter<TSelf, TCommonInterface> : IObjectBase, IPolyType
where TSelf : class,
ICustomAdapter<TSelf, TCommonInterface>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@

namespace SIL.Harmony.Adapters;

public class DefaultAdapter : IObjectAdapter
public class DefaultAdapterProvider(ObjectTypeListBuilder objectTypeListBuilder) : IObjectAdapterProvider
{
private readonly List<AdapterRegistration> _objectTypes = new();
private readonly List<AdapterRegistration> _objectTypes = [];

IEnumerable<AdapterRegistration> IObjectAdapter.GetRegistrations()
IEnumerable<AdapterRegistration> IObjectAdapterProvider.GetRegistrations()
{
return _objectTypes.AsReadOnly();
}

public DefaultAdapter Add<T>(Action<EntityTypeBuilder<T>>? configureEntry = null) where T : class, IObjectBase<T>
public DefaultAdapterProvider Add<T>(Action<EntityTypeBuilder<T>>? configureEntry = null) where T : class, IObjectBase<T>
{
objectTypeListBuilder.CheckFrozen();
JsonTypes.AddDerivedType(typeof(IObjectBase), typeof(T), T.TypeName);
_objectTypes.Add(new(typeof(T), builder =>
{
Expand All @@ -26,7 +27,7 @@ public DefaultAdapter Add<T>(Action<EntityTypeBuilder<T>>? configureEntry = null
return this;
}

IObjectBase IObjectAdapter.Adapt(object obj)
IObjectBase IObjectAdapterProvider.Adapt(object obj)
{
if (obj is IObjectBase objectBase)
{
Expand All @@ -37,5 +38,6 @@ IObjectBase IObjectAdapter.Adapt(object obj)
$"Object is of type {obj.GetType().Name} which does not implement {nameof(IObjectBase)}");
}

public Dictionary<Type, List<JsonDerivedType>> JsonTypes { get; } = [];
private Dictionary<Type, List<JsonDerivedType>> JsonTypes { get; } = [];
Dictionary<Type, List<JsonDerivedType>> IObjectAdapterProvider.JsonTypes => JsonTypes;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

namespace SIL.Harmony.Adapters;

public record AdapterRegistration(Type ObjectDbType, Func<ModelBuilder, EntityTypeBuilder> EntityBuilder);
internal record AdapterRegistration(Type ObjectDbType, Func<ModelBuilder, EntityTypeBuilder> EntityBuilder);

public interface IObjectAdapter
internal interface IObjectAdapterProvider
{
IEnumerable<AdapterRegistration> GetRegistrations();
IObjectBase Adapt(object obj);
Expand Down
2 changes: 1 addition & 1 deletion src/SIL.Harmony/Changes/ChangeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ internal ChangeContext(Commit commit, SnapshotWorker worker, CrdtConfig crdtConf
public async ValueTask<ObjectSnapshot?> GetSnapshot(Guid entityId) => await _worker.GetSnapshot(entityId);

public async ValueTask<bool> IsObjectDeleted(Guid entityId) => (await GetSnapshot(entityId))?.EntityIsDeleted ?? true;
public IObjectBase Adapt(object obj) => _crdtConfig.ObjectTypeListBuilder.Adapter.Adapt(obj);
public IObjectBase Adapt(object obj) => _crdtConfig.ObjectTypeListBuilder.AdapterProvider.Adapt(obj);
}
47 changes: 30 additions & 17 deletions src/SIL.Harmony/CrdtConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ public void Freeze()
{
if (_frozen) return;
_frozen = true;
JsonTypes = Adapter.JsonTypes;
foreach (var registration in Adapter.GetRegistrations())
JsonTypes = AdapterProvider.JsonTypes;
foreach (var registration in AdapterProvider.GetRegistrations())
{
ModelConfigurations.Add((builder, config) =>
{
Expand All @@ -122,7 +122,7 @@ public void Freeze()
}
}

private void CheckFrozen()
internal void CheckFrozen()
{
if (_frozen) throw new InvalidOperationException($"{nameof(ObjectTypeListBuilder)} is frozen");
}
Expand All @@ -131,27 +131,40 @@ private void CheckFrozen()

internal List<Action<ModelBuilder, CrdtConfig>> ModelConfigurations { get; } = [];

public ObjectTypeListBuilder AddDbModelConfig(Action<ModelBuilder> modelConfiguration)
{
CheckFrozen();
ModelConfigurations.Add((builder, _) => modelConfiguration(builder));
return this;
}
internal IObjectAdapter Adapter => _adapter ?? throw new InvalidOperationException("No adapter has been added to the builder");
private IObjectAdapter? _adapter;
internal IObjectAdapterProvider AdapterProvider => _adapterProvider ?? throw new InvalidOperationException("No adapter has been added to the builder");
private IObjectAdapterProvider? _adapterProvider;

public DefaultAdapter DefaultAdapter()
public DefaultAdapterProvider DefaultAdapter()
{
var adapter = new DefaultAdapter();
_adapter = adapter;
CheckFrozen();
if (_adapterProvider is not null) throw new InvalidOperationException("adapter has already been added");
var adapter = new DefaultAdapterProvider(this);
_adapterProvider = adapter;
return adapter;
}


/// <summary>
/// add a custom adapter for a common interface
/// this is required as CRDT objects must express their references and have an Id property
/// using a custom adapter allows your model to not take a dependency on Harmony
/// </summary>
/// <typeparam name="TCommonInterface">
/// A common interface that all objects in your application implement
/// which System.Text.Json will deserialize your objects to, they must support polymorphic deserialization
/// </typeparam>
/// <typeparam name="TAdapter">
/// This adapter will be serialized and stored in the database,
/// it should include the object it is adapting otherwise Harmony will not work
/// </typeparam>
/// <returns></returns>
/// <exception cref="InvalidOperationException">when another adapter has already been added or the config has been frozen</exception>
public CustomAdapterProvider<TCommonInterface, TAdapter> CustomAdapter<TCommonInterface, TAdapter>()
where TCommonInterface : class where TAdapter : class, ICustomAdapter<TAdapter, TCommonInterface>, IPolyType
{
var adapter = new CustomAdapterProvider<TCommonInterface, TAdapter>();
_adapter = adapter;
CheckFrozen();
if (_adapterProvider is not null) throw new InvalidOperationException("adapter has already been added");
var adapter = new CustomAdapterProvider<TCommonInterface, TAdapter>(this);
_adapterProvider = adapter;
return adapter;
}
}

0 comments on commit 8077a68

Please sign in to comment.