Skip to content

Commit c7ea1eb

Browse files
[.NET] Rework how users pass in topic tokens to invokers/executors/senders/receivers (#553)
Previously: - Code gen'd classes publicly exposed several topic token fields that were defined such that users could add to them, but our implementation ignored any changes the user made. - This led to a lot of confusion around how to set topic tokens properly - Custom topic tokens on RPC requests and telemetry could only be set at invoke/publish telemetry time. Not constructor time. - Custom topic tokens could not be set on a telemetry receiver/command executor, so they always subscribed to the DTDL topic with a wildcard for each custom topic token With some suggested requirements from @abhipsaMisra to allow our SDK to be used in the upcoming ADR mRPC service, this proposed design allows for: - Code gen'd client/service classes allow a user to pass in a topic token map at ctor time and it will be used for all requests/subscriptions made by that client/service - Code gen'd command invocation/telemetry send methods additionally take an optional topic token map param that will resolve in addition to the above ctor level token map - Code gen'd telemetry command executor/receivers additionally take an optional topic token map param that will resolve in addition to the above ctor level token map Note that the code gen'd output classes and our command/telemetry base classes still give the user all the "default" topic token evaluation that they would expect (i.e. invokerClientId token is handled for the user automatically). Like before, users are only responsible for adding custom topic tokens in their application layer. Generally, this follows the patterns used by Rust and Go for handling topic tokens --------- Co-authored-by: timtay-microsoft <[email protected]>
1 parent ff51f29 commit c7ea1eb

File tree

104 files changed

+2063
-3056
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+2063
-3056
lines changed

.github/workflows/run-codegen.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ jobs:
1313
steps:
1414
- uses: actions/checkout@v4
1515

16+
- name: Install Protoc
17+
uses: arduino/setup-protoc@v3
18+
19+
- name: Install AvroGen
20+
run: (pushd /; dotnet tool install --global Apache.Avro.Tools; popd) # need to install from root to avoid using the nuget.config we for build and push
21+
1622
- name: Setup .NET 8
1723
uses: actions/setup-dotnet@v4
1824
with:

codegen/src/Azure.Iot.Operations.ProtocolCompiler/T4/communication/dotnet/Command/t4/DotNetCommandExecutor.tt

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,38 +36,18 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
3636
<# } #>
3737
public class <#=this.commandName.GetTypeName(TargetLanguage.CSharp, "command", "executor")#> : CommandExecutor<#=this.TypeParams()#>
3838
{
39-
private CombinedPrefixedReadOnlyDictionary<string> effectiveTopicTokenMap;
40-
41-
/// <summary>
42-
/// Optionally initializes a custom token map to a dictionary that maps token values to replacement strings; defaults to new empty dictionary.
43-
/// </summary>
44-
public Dictionary<string, string> CustomTopicTokenMap { private get; init; } = new();
45-
46-
/// <summary>
47-
/// Gets a dictionary for adding custom token keys and their replacement strings, which will be substituted in request and response topic patterns.
48-
/// Note that keys will automatically be prefixed by "ex:" when used for substitution searches in topic pattern strings.
49-
/// </summary>
50-
public override Dictionary<string, string> TopicTokenMap { get => CustomTopicTokenMap; }
51-
52-
/// <summary>
53-
/// Gets a dictionary used by the base class's code for substituting tokens in request and response topic patterns.
54-
/// </summary>
55-
protected override IReadOnlyDictionary<string, string> EffectiveTopicTokenMap { get => effectiveTopicTokenMap; }
56-
5739
/// <summary>
5840
/// Initializes a new instance of the <see cref="<#=this.commandName.GetTypeName(TargetLanguage.CSharp, "command", "executor")#>"/> class.
5941
/// </summary>
6042
public <#=this.commandName.GetTypeName(TargetLanguage.CSharp, "command", "executor")#>(ApplicationContext applicationContext, IMqttPubSubClient mqttClient)
6143
: base(applicationContext, mqttClient, "<#=this.commandName.AsGiven#>", new <#=string.Format(this.serializerClassName, this.TypeParams())#>())
6244
{
63-
this.effectiveTopicTokenMap = new(string.Empty, (IReadOnlyDictionary<string, string>)base.TopicTokenMap, "ex:", this.CustomTopicTokenMap);
64-
65-
base.TopicTokenMap["modelId"] = "<#=this.modelId#>";
45+
TopicTokenMap["modelId"] = "<#=this.modelId#>";
6646
if (mqttClient.ClientId != null)
6747
{
68-
base.TopicTokenMap["executorId"] = mqttClient.ClientId;
48+
TopicTokenMap["executorId"] = mqttClient.ClientId;
6949
}
70-
base.TopicTokenMap["commandName"] = "<#=this.commandName.AsGiven#>";
50+
TopicTokenMap["commandName"] = "<#=this.commandName.AsGiven#>";
7151
}
7252
}
7353
}

codegen/src/Azure.Iot.Operations.ProtocolCompiler/T4/communication/dotnet/Command/t4/DotNetCommandInvoker.tt

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,6 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
2525
/// </summary>
2626
public class <#=this.commandName.GetTypeName(TargetLanguage.CSharp, "command", "invoker")#> : CommandInvoker<#=this.TypeParams()#>
2727
{
28-
private CombinedPrefixedReadOnlyDictionary<string> effectiveTopicTokenMap;
29-
30-
/// <summary>
31-
/// Optionally initializes a custom token map to a dictionary that maps token values to replacement strings; defaults to new empty dictionary.
32-
/// </summary>
33-
public Dictionary<string, string> CustomTopicTokenMap { private get; init; } = new();
34-
35-
/// <summary>
36-
/// Gets a dictionary for adding custom token keys and their replacement strings, which will be substituted in request and response topic patterns.
37-
/// Note that keys will automatically be prefixed by "ex:" when used for substitution searches in topic pattern strings.
38-
/// </summary>
39-
public override Dictionary<string, string> TopicTokenMap { get => CustomTopicTokenMap; }
40-
41-
/// <summary>
42-
/// Gets a dictionary used by the base class's code for substituting tokens in request and response topic patterns.
43-
/// </summary>
44-
protected override IReadOnlyDictionary<string, string> EffectiveTopicTokenMap { get => effectiveTopicTokenMap; }
45-
4628
/// <summary>
4729
/// Initializes a new instance of the <see cref="<#=this.commandName.GetTypeName(TargetLanguage.CSharp, "command", "invoker")#>"/> class.
4830
/// </summary>
@@ -51,14 +33,12 @@ namespace <#=this.projectName#>.<#=this.genNamespace.GetTypeName(TargetLanguage.
5133
{
5234
this.ResponseTopicPrefix = "clients/{invokerClientId}"; // default value, can be overwritten by user code
5335

54-
this.effectiveTopicTokenMap = new(string.Empty, (IReadOnlyDictionary<string, string>)base.TopicTokenMap, "ex:", this.CustomTopicTokenMap);
55-
56-
base.TopicTokenMap["modelId"] = "<#=this.modelId#>";
36+
TopicTokenMap["modelId"] = "<#=this.modelId#>";
5737
if (mqttClient.ClientId != null)
5838
{
59-
base.TopicTokenMap["invokerClientId"] = mqttClient.ClientId;
39+
TopicTokenMap["invokerClientId"] = mqttClient.ClientId;
6040
}
61-
base.TopicTokenMap["commandName"] = "<#=this.commandName.AsGiven#>";
41+
TopicTokenMap["commandName"] = "<#=this.commandName.AsGiven#>";
6242
}
6343
}
6444
}

0 commit comments

Comments
 (0)