Skip to content

Commit 6ef4c32

Browse files
feat: Add dockerized email inbound webhook consumer example (#789)
1 parent 82c31f1 commit 6ef4c32

26 files changed

+1761
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# directories
2+
**/bin/
3+
**/obj/
4+
**/out/
5+
6+
# files
7+
Dockerfile*
8+
**/*.trx
9+
**/*.md
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": ".NET Core Launch (web)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
"program": "${workspaceFolder}/src/inbound/bin/Debug/netcoreapp2.1/inbound.dll",
13+
"args": [],
14+
"cwd": "${workspaceFolder}/src/inbound",
15+
"stopAtEntry": false,
16+
"internalConsoleOptions": "openOnSessionStart",
17+
"launchBrowser": {
18+
"enabled": true,
19+
"args": "${auto-detect-url}",
20+
"windows": {
21+
"command": "cmd.exe",
22+
"args": "/C start ${auto-detect-url}"
23+
},
24+
"osx": {
25+
"command": "open"
26+
},
27+
"linux": {
28+
"command": "xdg-open"
29+
}
30+
},
31+
"env": {
32+
"ASPNETCORE_ENVIRONMENT": "Development"
33+
},
34+
"sourceFileMap": {
35+
"/Views": "${workspaceFolder}/Views"
36+
}
37+
},
38+
{
39+
"name": ".NET Core Attach",
40+
"type": "coreclr",
41+
"request": "attach",
42+
"processId": "${command:pickProcess}"
43+
}
44+
]
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/src/inbound/inbound.csproj"
11+
],
12+
"problemMatcher": "$msCompile"
13+
}
14+
]
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM microsoft/dotnet:2.1-sdk AS build
2+
WORKDIR /App
3+
4+
# copy csproj and restore as distinct layers
5+
COPY *.sln .
6+
COPY Src/Inbound/*.csproj ./Src/Inbound/
7+
COPY Tests/Inbound.Tests/*.csproj ./Tests/Inbound.Tests/
8+
RUN dotnet restore
9+
10+
# copy everything else and build app
11+
COPY Src/Inbound/. ./Src/Inbound/
12+
WORKDIR /App/Src/Inbound
13+
RUN dotnet publish -c Release -o Out
14+
15+
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
16+
WORKDIR /App
17+
COPY --from=build /App/Src/Inbound/Out ./
18+
19+
RUN echo "ASPNETCORE_URLS=http://0.0.0.0:\$PORT\nDOTNET_RUNNING_IN_CONTAINER=true" > /App/SetupHerokuEnv.sh && chmod +x /App/SetupHerokuEnv.sh
20+
21+
CMD /bin/bash -c "source /App/SetupHerokuEnv.sh && dotnet Inbound.dll"
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Overview
2+
3+
SendGrid has an Email Inbound Parse Webhook which posts data from a specified incoming email address to a URL of your choice. This library allows you to quickly and easily deployable solution that help you easily get up and running processing (parse and complete some action) your inbound parse webhooks.
4+
5+
This is docker-based solution which can be deployed on cloud services like Heroku out of the box.
6+
7+
# Table of Content
8+
- [Prerequisite](#prerequisite)
9+
- [Deploy locally](#deploy_locally)
10+
- [Deploy Heroku](#deploy_heroku)
11+
- [Testing the Source Code](#testing_the_source_code)
12+
13+
<a name="prerequisite"></a>
14+
## Prerequisite
15+
Clone the repository
16+
```
17+
git clone https://github.com/sendgrid/sendgrid-csharp.git
18+
```
19+
Move into the clonned repository
20+
```
21+
cd sendgrid-csharp/examples/inbound-webhook-handler
22+
```
23+
Restore the Packages
24+
```
25+
dotnet restore
26+
```
27+
28+
<a name="deploy_locally"></a>
29+
## Deploy locally
30+
Setup your MX records. Depending on your domain name host, you may need to wait up to 48 hours for the settings to propagate.
31+
32+
Run the Inbound Parse listener in your terminal:
33+
```
34+
git clone https://github.com/sendgrid/sendgrid-csharp.git
35+
36+
cd sendgrid-csharp/examples/inbound-webhook-handler
37+
38+
dotnet restore
39+
40+
dotnet run --project .\Src\Inbound\Inbound.csproj
41+
```
42+
Above will start server listening on a random port like below
43+
44+
In another terminal, use ngrok to allow external access to your machine:
45+
```
46+
ngrok http PORT_NUMBER
47+
```
48+
Update your SendGrid Incoming Parse settings: Settings Page | Docs
49+
50+
- For the HOSTNAME field, use the domain that you changed the MX records (e.g. inbound.yourdomain.com)
51+
- For the URL field, use the URL generated by ngrok + /inbound, e.g http://XXXXXXX.ngrok.io/inbound
52+
53+
Next, send an email to [anything]@inbound.yourdomain.com, then look at the terminal where you started the Inbound Parse listener.
54+
55+
<a name="deploy_heroku"></a>
56+
## Deploy to Heroku
57+
58+
[Create](https://signup.heroku.com/) Heruko account if not already present
59+
60+
Install the Heroku CLI
61+
62+
Download and install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-command-line).
63+
64+
If you haven't already, log in to your Heroku account and follow the prompts to create a new SSH public key.
65+
```
66+
$ heroku login
67+
```
68+
69+
Now you can sign into Container Registry.
70+
```
71+
$ heroku container:login
72+
```
73+
74+
Create app in heroku
75+
```
76+
$ heroku apps:create UNIQUE_APP_NAME
77+
```
78+
79+
Push your Docker-based app
80+
Build the Dockerfile in the current directory and push the Docker image.
81+
```
82+
$ heroku container:push web --app UNIQUE_APP_NAME
83+
```
84+
85+
Deploy the changes
86+
Release the newly pushed images to deploy your app.
87+
```
88+
$ heroku container:release web --app UNIQUE_APP_NAME
89+
```
90+
91+
<a name="testing_the_source_code"></a>
92+
## Testing the Source Code
93+
You can get all the test cases inside the `Tests` folder.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26124.0
5+
MinimumVisualStudioVersion = 15.0.26124.0
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{5E71A0CA-F2E2-4762-B020-29F1D8682F75}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inbound", "Src\Inbound\Inbound.csproj", "{9449C214-54EF-40A9-AAB3-4FE212BECA23}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1446F41E-1766-4B3E-B7AC-C8766A3E1751}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inbound.Tests", "Tests\Inbound.Tests\Inbound.Tests.csproj", "{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Debug|x64 = Debug|x64
18+
Debug|x86 = Debug|x86
19+
Release|Any CPU = Release|Any CPU
20+
Release|x64 = Release|x64
21+
Release|x86 = Release|x86
22+
EndGlobalSection
23+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|x64.ActiveCfg = Debug|Any CPU
27+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|x64.Build.0 = Debug|Any CPU
28+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|x86.ActiveCfg = Debug|Any CPU
29+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Debug|x86.Build.0 = Debug|Any CPU
30+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|x64.ActiveCfg = Release|Any CPU
33+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|x64.Build.0 = Release|Any CPU
34+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|x86.ActiveCfg = Release|Any CPU
35+
{9449C214-54EF-40A9-AAB3-4FE212BECA23}.Release|x86.Build.0 = Release|Any CPU
36+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|x64.ActiveCfg = Debug|Any CPU
39+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|x64.Build.0 = Debug|Any CPU
40+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|x86.ActiveCfg = Debug|Any CPU
41+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Debug|x86.Build.0 = Debug|Any CPU
42+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|Any CPU.ActiveCfg = Release|Any CPU
43+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|x64.ActiveCfg = Release|Any CPU
45+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|x64.Build.0 = Release|Any CPU
46+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|x86.ActiveCfg = Release|Any CPU
47+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927}.Release|x86.Build.0 = Release|Any CPU
48+
EndGlobalSection
49+
GlobalSection(SolutionProperties) = preSolution
50+
HideSolutionNode = FALSE
51+
EndGlobalSection
52+
GlobalSection(NestedProjects) = preSolution
53+
{9449C214-54EF-40A9-AAB3-4FE212BECA23} = {5E71A0CA-F2E2-4762-B020-29F1D8682F75}
54+
{0AF26ED1-3F2D-40F5-9A01-FE5955A8F927} = {1446F41E-1766-4B3E-B7AC-C8766A3E1751}
55+
EndGlobalSection
56+
GlobalSection(ExtensibilityGlobals) = postSolution
57+
SolutionGuid = {D6E6C0FF-04E9-4D98-B958-612DDD7FBB0A}
58+
EndGlobalSection
59+
EndGlobal
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Inbound.Parsers;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Newtonsoft.Json;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Inbound.Controllers
8+
{
9+
[Route("/")]
10+
[ApiController]
11+
public class InboundController : Controller
12+
{
13+
[HttpGet]
14+
public IActionResult Index()
15+
{
16+
return View();
17+
}
18+
19+
// Process POST from Inbound Parse and print received data.
20+
[HttpPost]
21+
[Route("inbound")]
22+
public IActionResult InboundParse()
23+
{
24+
InboundWebhookParser _inboundParser = new InboundWebhookParser(Request.Body);
25+
26+
var inboundEmail = _inboundParser.Parse();
27+
28+
return Ok();
29+
}
30+
31+
private void Log(IDictionary<string, string> keyValues)
32+
{
33+
if(keyValues == null)
34+
{
35+
return;
36+
}
37+
Console.WriteLine(JsonConvert.SerializeObject(keyValues));
38+
}
39+
}
40+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.1</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Folder Include="wwwroot\" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="HttpMultipartParser" Version="2.2.4" />
13+
<PackageReference Include="Microsoft.AspNetCore.App" />
14+
</ItemGroup>
15+
16+
</Project>

0 commit comments

Comments
 (0)