diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..670c71a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# Local files +.local \ No newline at end of file diff --git a/cli/.gitignore b/cli/.gitignore index 1f1d891..3c4efe2 100644 --- a/cli/.gitignore +++ b/cli/.gitignore @@ -1,9 +1,6 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. -# Local files -.local - # User-specific files *.suo *.user diff --git a/cli/Azure Event Grid Utilities.sln b/cli/Azure Event Grid Utilities.sln index 2ab50df..9068372 100644 --- a/cli/Azure Event Grid Utilities.sln +++ b/cli/Azure Event Grid Utilities.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32002.185 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventGrid.Publisher.ConsoleApp", "EventGrid.Publisher.ConsoleApp\EventGrid.Publisher.ConsoleApp.csproj", "{F80F7098-02E9-4C9E-B7CB-A2E3C285BDFA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventGrid.Publisher.ConsoleApp", "EventGrid.Publisher.ConsoleApp\EventGrid.Publisher.ConsoleApp.csproj", "{F80F7098-02E9-4C9E-B7CB-A2E3C285BDFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reduct.Azure.EventGrid", "Reduct.Azure.EventGrid\Reduct.Azure.EventGrid.csproj", "{05661C28-C52C-442E-B47F-E0CDCBFC0C6A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {F80F7098-02E9-4C9E-B7CB-A2E3C285BDFA}.Debug|Any CPU.Build.0 = Debug|Any CPU {F80F7098-02E9-4C9E-B7CB-A2E3C285BDFA}.Release|Any CPU.ActiveCfg = Release|Any CPU {F80F7098-02E9-4C9E-B7CB-A2E3C285BDFA}.Release|Any CPU.Build.0 = Release|Any CPU + {05661C28-C52C-442E-B47F-E0CDCBFC0C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05661C28-C52C-442E-B47F-E0CDCBFC0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05661C28-C52C-442E-B47F-E0CDCBFC0C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05661C28-C52C-442E-B47F-E0CDCBFC0C6A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/cli/EventGrid.Publisher.ConsoleApp/CommandLineParser.cs b/cli/EventGrid.Publisher.ConsoleApp/CommandLineParser.cs index c167a6d..57beecb 100644 --- a/cli/EventGrid.Publisher.ConsoleApp/CommandLineParser.cs +++ b/cli/EventGrid.Publisher.ConsoleApp/CommandLineParser.cs @@ -1,4 +1,5 @@ -using Spectre.Console; +using EventGrid.Publisher.ConsoleApp.Commands; +using Spectre.Console; using System; using System.Collections.Generic; using System.CommandLine; @@ -11,60 +12,13 @@ namespace EventGrid.Publisher.ConsoleApp internal class CommandLineParser { internal RootCommand Command { get; private set; } + public CommandLineParser() { Command = new RootCommand(); Command.Description = "Azure Event Grid Publisher"; - AddOptions(); - } - - private void AddOptions() - { - var topicName = new Option("--topic", "The name of the topic where the event should be sennd to."); - topicName.AddAlias("-t"); - topicName.IsRequired = true; - Command.AddOption(topicName); - - var region = new Option("--region", () => "westeurope-1", "The region where the topic is located. Used to format the url to publish the event."); - region.AddAlias("-r"); - Command.AddOption(region); - - var accessKey = new Option("--accesskey", "The access keys used to authenticate the application to publishing events to this Azure Event Grid Topic."); - accessKey.IsRequired = true; - Command.AddOption(accessKey); - - var filename = new Option("--filename", () => "event.json", "The message that should be send to the Event Grid Topic."); - filename.AddAlias("-f"); - Command.AddOption(filename); - - var overrideEventId = new Option("--override-eventid", () => false, "Indicates if the event id is overwritten with a new id."); - overrideEventId.ArgumentHelpName = "A new guid is generated as an id."; - Command.AddOption(overrideEventId); - - Command.SetHandler(async (string topic, string region, string accesskey, string filename, bool overrideId) => - { - AnsiConsole.MarkupLine($"Publishing event to topic [cyan1]{topic}[/]."); - - FileInfo file = new FileInfo(filename); - - if (!file.Exists) - { - AnsiConsole.MarkupLine($"File with name [cyan1]{filename}[/] was not found."); - return; - } - - var binary = BinaryData.FromBytes(File.ReadAllBytes(file.FullName)); - - AnsiConsole.MarkupLine($"Reading file [cyan1]{file.FullName}[/]."); - - EventPublisher publisher = new EventPublisher(topic, region, accesskey); - - string? id = overrideId ? Guid.NewGuid().ToString() : null; - - await publisher.PublishBinaryDataAsync(binary, id); - - }, topicName, region, accessKey, filename, overrideEventId); + Command.AddCommand(new SendRootCommand()); } } } diff --git a/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFileCommand.cs b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFileCommand.cs new file mode 100644 index 0000000..d96daf4 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFileCommand.cs @@ -0,0 +1,49 @@ +using EventGrid.Publisher.ConsoleApp.Utils; +using Reduct.Azure.EventGrid; +using Spectre.Console; +using System.CommandLine; + +namespace EventGrid.Publisher.ConsoleApp.Commands +{ + internal class SendFileCommand : Command + { + internal SendFileCommand(string? description = null) : base("file", description) + { + AddOptions(); + } + + private void AddOptions() + { + var topicName = new Option("--topic", "The name of the topic where the event should be sennd to."); + topicName.AddAlias("-t"); + topicName.IsRequired = true; + AddOption(topicName); + + var region = new Option("--region", () => "westeurope-1", "The region where the topic is located. Used to format the url to publish the event."); + region.AddAlias("-r"); + AddOption(region); + + var accessKey = new Option("--accesskey", "The access keys used to authenticate the application to publishing events to this Azure Event Grid Topic."); + accessKey.IsRequired = true; + AddOption(accessKey); + + var filename = new Option("--filename", () => "event.json", "The message that should be send to the Event Grid Topic."); + filename.AddAlias("-f"); + AddOption(filename); + + var overrideEventId = new Option("--override-eventid", () => false, "Indicates if the event id is overwritten with a new id."); + overrideEventId.ArgumentHelpName = "A new guid is generated as an id."; + AddOption(overrideEventId); + + this.SetHandler(async (string topic, string region, string accesskey, string filename, bool overrideId) => + { + AnsiConsole.MarkupLine($"Publishing event to topic [cyan1]{topic}[/]."); + AnsiConsole.MarkupLine($"Reading file [cyan1]{filename}[/]."); + + var file = new FileInfo(filename); + await EventSender.SendFileToEventGridAsync(file, topic, accesskey, region, overrideId); + + }, topicName, region, accessKey, filename, overrideEventId); + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFolderCommand.cs b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFolderCommand.cs new file mode 100644 index 0000000..fe8fd52 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendFolderCommand.cs @@ -0,0 +1,70 @@ +using EventGrid.Publisher.ConsoleApp.Options; +using EventGrid.Publisher.ConsoleApp.Utils; +using Reduct.Azure.EventGrid; +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventGrid.Publisher.ConsoleApp.Commands +{ + internal class SendFolderCommand : Command + { + internal SendFolderCommand(string? description = null) : base("folder", description) + { + AddOptions(); + } + + private void AddOptions() + { + var topicName = new Option("--topic", "The name of the topic where the event should be sennd to."); + topicName.AddAlias("-t"); + topicName.IsRequired = true; + AddOption(topicName); + + + var accessKey = new Option("--accesskey", "The access keys used to authenticate the application to publishing events to this Azure Event Grid Topic."); + accessKey.IsRequired = true; + AddOption(accessKey); + + var folder = new Option("--folder", () => "events/", "The folder containing the messages that should be send to the Event Grid Topic."); + folder.AddAlias("-f"); + AddOption(folder); + + var pattern = new Option("--search-pattern", () => "*.json", "The search string to match against the names of files."); + AddOption(pattern); + + var overrideEventId = new Option("--override-eventid", () => false, "Indicates if the event id is overwritten with a new id."); + overrideEventId.ArgumentHelpName = "A new guid is generated as an id."; + AddOption(overrideEventId); + + var region = new Option("--region", () => "westeurope-1", "The region where the topic is located. Used to format the url to publish the event."); + region.AddAlias("-r"); + AddOption(region); + + this.SetHandler(async (string topic, string region, string accesskey, string folder, bool overrideId, string pattern) => + { + AnsiConsole.MarkupLine($"Publishing events from folder [[{folder}]] to topic [cyan1]{topic}[/]."); + + DirectoryInfo di = new DirectoryInfo(folder); + if (!di.Exists) + { + AnsiConsole.MarkupLine($"[{ConsoleColors.Warning}]The message folder [[{di.FullName}]] does not exist.[/]."); + await Task.Delay(100); + return; + } + + AnsiConsole.MarkupLine($"Searching folder [cyan1]{di.FullName}[/]."); + var files = di.GetFiles(pattern); + foreach (var file in files) + { + await EventSender.SendFileToEventGridAsync(file, topic, accesskey, region, overrideId); + } + + }, topicName, region, accessKey, folder, overrideEventId, pattern); + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/Commands/SendRootCommand.cs b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendRootCommand.cs new file mode 100644 index 0000000..657794c --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Commands/SendRootCommand.cs @@ -0,0 +1,13 @@ +using System.CommandLine; + +namespace EventGrid.Publisher.ConsoleApp.Commands +{ + internal class SendRootCommand : Command + { + internal SendRootCommand() : base("send", "Send events to the event grid.") + { + this.AddCommand(new SendFileCommand("Send messages, based on a file, to the event grid.")); + this.AddCommand(new SendFolderCommand("Send all messages from a folder to the event grid.")); + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/ConsoleColors.cs b/cli/EventGrid.Publisher.ConsoleApp/ConsoleColors.cs new file mode 100644 index 0000000..fe72044 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/ConsoleColors.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventGrid.Publisher.ConsoleApp +{ + internal class ConsoleColors + { + internal static string Warning = "gold3_1"; + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/EventGrid.Publisher.ConsoleApp.csproj b/cli/EventGrid.Publisher.ConsoleApp/EventGrid.Publisher.ConsoleApp.csproj index aba3f3b..54dfa9c 100644 --- a/cli/EventGrid.Publisher.ConsoleApp/EventGrid.Publisher.ConsoleApp.csproj +++ b/cli/EventGrid.Publisher.ConsoleApp/EventGrid.Publisher.ConsoleApp.csproj @@ -8,17 +8,20 @@ evgtpub en 1.0.0 - 1.0.0.123 - 1.0.0-beta.1+204ff0a - 1.0.0-beta.1 + 1.0.1.1 + 1.0.1-beta.1+204ff0a + 1.0.1-beta.1 false - + + + + diff --git a/cli/EventGrid.Publisher.ConsoleApp/MetaData.cs b/cli/EventGrid.Publisher.ConsoleApp/MetaData.cs deleted file mode 100644 index 904e810..0000000 --- a/cli/EventGrid.Publisher.ConsoleApp/MetaData.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace EventGrid.Publisher.ConsoleApp -{ - internal class MetaData - { - public MetaData() - { - } - - public string LandingZone { get; set; } - public string AccountName { get; set; } - public string SourcePath { get; set; } - public string SourceType { get; set; } - public string Table { get; set; } - } -} \ No newline at end of file diff --git a/cli/EventGrid.Publisher.ConsoleApp/Options/TopicOption.cs b/cli/EventGrid.Publisher.ConsoleApp/Options/TopicOption.cs new file mode 100644 index 0000000..e407a66 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Options/TopicOption.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventGrid.Publisher.ConsoleApp.Options +{ + internal class TopicOption : Option + { + public TopicOption(Type? argumentType = null, Func? getDefaultValue = null, ArgumentArity arity = default) : + base("--topic", "The name of the topic where the event should be sennd to.", argumentType, getDefaultValue, arity) + { + var topicName = new Option("--topic", "The name of the topic where the event should be sennd to."); + AddAlias("-t"); + IsRequired = true; + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/Program.cs b/cli/EventGrid.Publisher.ConsoleApp/Program.cs index 75d4454..a497a79 100644 --- a/cli/EventGrid.Publisher.ConsoleApp/Program.cs +++ b/cli/EventGrid.Publisher.ConsoleApp/Program.cs @@ -3,8 +3,8 @@ using Spectre.Console; using System.CommandLine; using System.Diagnostics; -using System.Reflection; +//Debugger.Launch(); VersionInfo.PrintVerionInfo(); Console.WriteLine(); diff --git a/cli/EventGrid.Publisher.ConsoleApp/Utils/EventSender.cs b/cli/EventGrid.Publisher.ConsoleApp/Utils/EventSender.cs new file mode 100644 index 0000000..52f5e27 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Utils/EventSender.cs @@ -0,0 +1,39 @@ +using Reduct.Azure.EventGrid; +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventGrid.Publisher.ConsoleApp.Utils +{ + internal class EventSender + { + internal static async Task SendFileToEventGridAsync(FileInfo file, string topic, string accesskey, string region = "westeurope-1", bool overrideId = false) + { + try + { + AnsiConsole.MarkupLine($"File found [cyan1]{file.FullName}[/]."); + var binary = FileReader.ReadFile(file.FullName); + string? id = overrideId ? Guid.NewGuid().ToString() : null; + EventPublisher publisher = new EventPublisher(topic, region, accesskey); + + AnsiConsole.MarkupLine($"Publishing message."); + await publisher.PublishBinaryDataAsync(binary, id); + } + catch (ArgumentNullException ex) + { + AnsiConsole.MarkupLine($"[{ConsoleColors.Warning}]File [[{file.FullName}]] containts invalid event message. [[{ex.Message}]][/]."); + } + catch (InvalidOperationException ex) + { + AnsiConsole.MarkupLine($"[{ConsoleColors.Warning}]Unable to process file [[{file.FullName}]]. [[{ex.Message}]][/]."); + } + finally + { + AnsiConsole.MarkupLine($""); + } + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/Utils/FileReader.cs b/cli/EventGrid.Publisher.ConsoleApp/Utils/FileReader.cs new file mode 100644 index 0000000..a571572 --- /dev/null +++ b/cli/EventGrid.Publisher.ConsoleApp/Utils/FileReader.cs @@ -0,0 +1,31 @@ +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventGrid.Publisher.ConsoleApp.Utils +{ + internal class FileReader + { + /// + /// + /// + /// + /// + /// + internal static BinaryData ReadFile(string filename) + { + FileInfo file = new FileInfo(filename); + + if (!file.Exists) + { + AnsiConsole.MarkupLine($"File with name [cyan1]{filename}[/] was not found."); + throw new FileNotFoundException("File not found.", filename); + } + + return BinaryData.FromBytes(File.ReadAllBytes(file.FullName)); + } + } +} diff --git a/cli/EventGrid.Publisher.ConsoleApp/VersionInfo.cs b/cli/EventGrid.Publisher.ConsoleApp/VersionInfo.cs index eef31d7..ab7b2ae 100644 --- a/cli/EventGrid.Publisher.ConsoleApp/VersionInfo.cs +++ b/cli/EventGrid.Publisher.ConsoleApp/VersionInfo.cs @@ -18,7 +18,9 @@ public static void PrintVerionInfo() var path = Path.Combine(AppContext.BaseDirectory, "evgtpub.exe"); var fv = FileVersionInfo.GetVersionInfo(path); //.ProductVersion; - AnsiConsole.MarkupLine($"Event Grid Publisher - [lightgoldenrod2_1]{fv.FileMajorPart}.{fv.FileMinorPart}.{fv.FileBuildPart}[/] - Part of the [cyan1]Azure Utilities Collection[/]"); + AnsiConsole.MarkupLine(""); + AnsiConsole.MarkupLine($"Event Grid Publisher - [lightgoldenrod2_1]{fv.FileMajorPart}.{fv.FileMinorPart}.{fv.FileBuildPart}[/]"); + AnsiConsole.MarkupLine($"Part of the [cyan1]Azure Utilities Collection[/]"); AnsiConsole.MarkupLine($"[dim]Build info - {fv.ProductVersion}[/]"); } } diff --git a/cli/EventGrid.Publisher.ConsoleApp/EventPublisher.cs b/cli/Reduct.Azure.EventGrid/EventPublisher.cs similarity index 81% rename from cli/EventGrid.Publisher.ConsoleApp/EventPublisher.cs rename to cli/Reduct.Azure.EventGrid/EventPublisher.cs index 72cc4b4..87651aa 100644 --- a/cli/EventGrid.Publisher.ConsoleApp/EventPublisher.cs +++ b/cli/Reduct.Azure.EventGrid/EventPublisher.cs @@ -6,9 +6,9 @@ using System.Text; using System.Threading.Tasks; -namespace EventGrid.Publisher.ConsoleApp +namespace Reduct.Azure.EventGrid { - internal class EventPublisher + public class EventPublisher { EventGridPublisherClient client; @@ -20,7 +20,7 @@ public EventPublisher(string topicName, string region, string accessKey) ); } - internal async Task PublishBinaryDataAsync(BinaryData eventBinary, string? eventId = null) + public async Task PublishBinaryDataAsync(BinaryData eventBinary, string? eventId = null) { var eventData = EventGridEvent.Parse(eventBinary); diff --git a/cli/Reduct.Azure.EventGrid/Reduct.Azure.EventGrid.csproj b/cli/Reduct.Azure.EventGrid/Reduct.Azure.EventGrid.csproj new file mode 100644 index 0000000..7ad3675 --- /dev/null +++ b/cli/Reduct.Azure.EventGrid/Reduct.Azure.EventGrid.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/iac/deploy.ps1 b/iac/deploy.ps1 new file mode 100644 index 0000000..c76e849 --- /dev/null +++ b/iac/deploy.ps1 @@ -0,0 +1,8 @@ +if (!$env:IAC_SUBSCRIPTION_ID) { + Write-Host "Missing IAC_SUBSCRIPTION_ID Environmental variable" -ForegroundColor Red + exit 1 +} + +az deployment sub create -c --subscription $env:IAC_SUBSCRIPTION_ID ` + --location "West Europe" ` + --template-file main.bicep \ No newline at end of file diff --git a/iac/main.bicep b/iac/main.bicep new file mode 100644 index 0000000..2019fa4 --- /dev/null +++ b/iac/main.bicep @@ -0,0 +1,31 @@ +targetScope = 'subscription' + +@description('Specifies the location for all resources.') +param location string = 'westeurope' + +@description('Specifies the location for all resources.') +param resourceGroupName string = 'rg-eventgridpublisher-test' + +@description('Specifies the location for all resources.') +param environment string = 'dev' + +module resourceGroup 'modules/resourcegroup.bicep' = { + name: 'Azure.Demos.EventGrid.ResourceGroup' + scope: az.subscription() + params: { + groupName: resourceGroupName + location: location + } +} + +module topicAndQueue 'modules/eventtopicandqueue.bicep' = { + name: 'Azure.Demos.EventGrid.TopicAndQueue' + scope: az.resourceGroup(resourceGroupName) + params: { + location: location + environment: environment + } + dependsOn: [ + resourceGroup + ] +} diff --git a/iac/modules/eventtopicandqueue.bicep b/iac/modules/eventtopicandqueue.bicep new file mode 100644 index 0000000..187a0cb --- /dev/null +++ b/iac/modules/eventtopicandqueue.bicep @@ -0,0 +1,50 @@ +targetScope = 'resourceGroup' + +@description('Specifies the location for all resources.') +param location string + +param environment string + +var uniquePart = substring(uniqueString(az.subscription().id), 0, 4) + +resource eventGrid 'Microsoft.EventGrid/topics@2021-12-01' = { + name: 'egt-${environment}' + location: location +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: 'sa${environment}${uniquePart}' + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource queueService 'Microsoft.Storage/storageAccounts/queueServices@2021-08-01' = { + name: 'default' + parent: storage +} + +resource symbolicname 'Microsoft.Storage/storageAccounts/queueServices/queues@2021-08-01' = { + name: 'sq-eventgrid-publisher' + parent: queueService + properties: { + metadata: {} + } +} + +resource eventSubscription 'Microsoft.EventGrid/eventSubscriptions@2021-12-01' = { + name: 'es-${storage.name}' + scope: eventGrid + properties: { + destination: { + endpointType: 'StorageQueue' + properties: { + queueMessageTimeToLiveInSeconds: 1000 + queueName: symbolicname.name + resourceId: storage.id + } + } + } +} diff --git a/iac/modules/resourcegroup.bicep b/iac/modules/resourcegroup.bicep new file mode 100644 index 0000000..c3e4f3f --- /dev/null +++ b/iac/modules/resourcegroup.bicep @@ -0,0 +1,12 @@ +targetScope = 'subscription' + +@description('Specifies the location for all resources.') +param location string + +@description('Specifies the location for all resources.') +param groupName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: groupName + location: location +}