Skip to content

Commit 74eab04

Browse files
committed
init
1 parent 47b7ad4 commit 74eab04

File tree

12 files changed

+288
-0
lines changed

12 files changed

+288
-0
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
*.vs
2+
# Build results
3+
[Dd]ebug/
4+
[Dd]ebugPublic/
5+
[Rr]elease/
6+
[Rr]eleases/
7+
x64/
8+
x86/
9+
bld/
10+
[Bb]in/
11+
[Oo]bj/
12+
[Ll]og/

PubSub.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28307.902
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PubSub", "PubSub\PubSub.csproj", "{DB6688E2-6E09-49A6-B5F4-281CD01F3918}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{DB6688E2-6E09-49A6-B5F4-281CD01F3918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{DB6688E2-6E09-49A6-B5F4-281CD01F3918}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{DB6688E2-6E09-49A6-B5F4-281CD01F3918}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{DB6688E2-6E09-49A6-B5F4-281CD01F3918}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {363A27D1-6BBB-4344-BD99-E3E0EA73BEFD}
24+
EndGlobalSection
25+
EndGlobal

PubSub/DTO/Product.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace PubSub.DTO
2+
{
3+
/// <summary>
4+
/// Represents a simple product that will be entered in queue.
5+
/// </summary>
6+
public class Product
7+
{
8+
public string Name { get; set; }
9+
10+
public decimal Price { get; set; }
11+
}
12+
}

PubSub/DTO/Subscriber.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace PubSub.DTO
2+
{
3+
public class Subscriber
4+
{
5+
public string Username { get; set; }
6+
}
7+
}

PubSub/ProductService.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Azure.WebJobs;
4+
using Microsoft.Azure.WebJobs.Extensions.Http;
5+
using Microsoft.Extensions.Logging;
6+
using Newtonsoft.Json;
7+
using PubSub.DTO;
8+
using System;
9+
using System.Threading.Tasks;
10+
11+
namespace PubSub
12+
{
13+
public class ProductService
14+
{
15+
public ProductService()
16+
{
17+
18+
}
19+
20+
// for ICollector implementation refer :
21+
//https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library#binding-at-runtime
22+
[FunctionName(nameof(AddNewProduct))]
23+
//[return: Queue("newproduct", Connection = "AzureWebJobsStorage")]
24+
public async Task<IActionResult> AddNewProduct(
25+
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route = "addProduct")] HttpRequest req
26+
, [Queue("newProduct", Connection = "AzureWebJobsStorage")]ICollector<string> collector
27+
, ILogger logger
28+
)
29+
{
30+
try
31+
{
32+
var json = await req.ReadAsStringAsync();
33+
var product = JsonConvert.DeserializeObject<Product>(json);
34+
35+
//Fluent validation can be used. Leaving it for now.
36+
if (product == null || string.IsNullOrEmpty(product.Name.Trim()) || product.Price < 0)
37+
{
38+
return new BadRequestResult();
39+
}
40+
collector.Add(json); //writes only when method executes succesfully
41+
return new CreatedResult("/api/addProduct", product);
42+
}
43+
catch(JsonReaderException e)
44+
{
45+
return new BadRequestObjectResult(e.Message);
46+
}
47+
catch (Exception e)
48+
{
49+
logger.LogError(e.ToString());
50+
return new ObjectResult("An error occurred at server.");
51+
}
52+
53+
}
54+
}
55+
}

PubSub/Properties/launchSettings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"profiles": {
3+
"PubSub": {
4+
"commandName": "Project"
5+
}
6+
}
7+
}

PubSub/PubSub.csproj

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>netcoreapp2.1</TargetFramework>
4+
<AzureFunctionsVersion>v2</AzureFunctionsVersion>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
8+
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10" />
9+
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.31" />
10+
<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />
11+
</ItemGroup>
12+
<ItemGroup>
13+
<None Update="host.json">
14+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
15+
</None>
16+
<None Update="local.settings.json">
17+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
18+
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
19+
</None>
20+
</ItemGroup>
21+
</Project>

