Skip to content

Commit

Permalink
Add source generator for system event mapping/constants (#20873)
Browse files Browse the repository at this point in the history
* Add source generator for system event mapping/constants

* PR FB

* PR FB

* Missing files

* docsettings

* remove code

* Fix build

* changelog

* More fixing

* Use correct property

* DocSettings
  • Loading branch information
JoshLove-msft authored May 6, 2021
1 parent 98311bc commit ab7400c
Show file tree
Hide file tree
Showing 110 changed files with 1,173 additions and 1,002 deletions.
1 change: 1 addition & 0 deletions eng/.docsettings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ known_presence_issues:
- ['sdk/cognitiveservices/Vision.Face','#5499']
- ['sdk/cognitiveservices/Vision.FormRecognizer','#5499']
- ['sdk/containerregistry/Microsoft.Azure.ContainerRegistry','#5499']
- ['sdk/eventgrid/Azure.Messaging.EventGrid/EventGridSourceGenerator','#5499']
- ['sdk/eventgrid/Microsoft.Azure.EventGrid','#5499']
- ['sdk/eventhub/Microsoft.Azure.EventHubs.Processor','#5499']
- ['sdk/eventhub/Microsoft.Azure.EventHubs.ServiceFabricProcessor','#5499']
Expand Down
1 change: 1 addition & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Update="SauceControl.InheritDoc" Version="1.2.0" PrivateAssets="All" />
<PackageReference Update="StyleCop.Analyzers" Version="1.2.0-beta.333" PrivateAssets="All" />
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version ="3.9.0" PrivateAssets="all" />
</ItemGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Messaging.E
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Messaging.EventGrid.CloudNativeCloudEvents.Tests", "..\Microsoft.Azure.Messaging.EventGrid.CloudNativeCloudEvents\tests\Microsoft.Azure.Messaging.EventGrid.CloudNativeCloudEvents.Tests.csproj", "{28B77D76-084F-4C7E-8A1B-631444EED6A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventGridSourceGenerator", "EventGridSourceGenerator\src\EventGridSourceGenerator.csproj", "{FFBE6318-A232-4490-8DB4-2441EA3ED8C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -51,6 +53,10 @@ Global
{28B77D76-084F-4C7E-8A1B-631444EED6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28B77D76-084F-4C7E-8A1B-631444EED6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28B77D76-084F-4C7E-8A1B-631444EED6A2}.Release|Any CPU.Build.0 = Release|Any CPU
{FFBE6318-A232-4490-8DB4-2441EA3ED8C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFBE6318-A232-4490-8DB4-2441EA3ED8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFBE6318-A232-4490-8DB4-2441EA3ED8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFBE6318-A232-4490-8DB4-2441EA3ED8C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Add any shared properties you want for the projects under this package directory that need to be set before the auto imported Directory.Build.props
-->
<PropertyGroup>
<ImportRepoCommonSettings>true</ImportRepoCommonSettings>
<SupportsNetStandard20>true</SupportsNetStandard20>
<IsShippingLibrary>false</IsShippingLibrary>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., Directory.Build.props))\Directory.Build.props" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;

namespace EventGridSourceGenerator
{
/// <summary>
/// This class generates the SystemEventNames constant values as well as the SystemEventExtensions which contains a mapping
/// from constant values to deserialization method for each system event.
/// </summary>
[Generator]
internal class EventGridSourceGenerator : ISourceGenerator
{
private SourceVisitor _visitor;
private const string Indent = " ";

public void Execute(GeneratorExecutionContext context)
{
_visitor = new SourceVisitor();
var root = context.Compilation.GetSymbolsWithName(
"SystemEvents",
SymbolFilter.Namespace)
.Single();
_visitor.Visit(root);

context.AddSource("SystemEventNames.cs", SourceText.From(ConstructSystemEventNames(), Encoding.UTF8));
context.AddSource("SystemEventExtensions.cs", SourceText.From(ConstructSystemEventExtensions(), Encoding.UTF8));
}

public void Initialize(GeneratorInitializationContext context)
{
// Uncomment to debug
//if (!Debugger.IsAttached)
//{
// Debugger.Launch();
//}
}

private string ConstructSystemEventNames()
{
var sourceBuilder = new StringBuilder(
@"// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// <auto-generated/>
using Azure.Messaging.EventGrid.SystemEvents;
namespace Azure.Messaging.EventGrid
{
/// <summary>
/// Represents the names of the various event types for the system events published to
/// Azure Event Grid.
/// </summary>
public static class SystemEventNames
{
");
for (int i = 0; i < _visitor.SystemEvents.Count; i++)
{
if (i > 0)
{
sourceBuilder.AppendLine();
}
SystemEventNode sysEvent = _visitor.SystemEvents[i];

// Add the ref docs for each constant
sourceBuilder.AppendLine($"{Indent}{Indent}/// <summary>");
sourceBuilder.AppendLine($"{Indent}{Indent}/// The value of the Event Type stored in <see cref=\"EventGridEvent.EventType\"/> and <see cref=\"CloudEvent.Type\"/> ");
sourceBuilder.AppendLine($"{Indent}{Indent}/// for the <see cref=\"{sysEvent.EventName}\"/> system event.");
sourceBuilder.AppendLine($"{Indent}{Indent}/// </summary>");

// Add the constant
sourceBuilder.AppendLine($"{Indent}{Indent}public const string {sysEvent.EventConstantName} = {sysEvent.EventType};");
}

sourceBuilder.Append($@"{Indent}}}
}}");
return sourceBuilder.ToString();
}

private string ConstructSystemEventExtensions()
{
var sourceBuilder = new StringBuilder(
@"// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// <auto-generated/>
using System;
using System.Collections.Generic;
using System.Text.Json;
using Azure.Messaging.EventGrid.SystemEvents;
namespace Azure.Messaging.EventGrid
{
internal class SystemEventExtensions
{
public static object AsSystemEventData(string eventType, JsonElement data)
{
if (s_systemEventDeserializers.TryGetValue(eventType, out Func<JsonElement, object> systemDeserializationFunction))
{
return systemDeserializationFunction(data);
}
else
{
return null;
}
}
internal static readonly IReadOnlyDictionary<string, Func<JsonElement, object>> s_systemEventDeserializers = new Dictionary<string, Func<JsonElement, object>>(StringComparer.OrdinalIgnoreCase)
{
");
foreach (SystemEventNode sysEvent in _visitor.SystemEvents)
{
// Add each an entry for each system event to the dictionary containing a mapping from constant name to deserialization method.
sourceBuilder.AppendLine(
$"{Indent}{Indent}{Indent}{{ SystemEventNames.{sysEvent.EventConstantName}, {sysEvent.EventName}.{sysEvent.DeserializeMethod} }},");
}
sourceBuilder.Append($@"{Indent}{Indent}}};
{Indent}}}
}}");
return sourceBuilder.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace EventGridSourceGenerator
{
internal class SourceVisitor : SymbolVisitor
{
public List<SystemEventNode> SystemEvents { get; } = new();

public override void VisitNamespace(INamespaceSymbol symbol)
{
foreach (var childSymbol in symbol.GetMembers())
{
childSymbol.Accept(this);
}
}

public override void VisitNamedType(INamedTypeSymbol symbol)
{
if (symbol.Name.EndsWith("EventData"))
{
string type = null;
XmlDocument xmlDoc = new();
xmlDoc.LoadXml(symbol.GetDocumentationCommentXml());
var xmlNode = xmlDoc.SelectSingleNode("member/summary");
var match = Regex.Match(xmlNode.InnerText, "[a-zA-Z]+\\.[a-zA-Z]+\\.[a-zA-Z]+");
if (!match.Success)
{
// We expect some EventData to not have event types if they are base types,
// e.g. ContainerRegistryEventData
return;
}

type = $@"""{match.Value}""";
SystemEvents.Add(
new SystemEventNode()
{
EventName = symbol.Name,
EventType =
// temporary workaround until https://github.com/Azure/azure-rest-api-specs/pull/14261/ is merged
type == @"""Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListenersEvent""" ?
@"""Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener"""
: type,
DeserializeMethod = symbol.MemberNames.Single(m => m.StartsWith("Deserialize"))
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace EventGridSourceGenerator
{
internal class SystemEventNode
{
public string EventName { get; set; }

public string EventConstantName
{
get
{
// special case a few events that don't follow the pattern
return EventName switch
{
"ServiceBusDeadletterMessagesAvailableWithNoListenersEventData" => "ServiceBusDeadletterMessagesAvailableWithNoListener",
"SubscriptionDeletedEventData" => "EventGridSubscriptionDeleted",
"SubscriptionValidationEventData" => "EventGridSubscriptionValidation",
_ => EventName?.Replace("EventData", ""),
};
}
}

public string EventType { get; set; }

public string DeserializeMethod { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public static partial class SystemEventNames
public const string MediaLiveEventIncomingVideoStreamsOutOfSync = "Microsoft.Media.LiveEventIncomingVideoStreamsOutOfSync";
public const string MediaLiveEventIngestHeartbeat = "Microsoft.Media.LiveEventIngestHeartbeat";
public const string MediaLiveEventTrackDiscontinuityDetected = "Microsoft.Media.LiveEventTrackDiscontinuityDetected";
public const string PolicyInsightsPolicyStateChanged = "Microsoft.PolicyInsights.PolicyStateChanged";
public const string PolicyInsightsPolicyStateCreated = "Microsoft.PolicyInsights.PolicyStateCreated";
public const string PolicyInsightsPolicyStateDeleted = "Microsoft.PolicyInsights.PolicyStateDeleted";
public const string RedisExportRdbCompleted = "Microsoft.Cache.ExportRDBCompleted";
public const string RedisImportRdbCompleted = "Microsoft.Cache.ImportRDBCompleted";
public const string RedisPatchingCompleted = "Microsoft.Cache.PatchingCompleted";
Expand All @@ -154,9 +157,11 @@ public static partial class SystemEventNames
public const string ServiceBusDeadletterMessagesAvailableWithNoListener = "Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener";
public const string SignalRServiceClientConnectionConnected = "Microsoft.SignalRService.ClientConnectionConnected";
public const string SignalRServiceClientConnectionDisconnected = "Microsoft.SignalRService.ClientConnectionDisconnected";
public const string StorageAsyncOperationInitiated = "Microsoft.Storage.AsyncOperationInitiated";
public const string StorageBlobCreated = "Microsoft.Storage.BlobCreated";
public const string StorageBlobDeleted = "Microsoft.Storage.BlobDeleted";
public const string StorageBlobRenamed = "Microsoft.Storage.BlobRenamed";
public const string StorageBlobTierChanged = "Microsoft.Storage.BlobTierChanged";
public const string StorageDirectoryCreated = "Microsoft.Storage.DirectoryCreated";
public const string StorageDirectoryDeleted = "Microsoft.Storage.DirectoryDeleted";
public const string StorageDirectoryRenamed = "Microsoft.Storage.DirectoryRenamed";
Expand Down Expand Up @@ -1105,6 +1110,39 @@ public partial class PhoneNumberIdentifierModel
internal PhoneNumberIdentifierModel() { }
public string Value { get { throw null; } }
}
public partial class PolicyInsightsPolicyStateChangedEventData
{
internal PolicyInsightsPolicyStateChangedEventData() { }
public string ComplianceReasonCode { get { throw null; } }
public string ComplianceState { get { throw null; } }
public string PolicyAssignmentId { get { throw null; } }
public string PolicyDefinitionId { get { throw null; } }
public string PolicyDefinitionReferenceId { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public System.DateTimeOffset? Timestamp { get { throw null; } }
}
public partial class PolicyInsightsPolicyStateCreatedEventData
{
internal PolicyInsightsPolicyStateCreatedEventData() { }
public string ComplianceReasonCode { get { throw null; } }
public string ComplianceState { get { throw null; } }
public string PolicyAssignmentId { get { throw null; } }
public string PolicyDefinitionId { get { throw null; } }
public string PolicyDefinitionReferenceId { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public System.DateTimeOffset? Timestamp { get { throw null; } }
}
public partial class PolicyInsightsPolicyStateDeletedEventData
{
internal PolicyInsightsPolicyStateDeletedEventData() { }
public string ComplianceReasonCode { get { throw null; } }
public string ComplianceState { get { throw null; } }
public string PolicyAssignmentId { get { throw null; } }
public string PolicyDefinitionId { get { throw null; } }
public string PolicyDefinitionReferenceId { get { throw null; } }
public string SubscriptionId { get { throw null; } }
public System.DateTimeOffset? Timestamp { get { throw null; } }
}
public partial class RedisExportRdbCompletedEventData
{
internal RedisExportRdbCompletedEventData() { }
Expand Down Expand Up @@ -1344,6 +1382,20 @@ internal SignalRServiceClientConnectionDisconnectedEventData() { }
public static bool operator !=(Azure.Messaging.EventGrid.SystemEvents.StampKind left, Azure.Messaging.EventGrid.SystemEvents.StampKind right) { throw null; }
public override string ToString() { throw null; }
}
public partial class StorageAsyncOperationInitiatedEventData
{
internal StorageAsyncOperationInitiatedEventData() { }
public string Api { get { throw null; } }
public string BlobType { get { throw null; } }
public string ClientRequestId { get { throw null; } }
public long? ContentLength { get { throw null; } }
public string ContentType { get { throw null; } }
public string Identity { get { throw null; } }
public string RequestId { get { throw null; } }
public string Sequencer { get { throw null; } }
public object StorageDiagnostics { get { throw null; } }
public string Url { get { throw null; } }
}
public partial class StorageBlobCreatedEventData
{
internal StorageBlobCreatedEventData() { }
Expand Down Expand Up @@ -1385,6 +1437,20 @@ internal StorageBlobRenamedEventData() { }
public string SourceUrl { get { throw null; } }
public object StorageDiagnostics { get { throw null; } }
}
public partial class StorageBlobTierChangedEventData
{
internal StorageBlobTierChangedEventData() { }
public string Api { get { throw null; } }
public string BlobType { get { throw null; } }
public string ClientRequestId { get { throw null; } }
public long? ContentLength { get { throw null; } }
public string ContentType { get { throw null; } }
public string Identity { get { throw null; } }
public string RequestId { get { throw null; } }
public string Sequencer { get { throw null; } }
public object StorageDiagnostics { get { throw null; } }
public string Url { get { throw null; } }
}
public partial class StorageDirectoryCreatedEventData
{
internal StorageDirectoryCreatedEventData() { }
Expand Down
Loading

0 comments on commit ab7400c

Please sign in to comment.