Skip to content

Commit

Permalink
Command logging as event stream
Browse files Browse the repository at this point in the history
  • Loading branch information
MerrionComputing committed Apr 8, 2018
1 parent 6f44082 commit 8a804cf
Show file tree
Hide file tree
Showing 30 changed files with 1,028 additions and 143 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,6 @@ __pycache__/

# Config settings
local.settings.json

#publish profiles
*.pubxml
15 changes: 15 additions & 0 deletions TheLongRun-Leagues-Function/Commands/EchoContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Azure.WebJobs.Host;
using TheLongRun.Common;
using TheLongRun.Common.Bindings;

namespace TheLongRunLeaguesFunction.Commands
{
Expand Down Expand Up @@ -32,6 +33,20 @@ public static void EchoContent([EventGridTrigger] EventGridEvent eventGridEvent,
parameters);

log.Info($"Command unique identifier : {cmdRecord.CommandUniqueIdentifier} ");


EventStream outputStream = new EventStream("Leagues", "League", cmdRecord.Parameters.LeagueName);
if (null != outputStream)
{
log.Info($"Writing to event stream {outputStream.DomainName}.{outputStream.AggregateTypeName}.{outputStream.AggregateInstanceKey} ");

// create a new "League created" event
Leagues.League.eventDefinition.Formed evtFormed = new Leagues.League.eventDefinition.Formed(cmdRecord.Parameters.Date_Incorporated,
cmdRecord.Parameters.Location,
$"League : {cmdRecord.Parameters.LeagueName} contactable by email {cmdRecord.Parameters.Email_Address} or twitter {cmdRecord.Parameters.Twitter_Handle } ");

outputStream.AppendEvent(evtFormed);
}
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ public static partial class CommandHandler
public static void CreateLeagueCommandHandler(
[BlobTrigger("command-log/create-league/{name}",
Connection = "CommandStorageConnectionAppSetting")]Stream myBlob,
string name,
[EventStream("Leagues", "League", "{name}")] EventStream eventOut,
string name,
TraceWriter log)
{

Expand Down Expand Up @@ -72,20 +71,16 @@ public static void CreateLeagueCommandHandler(
}
#endregion

// Process the command - create a new league with the given name...
if (null != eventOut)
// Post an "League incorporated" event
EventStream outputStream = new EventStream("Leagues", "League", cmdRecord.Parameters.LeagueName);
if (null != outputStream)
{
// create a new "League created" event
Leagues.League.eventDefinition.Formed evtFormed = new Leagues.League.eventDefinition.Formed(cmdRecord.Parameters.Date_Incorporated,
cmdRecord.Parameters.Location,
$"League : {cmdRecord.Parameters.LeagueName} contactable by email {cmdRecord.Parameters.Email_Address} or twitter {cmdRecord.Parameters.Twitter_Handle } ");

}
else
{
#region Logging
if (null != log)
{
log.Error ($"Unable to create event stream for 'eventOut' ",
source: "CreateLeagueCommandHandler");
}
#endregion
outputStream.AppendEvent(evtFormed);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Leagues.League.commandDefinition;
using TheLongRun.Common;
using TheLongRun.Common.Attributes;
using TheLongRun.Common.Bindings;

namespace TheLongRunLeaguesFunction
{
Expand Down Expand Up @@ -36,7 +37,6 @@ public static partial class Command
[FunctionName("OnCreateLeagueCommand")]
public static async void OnCreateLeagueCommand(
[EventGridTrigger] EventGridEvent eventGridEvent,
Binder commandLog,
TraceWriter log
)
{
Expand Down Expand Up @@ -70,57 +70,27 @@ TraceWriter log
parameters);


// And write this command log to a new file on the command-log blob container
var commandLogBlogAttribute = new BlobAttribute(CommandLogRecord.MakeFullPath(
CommandLogRecord.DEFAULT_CONTAINER_NAME,
COMMAND_NAME,cmdRecord),
FileAccess.Write);

EventStream commandEvents = new EventStream(@"Command",
COMMAND_NAME,
cmdRecord.CommandUniqueIdentifier.ToString());
if (null != commandEvents )
{
commandEvents.AppendEvent(new TheLongRun.Common.Events.Command.CommandCreated(COMMAND_NAME,
cmdRecord.CommandUniqueIdentifier));

// Log the parameters
commandEvents.AppendEvent(new TheLongRun.Common.Events.Command.ParameterValueSet(nameof(parameters.LeagueName), parameters.LeagueName));
commandEvents.AppendEvent(new TheLongRun.Common.Events.Command.ParameterValueSet(nameof(parameters.Email_Address ), parameters.Email_Address ));
commandEvents.AppendEvent(new TheLongRun.Common.Events.Command.ParameterValueSet(nameof(parameters.Date_Incorporated ), parameters.Date_Incorporated));
commandEvents.AppendEvent(new TheLongRun.Common.Events.Command.ParameterValueSet(nameof(parameters.Twitter_Handle), parameters.Twitter_Handle ));

var commandLogStorageAccountAttribute = new StorageAccountAttribute(CommandLogRecord.DEFAULT_CONNECTION);

#region Logging
if (null != log)
{
if (null != commandLogBlogAttribute)
{
log.Verbose($"Created valid Blog attribute {commandLogBlogAttribute.BlobPath} using {commandLogBlogAttribute.Connection} ",
source: "OnCreateLeagueCommand");
}
else
{
log.Error("Unable to initialise Blob attribute");
}
if (null != commandLogStorageAccountAttribute)
{
log.Verbose($"Created valid storage account attribute {commandLogStorageAccountAttribute.Account} ",
source: "OnCreateLeagueCommand");
}
else
{
log.Error("Unable to initialise Storage Account attribute");
}
}
#endregion

var attributes = new Attribute[]
{
commandLogBlogAttribute,
commandLogStorageAccountAttribute
};

using (var writer = await commandLog.BindAsync<TextWriter>(attributes))
{
#region Logging
if (null != log)
{
log.Verbose($"Saving command to {writer.ToString()}");
}
#endregion

// persist the command to the blob
writer.Write(Newtonsoft.Json.JsonConvert.SerializeObject(cmdRecord));
}

// TODO : FIX bool success = await cmdRecord.SaveToFile();

}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,42 @@
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

using TheLongRun.Common;

namespace TheLongRunLeaguesFunction.Projections
{
public static class LeagueSummaryInformationProjection
{
/// <summary>
/// Run the [League Summary Information] projection for the given league and
/// Run the projection for the given league in the message
/// return the result
/// </summary>
/// <param name="req"></param>
/// <param name="log"></param>
/// <param name="projectionRequestQueueItem">
/// The projection request to process (in the format [projection-name::key] )
/// </param>
/// <param name="log">
/// </param>
/// <returns></returns>
[FunctionName("LeagueSummaryInformationProjection")]
public static async Task<HttpResponseMessage> LeagueSummaryInformationProjectionRun([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
[FunctionName("LeagueProjection")]
public static async void LeagueProjectionRun(
[QueueTrigger(Constants.Queue_Projection_Run)] string projectionRequestQueueItem,
TraceWriter log)
{


const string PROJECTION_NAME = @"league-summary-information";

// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;

// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();

// Set name to query string or body data
name = name ?? data?.name;

if (null == name )
#region Logging
if (null != log)
{
return req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name of the league on the query string or in the request body");
log.Info($"League Projection Run requested - {projectionRequestQueueItem}",
source: "LeagueProjectionRun");
}
#endregion

const string PROJECTION_NAME = @"league-summary-information";

// TODO : Run the projection



// Log that this step has completed
if (null != log)
{
log.Verbose("Projection run",
source: "LeagueSummaryInformationProjection");
}
// Run the projection as requested


// Return the results of the projection...
return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ by editing this MSBuild file. In order to learn more about this please visit htt
<LaunchSiteAfterPublish>False</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>thelongrun-leagues-function.scm.azurewebsites.net:443</MSDeployServiceURL>
<ResourceId>/subscriptions/9f18dd62-6f67-4ec9-b571-457f08a7b20d/resourceGroups/TheLongRun/providers/Microsoft.Web/sites/TheLongRun-Leagues-Function</ResourceId>
<DeployIisAppPath>TheLongRun-Leagues-Function</DeployIisAppPath>
<SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
<EnableMSDeployBackup>True</EnableMSDeployBackup>
<_SavePWD>True</_SavePWD>
<EnableMsDeployAppOffline>False</EnableMsDeployAppOffline>
<TargetFramework>net461</TargetFramework>
<ResourceId>/subscriptions/9f18dd62-6f67-4ec9-b571-457f08a7b20d/resourceGroups/TheLongRun/providers/Microsoft.Web/sites/TheLongRun-Leagues-Function</ResourceId>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public static partial class Query
[QueryName("Get League Summary")]
[FunctionName("OnGetLeagueSummaryQueryHandler")]
public static void OnGetLeagueSummaryQueryHandler([EventGridTrigger] EventGridEvent eventGridEvent,
Binder queryLog,
TraceWriter log
)
{
Expand All @@ -42,17 +41,7 @@ TraceWriter log

QueryLogRecord< object> qryRecord = QueryLogRecord< object>.Create(QUERY_NAME, null);

var queryLogBlogAttribute = new BlobAttribute(QueryLogRecord.DEFAULT_CONTAINER_NAME + @"/" + QUERY_NAME + @"/" + QueryLogRecord.MakeFilename(qryRecord),
FileAccess.Write)
{
Connection = CommandLogRecord.DEFAULT_CONNECTION
};

using (var writer = queryLog.Bind<TextWriter>(queryLogBlogAttribute))
{
// persist the query to the blob

}


// Log that this step has completed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
<RootNamespace>TheLongRunLeaguesFunction</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventGrid" Version="1.0.0-beta2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.10" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="WindowsAzure.Storage" Version="8.7.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Demo\The Long Run\Generated Code\Leagues.CommandHandler.csproj" />
Expand Down
85 changes: 85 additions & 0 deletions TheLongRun.Common/Attributes/ClassifierAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Description;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TheLongRun.Common.Attributes
{
/// <summary>
/// An attribute to mark a projection to use for reading identity group membership from
/// </summary>
/// <remarks>
/// This is not a trigger - we decide on a case by case basis what triggers a classifier
/// and the same classifier may have different invocations
/// </remarks>
[AttributeUsage(AttributeTargets.Parameter)]
[Binding]
public class ClassifierAttribute
: Attribute,
CQRSAzure.EventSourcing.IEventStreamUntypedIdentity
{

/// <summary>
/// The domain name the aggregate instance belongs to
/// </summary>
private readonly string _domainName;
public string DomainName
{
get
{
return _domainName;
}
}

/// <summary>
/// The aggregate type to which the event stream belongs
/// </summary>
private readonly string _aggregateTypeName;
public string AggregateTypeName
{
get
{
return _aggregateTypeName;
}
}

/// <summary>
/// The unique identifier of the specific instance of the aggregate
/// </summary>
private readonly string _aggregateInstanceKey;
[AutoResolve]
public string InstanceKey
{
get
{
return _aggregateInstanceKey;
}
}

/// <summary>
/// The specific classifier type to execute
/// </summary>
private readonly string _classifierTypeName;
public string ClassifierTypeName
{
get
{
return _classifierTypeName;
}
}

public ClassifierAttribute(string domainName,
string aggregateTypeName,
string aggregateInstanceKey,
string classifierTypeName)
{
_domainName = domainName;
_aggregateTypeName = aggregateTypeName;
_aggregateInstanceKey = aggregateInstanceKey;
_classifierTypeName = classifierTypeName;
}
}
}
Loading

0 comments on commit 8a804cf

Please sign in to comment.