PubSub/Startup.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
2+
3+
[assembly: FunctionsStartup(typeof(PubSub.Startup))]
4+
namespace PubSub
5+
{
6+
public class Startup : FunctionsStartup
7+
{
8+
public override void Configure(IFunctionsHostBuilder builder)
9+
{
10+
11+
}
12+
}
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Microsoft.WindowsAzure.Storage.Table;
2+
3+
namespace PubSub.StorageTableEntity
4+
{
5+
public class QueueSubscriptionMessage : TableEntity
6+
{
7+
8+
}
9+
}

PubSub/SubscriberService.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Azure.WebJobs;
4+
using Microsoft.Azure.WebJobs.Extensions.Http;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.WindowsAzure.Storage;
7+
using Microsoft.WindowsAzure.Storage.Table;
8+
using Newtonsoft.Json;
9+
using PubSub.DTO;
10+
using PubSub.StorageTableEntity;
11+
using System;
12+
using System.Threading.Tasks;
13+
14+
namespace PubSub
15+
{
16+
public class SubscriberService
17+
{
18+
public SubscriberService()
19+
{
20+
21+
}
22+
23+
[FunctionName(nameof(Subscribe))]
24+
public async Task<IActionResult> Subscribe(
25+
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route = "subscribe/{queue}")]HttpRequest req
26+
, string queue
27+
, ILogger logger
28+
)
29+
{
30+
try
31+
{
32+
var user = JsonConvert.DeserializeObject<Subscriber>(await req.ReadAsStringAsync());
33+
if (user == null || string.IsNullOrWhiteSpace(user.Username))
34+
return new BadRequestResult();
35+
36+
await AddSubscriberAsync(queue, user.Username);
37+
return new OkResult();
38+
}
39+
catch(JsonReaderException e)
40+
{
41+
logger.LogError(e.ToString());
42+
return new BadRequestObjectResult(e.Message);
43+
}
44+
catch (Exception e)
45+
{
46+
logger.LogError(e.ToString());
47+
return new ObjectResult(new { error = e.ToString() });
48+
}
49+
}
50+
51+
[FunctionName(nameof(NotifySubscribers))]
52+
public async Task NotifySubscribers(
53+
[QueueTrigger("newproduct")]Product input,
54+
ILogger logger
55+
)
56+
{
57+
try
58+
{
59+
var table = await GetCloudTableReferenceAsync("QueueMessageSubscribers");
60+
var query = new TableQuery<QueueSubscriptionMessage>().Where(
61+
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "newProduct")
62+
);
63+
var subscribers = await table.ExecuteQuerySegmentedAsync(query, new TableContinuationToken());
64+
65+
//TODO: implement notification service.
66+
subscribers.Results.ForEach(x => logger.LogInformation($"Sending notification to {x.RowKey}"));//Row key stores the username.
67+
68+
}
69+
catch (Exception e)
70+
{
71+
logger.LogError(e.ToString());
72+
}
73+
}
74+
75+
76+
77+
private async Task AddSubscriberAsync(string queue, string username)
78+
{
79+
var subscriptionMessage = new QueueSubscriptionMessage()
80+
{
81+
// Because a queue can have multiple subscribers.
82+
PartitionKey = queue,
83+
84+
// Because every subcriber is unique.
85+
RowKey = username,
86+
};
87+
88+
var table = await GetCloudTableReferenceAsync("QueueMessageSubscribers");
89+
90+
TableOperation insert = TableOperation.Insert(subscriptionMessage);
91+
var insertionResult = await table.ExecuteAsync(insert);
92+
}
93+
94+
95+
private async Task<CloudTable> GetCloudTableReferenceAsync(string tableName, string connectionString = "")
96+
{
97+
CloudStorageAccount storageAccount =
98+
string.IsNullOrWhiteSpace(connectionString)
99+
? CloudStorageAccount.DevelopmentStorageAccount : CloudStorageAccount.Parse(connectionString);
100+
101+
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
102+
103+
CloudTable table = tableClient.GetTableReference(tableName);
104+
await table.CreateIfNotExistsAsync();
105+
return table;
106+
}
107+
108+
}
109+
}

0 commit comments

Comments
 (0)