Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,13 @@ private void AddDeserializeMethod(
.SetType(TypeNames.JsonElement.MakeNullable());
}

var isNonNull = typeReference.IsNonNull();

var jsonElementNullCheck = IfBuilder
.New()
.SetCondition($"!{Obj}.HasValue")
.AddCode(
typeReference.IsNonNull()
isNonNull
? ExceptionBuilder.New(TypeNames.ArgumentNullException)
: CodeLineBuilder.From("return null;"));

Expand All @@ -228,23 +230,24 @@ private void AddDeserializeMethod(
.New()
.SetCondition($"{Obj}.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null")
.AddCode(
typeReference.IsNonNull()
isNonNull
? ExceptionBuilder.New(TypeNames.ArgumentNullException)
: CodeLineBuilder.From("return null;"));

methodBuilder
.AddCode(jsonElementNullValueKindCheck)
.AddEmptyLine();

AddDeserializeMethodBody(classBuilder, methodBuilder, typeReference, processed);
AddDeserializeMethodBody(classBuilder, methodBuilder, typeReference, processed, isNonNull);
}
}

private void AddDeserializeMethodBody(
ClassBuilder classBuilder,
MethodBuilder methodBuilder,
ITypeDescriptor typeDescriptor,
HashSet<string> processed)
HashSet<string> processed,
bool isNonNull)
{
switch (typeDescriptor)
{
Expand All @@ -261,30 +264,43 @@ private void AddDeserializeMethodBody(
classBuilder,
methodBuilder,
d,
processed);
processed,
isNonNull);
break;

case ComplexTypeDescriptor { Kind: TypeKind.AbstractData } d:
AddDataTypeDeserializerMethod(classBuilder, methodBuilder, d, processed);
AddDataTypeDeserializerMethod(classBuilder, methodBuilder, d, processed, isNonNull);
break;

case ComplexTypeDescriptor { Kind: TypeKind.Data } d:
AddDataTypeDeserializerMethod(classBuilder, methodBuilder, d, processed);
AddDataTypeDeserializerMethod(classBuilder, methodBuilder, d, processed, isNonNull);
break;

case INamedTypeDescriptor { Kind: TypeKind.Entity } d:
AddUpdateEntityMethod(classBuilder, methodBuilder, d, processed);
AddUpdateEntityMethod(classBuilder, methodBuilder, d, processed, isNonNull);
break;

case NonNullTypeDescriptor d:
AddDeserializeMethodBody(classBuilder, methodBuilder, d.InnerType, processed);
AddDeserializeMethodBody(classBuilder, methodBuilder, d.InnerType, processed, isNonNull);
break;

default:
throw new ArgumentOutOfRangeException(nameof(typeDescriptor));
}
}

/// <summary>
/// Builds the fallback that is reached when the transport returns a concrete
/// __typename that is unknown to the generated abstract type mapping (for example
/// a union or interface member that was added to the server after the client was
/// generated). For a nullable position the unknown value degrades to <c>null</c>,
/// for a non-null position it remains an error.
/// </summary>
private static ICode CreateUnknownTypeFallback(bool isNonNull)
=> isNonNull
? ExceptionBuilder.New(TypeNames.NotSupportedException)
: CodeLineBuilder.From("return null;");

