This project contains backend functionality to run the DDD conferences, including:
- Syncing data from Sessionize to Azure Table Storage (tenanted by conference year) for submitted sessions (and submitters) and separate to that, selected sessions (and presenters)
- APIs that return submission and session (agenda) information during allowed times
- APIs to facilitate voting by the community against (optionally anonymous) submitted sessions (notes stored to Azure Table Storage tenanted by conference year) including various mechanisms to detect fraud
- Syncing Eventbrite order IDs and Azure App Insights voting user IDs to assist with voting fraud detection and validation
- API to return analysed voting information
- Ability to trigger an Azure Logic App when a new session is detected from Sessionize (which can then be used to create Microsoft Teams / Slack notifications for visibility to the organising committee and/or to trigger moderation actions)
- Eventbrite webhook to take order notifications, de-duplicate them and place them in queue storage so they can be picked up by a Logic App (or similar) to do things like create Microsoft Teams / Slack notifications for visibility to the organising committee
- Getting feedback information and prize draw names
DDD.Core
: Cross-cutting logic and core domain modelDDD.Functions
: Azure Functions project that contains:AppInsightsSync
: C# Azure Function that syncs app insights user IDs to Azure Table Storage for users that submitted a voteEventbriteNotification
: Node.js Azure Function that exposes a web hook URL that can be added to Eventbrite (via Account Settings > Webhooks) for theorder.placed
action that will then de-duplicate webhook events and push them to queue storage for further processing (e.g. via a Logic App)EventbriteSync
: C# Azure Function that syncs Eventbrite order IDs to Azure Table Storage for a configured eventGetAgenda
: C# Azure Function that returns sessions and presenters that have been approved for agendaGetSubmissions
: C# Azure Function that returns submissions and submitters for use with either voting or showing submitted sessionsGetVotes
: C# Azure Function that returns analysed vote information; can be piped into Microsoft Power BI or similar for further processing and visualisationNewSessionNotification
: C# Azure Function that responds to new submissions in Azure Table Storage and then calls a Logic App Web Hook URL (from config) with the session and presenter information (marking that session as notified to avoid duplicate notifications)SessionizeReadModelSync
: C# Azure Function triggered by a cron schedule defined in config that performs a sync from Sessionize to Azure Table Storage for submissionsSessionizeAgendaSync
: C# Azure Function triggered by a cron schedule defined in config that performs a sync from Sessionize to Azure Table Storage for approved sessionsSubmitVote
: : C# Azure Function that allows a vote for submissions to be submitted, where it is validated and persisted to Azure Table Storage
DDD.Sessionize
: Syncing logic to sync data from sessionize to Azure Table StorageDDD.Sessionize.Tests
: Unit tests for the Sessionize Syncing codeinfrastructure
: Azure ARM deployment scripts to provision the backend environmentDeploy-Local.ps1
: Run locally to debug or develop the scripts using your user context in AzureDeploy.ps1
: Main deployment script that you need to call from CD pipelineazuredeploy.json
: Azure ARM template
.vsts-ci.yml
: VSTS Continuous Integration definition for this project
The backend application depends on programatic access to the Frontend Website's Application Insights to pull and store information on voting behaviour.
To supply this access, create an API key with Read telemetry
permissions within the frontend website's Application Insights instance in the Azure Portal, and enter the Application ID and Key presented into the AppInsightsApplicationId
and AppInsightsApplicationKey
parameters.
VSTS doesn't yet support .yml files for Continuous Delivery (Release) so the steps to set it up are:
- Install SAS Token VSTS extension
- todo: Just add it to Deploy.ps1
- Create the release definition triggered by the CI build
- Add a task for the SAS token generation for the storage account you persisted deployments to set the output variables to
DeploymentZipUri
andDeploymentZipToken
respectively, recommend setting the timeout to a big number so it's always available (e.g.1000000
), permission should just ber
- Add an Azure PowerShell task against your subscription and:
$(System.DefaultWorkingDirectory)/{CI build name}/infrastructure/Deploy.ps1
as the script file path- `` as the script arguments
- Add variables, e.g.:
- Profit!
The NewSessionNotificationLogicAppUrl
value is gotten by creating a logic app and copying the webhook URL from it. The logic app would roughly have:
-
When a HTTP request is received
trigger with json schema of:{ "properties": { "Presenters": { "items": { "properties": { "Bio": { "type": "string" }, "ExternalId": { "type": "string" }, "Id": { "type": "string" }, "Name": { "type": "string" }, "ProfilePhotoUrl": { "type": "string" }, "Tagline": { "type": "string" }, "TwitterHandle": { "type": "string" }, "WebsiteUrl": { "type": "string" } }, "required": [ "Id", "ExternalId", "Name", "Tagline", "Bio", "ProfilePhotoUrl", "WebsiteUrl", "TwitterHandle" ], "type": "object" }, "type": "array" }, "Session": { "properties": { "Abstract": { "type": "string" }, "CreatedDate": { "type": "string" }, "ExternalId": { "type": "string" }, "Format": { "type": "number" }, "Id": { "type": "string" }, "Level": {}, "MobilePhoneContact": {}, "PresenterIds": { "items": { "type": "string" }, "type": "array" }, "Tags": { "type": "array" }, "Title": { "type": "string" } }, "type": "object" } }, "type": "object" }
-
For each
action againstPresenters
with a nestedCompose
action againstName
-
Post message
action (for Teams/Slack) with something like@{join(actionOutputs('Compose'), ', ')} submitted a talk '@{triggerBody()?['Session']['Title']}' as @{triggerBody()?['Session']['Format']} / @{triggerBody()?['Session']['Level']} with tags @{join(triggerBody()?['Session']['Tags'], ', ')}.
-
Send an email
action (for O365/GMail/Outlook.com depending on what you have) that sends an email if the previous step failed (viaConfigure run after
)
The logic app would roughly have:
When there are messages in a queue
trigger to theattendees
queue of the{conferencename}functions{environment}
storage accountPost message
action (for Teams/Slack) with something like@{json(trigger().outputs.body.MessageText).name} is attending @{json(trigger().outputs.body.MessageText).event} as @{json(trigger().outputs.body.MessageText).ticketClass} (orderid: @{json(trigger().outputs.body.MessageText).orderId}). @{json(trigger().outputs.body.MessageText).qtySold}/@{json(trigger().outputs.body.MessageText).totalQty} @{json(trigger().outputs.body.MessageText).ticketClass} tickets taken.
Delete message
action for theattendees
queue with the Message ID and Pop Receipt from the trigger