Skip to content

Commit

Permalink
refactor rich transient coding style
Browse files Browse the repository at this point in the history
- add find target from initializer convention
- change convention orders to execute before entity and command conventions
- edit routing rich transients samples and test
  • Loading branch information
dncsvr committed Oct 28, 2024
1 parent d29e94b commit ccd58ce
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Baked.Business;
using Baked.RestApi.Configuration;

namespace Baked.CodingStyle.RichTransient;

public class FindTargetFromInitializerConvention : IApiModelConvention<ActionModelContext>
{
public void Apply(ActionModelContext context)
{
if (!context.Controller.MappedType.TryGetMembers(out var members)) { return; }
if (!members.Methods.Having<InitializerAttribute>().Any()) { return; }
if (context.Action.MappedMethod is null) { return; }
if (context.Action.MappedMethod.Has<InitializerAttribute>()) { return; }

var targetParameter = context.Action.Parameter["target"];
targetParameter.Name = "newTarget";
targetParameter.Type = $"Func<{targetParameter.Type}>";

var initializer = members.Methods.Having<InitializerAttribute>().Single();
context.Action.FindTargetStatement = $"newTarget().{initializer.Name}({initializer.DefaultOverload.Parameters.Select(p => $"@{p.Name}").Join(", ")})";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ public class InitializeUsingIdParameterConvention(DomainModel _domain)
{
public void Apply(ActionModelContext context)
{
if (!context.Controller.MappedType.TryGetMetadata(out var metadata)) { return; }
if (!metadata.Has<LocatableAttribute>()) { return; }
if (context.Controller.MappedType.TryGetQueryType(_domain, out var _)) { return; }
if (!context.Controller.MappedType.TryGetMembers(out var members)) { return; }
if (!members.Methods.Having<InitializerAttribute>().Any()) { return; }
if (!members.Has<LocatableAttribute>()) { return; }
if (context.Controller.MappedType.TryGetQueryType(_domain, out var _)) { return; }
if (context.Action.MappedMethod is null) { return; }
if (context.Action.MappedMethod.Has<InitializerAttribute>()) { return; }

Expand All @@ -27,15 +27,9 @@ public void Apply(ActionModelContext context)
IsOptional = parameter.IsOptional,
DefaultValue = parameter.DefaultValue,
IsInvokeMethodParameter = false,
RoutePosition = 1
RoutePosition = 1,
AdditionalAttributes = [$"SwaggerSchema(\"Unique value to initialize {context.Controller.MappedType.Name.Humanize().ToLowerInvariant()} resource\")"]
};

var targetParameter = context.Action.Parameter["target"];
targetParameter.Name = "newTarget";
targetParameter.Type = $"Func<{targetParameter.Type}>";

context.Action.RouteParts.RemoveAt(0);
context.Action.RouteParts.Insert(0, context.Controller.MappedType.Name.Pluralize());
context.Action.FindTargetStatement = $"newTarget().{initializer.Name}({initializer.DefaultOverload.Parameters.Select(p => $"@{p.Name}").Join(", ")})";
context.Action.RouteParts = [context.Controller.MappedType.Name.Pluralize(), context.Action.Name];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ public class InitializeUsingQueryParametersConvention : IApiModelConvention<Acti
public void Apply(ActionModelContext context)
{
if (!context.Controller.MappedType.TryGetMembers(out var members)) { return; }
if (members.Has<LocatableAttribute>()) { return; }
if (!members.Methods.Having<InitializerAttribute>().Any()) { return; }
if (members.Has<LocatableAttribute>()) { return; }

var initializer = members.Methods.Having<InitializerAttribute>().Single();
foreach (var parameter in initializer.DefaultOverload.Parameters)
Expand All @@ -23,11 +23,5 @@ public void Apply(ActionModelContext context)
IsInvokeMethodParameter = false
};
}

var targetParameter = context.Action.Parameter["target"];
targetParameter.Name = "newTarget";
targetParameter.Type = $"Func<{targetParameter.Type}>";

context.Action.FindTargetStatement = $"newTarget().{initializer.Name}({initializer.DefaultOverload.Parameters.Select(p => $"@{p.Name}").Join(", ")})";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ public void Configure(LayerConfigurator configurator)
{
configurator.ConfigureDomainModelBuilder(builder =>
{
builder.Conventions.AddTypeMetadata(new LocatableAttribute(),
builder.Conventions.AddTypeMetadata(
apply: (c, add) =>
{
add(c.Type, new ApiInputAttribute());
add(c.Type, new LocatableAttribute());
},
when: c =>
c.Type.IsClass && !c.Type.IsAbstract &&
c.Type.TryGetMembers(out var members) &&
Expand Down Expand Up @@ -45,9 +50,10 @@ public void Configure(LayerConfigurator configurator)
{
var domainModel = configurator.Context.GetDomainModel();
conventions.Add(new InitializeUsingQueryParametersConvention(), order: -10);
conventions.Add(new InitializeUsingIdParameterConvention(domainModel));
conventions.Add(new RichTransientInitializerIsGetResourceConvention(domainModel));
conventions.Add(new FindTargetFromInitializerConvention(), order: -30);
conventions.Add(new InitializeUsingQueryParametersConvention(), order: -30);
conventions.Add(new InitializeUsingIdParameterConvention(domainModel), order: -30);
conventions.Add(new RichTransientInitializerIsGetResourceConvention(), order: -30);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
using Baked.Business;
using Baked.Domain.Model;
using Baked.RestApi.Configuration;
using Humanizer;

namespace Baked.CodingStyle.RichTransient;

public class RichTransientInitializerIsGetResourceConvention(DomainModel _domain)
: IApiModelConvention<ActionModelContext>
public class RichTransientInitializerIsGetResourceConvention : IApiModelConvention<ActionModelContext>
{
public void Apply(ActionModelContext context)
{
if (!context.Controller.MappedType.TryGetMetadata(out var metadata)) { return; }
if (!metadata.Has<LocatableAttribute>()) { return; }
if (context.Controller.MappedType.TryGetQueryType(_domain, out var _)) { return; }
if (!metadata.Has<HasPublicDataAttribute>()) { return; }
if (context.Action.MappedMethod is null) { return; }
if (!context.Action.MappedMethod.Has<InitializerAttribute>()) { return; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,27 @@ namespace Baked.Test.CodingStyle;

public class RoutingRichTransients : TestServiceNfr
{
[Test]
public async Task Get()
[TestCase("1")]
[TestCase("59dfa608-9fe4-4e77-b448-a65adcfda605")]
public async Task Get(string id)
{
var response = await Client.GetAsync("rich-transient-with-datas/1");
var response = await Client.GetAsync($"rich-transient-with-datas/{id}");

response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
dynamic? actual = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());

((string?)actual?.id).ShouldBe("1");
((string?)actual?.id).ShouldBe(id);
}

[TestCase("rich-transient-with-datas/1/method")]
[TestCase("rich-transient-no-datas/1/method")]
public async Task Post(string path)
{
var response = await Client.PostAsync(path, JsonContent.Create(new { data = "text" }));
var response = await Client.PostAsync(path, JsonContent.Create(new { text = "text" }));

response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
dynamic? actual = JsonConvert.DeserializeObject<dynamic>(await response.Content.ReadAsStringAsync());
((string?)actual?.id).ShouldBe("1");
((object?)actual?.data).ShouldDeeplyBe(new { text = "text" });
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public RichTransientNoData With(string id)
internal string Id { get; set; } = default!;

public object Method(object data) =>
data;
new { Id, data };
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public RichTransientWithData With(string id)
public string Id { get; set; } = default!;

public object Method(object data) =>
data;
new { Id, data };
}

0 comments on commit ccd58ce

Please sign in to comment.