private static MethodCallBuilder BuildUpdateMethodCall(PropertyDescriptor property)
{
var propertyAccessor = MethodCallBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ private void AddDataTypeDeserializerMethod(
ClassBuilder classBuilder,
MethodBuilder methodBuilder,
ComplexTypeDescriptor complexTypeDescriptor,
HashSet<string> processed)
HashSet<string> processed,
bool isNonNull)
{
if (complexTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
{
AddInterfaceDataTypeDeserializerToMethod(methodBuilder, interfaceTypeDescriptor);
AddInterfaceDataTypeDeserializerToMethod(
methodBuilder,
interfaceTypeDescriptor,
isNonNull);
}
else
{
Expand All @@ -42,7 +46,8 @@ private void AddDataTypeDeserializerMethod(

private void AddInterfaceDataTypeDeserializerToMethod(
MethodBuilder methodBuilder,
InterfaceTypeDescriptor interfaceTypeDescriptor)
InterfaceTypeDescriptor interfaceTypeDescriptor,
bool isNonNull)
{
methodBuilder.AddCode(
AssignmentBuilder
Expand Down Expand Up @@ -77,7 +82,7 @@ private void AddInterfaceDataTypeDeserializerToMethod(

methodBuilder
.AddEmptyLine()
.AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
.AddCode(CreateUnknownTypeFallback(isNonNull));
}

private MethodCallBuilder CreateBuildDataStatement(ObjectTypeDescriptor concreteType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ private void AddEntityOrDataTypeDeserializerMethod(
ClassBuilder classBuilder,
MethodBuilder methodBuilder,
ComplexTypeDescriptor complexTypeDescriptor,
HashSet<string> processed)
HashSet<string> processed,
bool isNonNull)
{
if (complexTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
{
AddEntityDataTypeDeserializerToMethod(methodBuilder, interfaceTypeDescriptor);
AddEntityDataTypeDeserializerToMethod(
methodBuilder,
interfaceTypeDescriptor,
isNonNull);
}
else
{
Expand All @@ -28,7 +32,8 @@ private void AddEntityOrDataTypeDeserializerMethod(

private void AddEntityDataTypeDeserializerToMethod(
MethodBuilder methodBuilder,
InterfaceTypeDescriptor interfaceTypeDescriptor)
InterfaceTypeDescriptor interfaceTypeDescriptor,
bool isNonNull)
{
methodBuilder.AddCode(
AssignmentBuilder
Expand Down Expand Up @@ -92,6 +97,6 @@ private void AddEntityDataTypeDeserializerToMethod(

methodBuilder
.AddEmptyLine()
.AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
.AddCode(CreateUnknownTypeFallback(isNonNull));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text.Json;
using StrawberryShake.CodeGeneration.CSharp.Builders;
using StrawberryShake.CodeGeneration.Descriptors.TypeDescriptors;
using StrawberryShake.CodeGeneration.Extensions;
Expand All @@ -15,42 +16,41 @@ private void AddUpdateEntityMethod(
ClassBuilder classBuilder,
MethodBuilder methodBuilder,
INamedTypeDescriptor namedTypeDescriptor,
HashSet<string> processed)
HashSet<string> processed,
bool isNonNull)
{
methodBuilder.AddCode(
AssignmentBuilder
.New()
.SetLeftHandSide($"{TypeNames.EntityId} {EntityId}")
.SetRightHandSide(
MethodCallBuilder
.Inline()
.SetMethodName(GetFieldName(IdSerializer), "Parse")
.AddArgument($"{Obj}.Value")));

methodBuilder.AddCode(
MethodCallBuilder
.New()
.SetMethodName(EntityIds, nameof(List<object>.Add))
.AddArgument(EntityId));

methodBuilder.AddEmptyLine();

if (namedTypeDescriptor is InterfaceTypeDescriptor interfaceTypeDescriptor)
{
// If the type is an interface
// If the type is an interface we first read the concrete __typename and
// only parse the entity id for a member that is known to this client. A
// member that is unknown (for example one added to the server after the
// client was generated) degrades to the fallback instead of failing the
// whole result.
methodBuilder.AddCode(
AssignmentBuilder
.New()
.SetLeftHandSide($"var {Typename}")
.SetRightHandSide(MethodCallBuilder
.Inline()
.SetMethodName(Obj, "Value", nameof(JsonElement.GetProperty))
.AddArgument(WellKnownNames.TypeName.AsStringToken())
.Chain(x => x.SetMethodName(nameof(JsonElement.GetString)))));

foreach (var concreteType in interfaceTypeDescriptor.ImplementedBy)
{
methodBuilder
.AddEmptyLine()
.AddCode(CreateUpdateEntityStatement(concreteType)
.AddCode($"return {EntityId};"));
.AddCode(CreateUpdateEntityByTypenameStatement(concreteType));
}

methodBuilder.AddEmptyLine();
methodBuilder.AddCode(ExceptionBuilder.New(TypeNames.NotSupportedException));
methodBuilder.AddCode(CreateUnknownTypeFallback(isNonNull));
}
else if (namedTypeDescriptor is ObjectTypeDescriptor objectTypeDescriptor)
{
methodBuilder.AddCode(CreateParseEntityIdStatement());
methodBuilder.AddEmptyLine();

BuildTryGetEntityIf(
CreateEntityType(
objectTypeDescriptor.Name,
Expand All @@ -65,6 +65,22 @@ private void AddUpdateEntityMethod(
AddRequiredDeserializeMethods(namedTypeDescriptor, classBuilder, processed);
}

private static ICode CreateParseEntityIdStatement()
=> CodeBlockBuilder
.New()
.AddCode(AssignmentBuilder
.New()
.SetLeftHandSide($"{TypeNames.EntityId} {EntityId}")
.SetRightHandSide(
MethodCallBuilder
.Inline()
.SetMethodName(GetFieldName(IdSerializer), "Parse")
.AddArgument($"{Obj}.Value")))
.AddCode(MethodCallBuilder
.New()
.SetMethodName(EntityIds, nameof(List<object>.Add))
.AddArgument(EntityId));

private IfBuilder CreateUpdateEntityStatement(
ObjectTypeDescriptor concreteType)
{
Expand All @@ -90,6 +106,31 @@ private IfBuilder CreateUpdateEntityStatement(
.AddEmptyLine();
}

private IfBuilder CreateUpdateEntityByTypenameStatement(
ObjectTypeDescriptor concreteType)
{
var ifStatement = IfBuilder
.New()
.SetCondition(
$"{Typename}?.Equals(\"{concreteType.Name}\", "
+ $"{TypeNames.OrdinalStringComparison}) ?? false");

var entityTypeName = CreateEntityType(
concreteType.Name,
concreteType.RuntimeType.NamespaceWithoutGlobal);

var ifBuilder = BuildTryGetEntityIf(entityTypeName)
.AddCode(CreateEntityConstructorCall(concreteType, false))
.AddElse(CreateEntityConstructorCall(concreteType, true));

return ifStatement
.AddCode(CreateParseEntityIdStatement())
.AddEmptyLine()
.AddCode(ifBuilder)
.AddEmptyLine()
.AddCode($"return {EntityId};");
}

private static ICode CreateEntityConstructorCall(
ObjectTypeDescriptor objectType,
bool assignDefault)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ public GetFooBuilder(global::StrawberryShake.IEntityStore entityStore, global::S
return new global::StrawberryShake.EntityIdOrData(new global::StrawberryShake.CodeGeneration.CSharp.Integration.EntityIdOrData.State.Quox2Data(typename, foo: Deserialize_String(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "foo"))));
}

throw new global::System.NotSupportedException();
return null;
}

private global::System.String? Deserialize_String(global::System.Text.Json.JsonElement? obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1997,10 +1997,11 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return null;
}

global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (entityId.Name.Equals("Droid", global::System.StringComparison.Ordinal))
var typename = obj.Value.GetProperty("__typename").GetString();
if (typename?.Equals("Droid", global::System.StringComparison.Ordinal) ?? false)
{
global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.DroidEntity? entity))
{
session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.DroidEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "name")), Deserialize_IGetHero_Hero_Friends(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends"), entityIds)));
Expand All @@ -2013,8 +2014,10 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return entityId;
}

if (entityId.Name.Equals("Human", global::System.StringComparison.Ordinal))
if (typename?.Equals("Human", global::System.StringComparison.Ordinal) ?? false)
{
global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.HumanEntity? entity))
{
session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.HumanEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "name")), Deserialize_IGetHero_Hero_Friends(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends"), entityIds)));
Expand All @@ -2027,7 +2030,7 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return entityId;
}

throw new global::System.NotSupportedException();
return null;
}

private global::System.String Deserialize_NonNullableString(global::System.Text.Json.JsonElement? obj)
Expand Down Expand Up @@ -2063,7 +2066,7 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData(typename, nodes: Update_IGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds));
}

throw new global::System.NotSupportedException();
return null;
}

private global::System.Collections.Generic.IReadOnlyList<global::StrawberryShake.EntityId?>? Update_IGetHero_Hero_Friends_NodesEntityArray(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet<global::StrawberryShake.EntityId> entityIds)
Expand Down Expand Up @@ -2099,10 +2102,11 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return null;
}

global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (entityId.Name.Equals("Droid", global::System.StringComparison.Ordinal))
var typename = obj.Value.GetProperty("__typename").GetString();
if (typename?.Equals("Droid", global::System.StringComparison.Ordinal) ?? false)
{
global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.DroidEntity? entity))
{
session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.DroidEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "name")), entity.Friends));
Expand All @@ -2115,8 +2119,10 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return entityId;
}

if (entityId.Name.Equals("Human", global::System.StringComparison.Ordinal))
if (typename?.Equals("Human", global::System.StringComparison.Ordinal) ?? false)
{
global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value);
entityIds.Add(entityId);
if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.HumanEntity? entity))
{
session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.HumanEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "name")), entity.Friends));
Expand All @@ -2129,7 +2135,7 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global::
return entityId;
}

throw new global::System.NotSupportedException();
return null;
}
}

Expand Down
Loading
Loading