Skip to content

Commit 6b9cdde

Browse files
committed
Update
1 parent ff4936a commit 6b9cdde

File tree

2 files changed

+201
-166
lines changed

2 files changed

+201
-166
lines changed

samples/EventGridIntegration/README.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,25 @@ An Azure Storage account is required by a function app using Event Grid trigger.
5151

5252
```bash
5353
git clone [email protected]:aspnet/AzureSignalR-samples.git
54+
```
55+
56+
- In the repository, there're two Event Grid integration samples using different languages. For the JavaScript sample, investigate to
5457
55-
cd AzureSignalR-samples/samples/EventGridIntegration/javascript
58+
```bash
59+
AzureSignalR-samples/samples/EventGridIntegration/javascript
60+
```
61+
62+
- If you want to use C# sample, investigate to
63+
64+
```bash
65+
AzureSignalR-samples/samples/EventGridIntegration/csharp
5666
```
5767
5868
### Configure application settings
5969
6070
When running and debugging the Azure Functions runtime locally, application settings are read from **local.settings.json**. Also, you can upload there settings to remote when you try to deploy Function App to Azure. Update this file with the connection string of the SignalR Service instance that you created earlier.
6171
62-
1. Open the file **local.settings.json** and update the settings.
72+
1. Open the file **local.settings.json** and update the settings. (The file shown below is used by JavaScript sample, and the C# sample is similar)
6373
6474
```json
6575
{
@@ -232,7 +242,25 @@ App Service Authentication supports authentication with Azure Active Directory,
232242
233243
### Update negotiate function
234244
235-
1. For the JavaScript sample, update in `userId` in `negotiate/function.json` to `"{headers.x-ms-client-principal-name}"`. And for the C# sample, add parameter `UserId = "{headers.x-ms-signalr-userid}"` to `Negotiate` function.
245+
1. Update the attribute parameter of negotiate function.
246+
247+
- If you're using JavaScript sample, update in `userId` in `negotiate/function.json` to `"{headers.x-ms-client-principal-name}"`.
248+
249+
```json
250+
{
251+
"type": "signalRConnectionInfo",
252+
"name": "connectionInfo",
253+
"userId": "{headers.x-ms-client-principal-name}",
254+
"hubName": "EventGridIntegrationSampleChat",
255+
"direction": "in"
256+
}
257+
```
258+
259+
- If you're using C# sample, add parameter `UserId = "{headers.x-ms-signalr-userid}"` to `Negotiate` function.
260+
261+
```C#
262+
[SignalRConnectionInfo(HubName = HubName, UserId = "{headers.x-ms-signalr-userid}")] SignalRConnectionInfo connectionInfo
263+
```
236264
237265
1. Deploy the function to Azure again
238266
Lines changed: 170 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,170 @@
1-
using System;
2-
using System.IO;
3-
using System.Text;
4-
using System.Threading.Tasks;
5-
using Microsoft.AspNetCore.Http;
6-
using Microsoft.AspNetCore.SignalR;
7-
using Microsoft.Azure.EventGrid.Models;
8-
using Microsoft.Azure.WebJobs;
9-
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
10-
using Microsoft.Azure.WebJobs.Extensions.Http;
11-
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
12-
using Microsoft.Extensions.Logging;
13-
using Microsoft.WindowsAzure.Storage.Table;
14-
using Newtonsoft.Json;
15-
using Newtonsoft.Json.Linq;
16-
17-
namespace SignalR.Sample
18-
{
19-
public static class Functions
20-
{
21-
private const string TableName = "connection";
22-
23-
[FunctionName("negotiate")]
24-
public static SignalRConnectionInfo Negotiate(
25-
[HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
26-
[SignalRConnectionInfo(HubName = "EventGridIntegrationSampleChat")] SignalRConnectionInfo connectionInfo)
27-
{
28-
return connectionInfo;
29-
}
30-
31-
[FunctionName("messages")]
32-
public static Task SendMessage(
33-
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req,
34-
[SignalR(HubName = "EventGridIntegrationSampleChat")]IAsyncCollector<SignalRMessage> signalRMessages)
35-
{
36-
var message = new JsonSerializer().Deserialize<ChatMessage>(new JsonTextReader(new StreamReader(req.Body)));
37-
message.sender = req.Headers?["x-ms-client-principal-name"] ?? "";
38-
var recipientUserId = "";
39-
if (!string.IsNullOrEmpty(message.recipient))
40-
{
41-
recipientUserId = message.recipient;
42-
message.isPrivate = true;
43-
}
44-
45-
return signalRMessages.AddAsync(new SignalRMessage
46-
{
47-
UserId = message.recipient,
48-
Target = "newMessage",
49-
Arguments = new[] { message }
50-
});
51-
}
52-
53-
[FunctionName("OnConnection")]
54-
public static async Task EventGridTest([EventGridTrigger]EventGridEvent eventGridEvent,
55-
[SignalR(HubName = "EventGridIntegrationSampleChat")]IAsyncCollector<SignalRMessage> signalRMessages,
56-
[Table(TableName)]CloudTable cloudTable,
57-
ILogger log)
58-
{
59-
var message = ((JObject) eventGridEvent.Data).ToObject<SignalREvent>();
60-
var partitionKey = GetLastPart(eventGridEvent.Topic);
61-
var rowKey = message.HubName;
62-
var token = true;
63-
var newConnectionCount = 0;
64-
ConnectionCountEntity entity;
65-
while (token)
66-
{
67-
try
68-
{
69-
var operation = TableOperation.Retrieve<ConnectionCountEntity>(partitionKey, rowKey);
70-
var result = await cloudTable.ExecuteAsync(operation);
71-
72-
if (result.Result == null)
73-
{
74-
entity = new ConnectionCountEntity(partitionKey, rowKey)
75-
{
76-
Count = newConnectionCount = eventGridEvent.EventType == "Microsoft.SignalRService.ClientConnectionConnected" ? 1 : 0
77-
};
78-
operation = TableOperation.Insert(entity);
79-
}
80-
else
81-
{
82-
entity = (ConnectionCountEntity)result.Result;
83-
entity.Count = newConnectionCount = entity.Count + (eventGridEvent.EventType == "Microsoft.SignalRService.ClientConnectionConnected" ? 1 : -1);
84-
operation = TableOperation.Replace(entity);
85-
}
86-
87-
await cloudTable.ExecuteAsync(operation);
88-
token = false;
89-
}
90-
catch (Exception ex)
91-
{
92-
log.LogError(ex, "Failed to complete operation with storage");
93-
}
94-
}
95-
96-
if (eventGridEvent.EventType == "Microsoft.SignalRService.ClientConnectionConnected")
97-
{
98-
await signalRMessages.AddAsync(new SignalRMessage
99-
{
100-
ConnectionId = message.ConnectionId,
101-
Target = "newMessage",
102-
Arguments = new[] { new ChatMessage
103-
{
104-
text = "Welcome to Serverless Chat",
105-
sender = "__SYSTEM__",
106-
}}
107-
});
108-
}
109-
110-
await signalRMessages.AddAsync(new SignalRMessage
111-
{
112-
Target = "connectionCount",
113-
Arguments = new[] { (object)newConnectionCount },
114-
});
115-
}
116-
117-
private static string GetLastPart(string data)
118-
{
119-
var index = data.LastIndexOf('/');
120-
if (index == -1)
121-
{
122-
return data;
123-
}
124-
else
125-
{
126-
return data.Substring(index + 1);
127-
}
128-
}
129-
130-
public class ChatMessage
131-
{
132-
// The name here is not keep the C# naming convension
133-
// To keep the name consistant with JavaScript sample for simplification
134-
public string sender { get; set; }
135-
public string text { get; set; }
136-
public string recipient { get; set; }
137-
public bool isPrivate { get; set; }
138-
}
139-
140-
public class SignalREvent
141-
{
142-
public DateTime Timestamp { get; set; }
143-
public string HubName { get; set; }
144-
public string ConnectionId { get; set; }
145-
public string UserId { get; set; }
146-
}
147-
148-
public class ConnectionCountEntity : TableEntity
149-
{
150-
public int Count { get; set; }
151-
152-
public ConnectionCountEntity()
153-
{
154-
}
155-
156-
public ConnectionCountEntity(string partitionKey, string rowKey)
157-
{
158-
PartitionKey = partitionKey;
159-
RowKey = rowKey;
160-
}
161-
}
162-
}
163-
}
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.AspNetCore.SignalR;
7+
using Microsoft.Azure.EventGrid.Models;
8+
using Microsoft.Azure.WebJobs;
9+
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
10+
using Microsoft.Azure.WebJobs.Extensions.Http;
11+
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
12+
using Microsoft.Extensions.Logging;
13+
using Microsoft.WindowsAzure.Storage.Table;
14+
using Newtonsoft.Json;
15+
using Newtonsoft.Json.Linq;
16+
17+
namespace SignalR.Sample
18+
{
19+
public static class Functions
20+
{
21+
private const string TableName = "connection";
22+
private const string EventGridConnectedEventName = "Microsoft.SignalRService.ClientConnectionConnected";
23+
private const string HubName = "EventGridIntegrationSampleChat";
24+
25+
[FunctionName("negotiate")]
26+
public static SignalRConnectionInfo Negotiate(
27+
[HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
28+
[SignalRConnectionInfo(HubName = HubName)] SignalRConnectionInfo connectionInfo)
29+
{
30+
return connectionInfo;
31+
}
32+
33+
[FunctionName("messages")]
34+
public static Task SendMessage(
35+
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req,
36+
[SignalR(HubName = HubName)]IAsyncCollector<SignalRMessage> signalRMessages)
37+
{
38+
var message = new JsonSerializer().Deserialize<ChatMessage>(new JsonTextReader(new StreamReader(req.Body)));
39+
message.Sender = req.Headers?["x-ms-client-principal-name"] ?? "";
40+
var recipientUserId = "";
41+
if (!string.IsNullOrEmpty(message.Recipient))
42+
{
43+
recipientUserId = message.Recipient;
44+
message.IsPrivate = true;
45+
}
46+
47+
return signalRMessages.AddAsync(new SignalRMessage
48+
{
49+
UserId = message.Recipient,
50+
Target = "newMessage",
51+
Arguments = new[] { message }
52+
});
53+
}
54+
55+
[FunctionName("OnConnection")]
56+
public static async Task EventGridTest([EventGridTrigger]EventGridEvent eventGridEvent,
57+
[SignalR(HubName = HubName)]IAsyncCollector<SignalRMessage> signalRMessages,
58+
[Table(TableName)]CloudTable cloudTable,
59+
ILogger log)
60+
{
61+
var message = ((JObject) eventGridEvent.Data).ToObject<SignalREvent>();
62+
var partitionKey = GetLastPart(eventGridEvent.Topic);
63+
var rowKey = message.HubName;
64+
var isSuccess = true;
65+
var newConnectionCount = 0;
66+
67+
while (isSuccess)
68+
{
69+
try
70+
{
71+
ConnectionCountEntity entity;
72+
var operation = TableOperation.Retrieve<ConnectionCountEntity>(partitionKey, rowKey);
73+
var result = await cloudTable.ExecuteAsync(operation);
74+
75+
if (result.Result == null)
76+
{
77+
entity = new ConnectionCountEntity(partitionKey, rowKey)
78+
{
79+
Count = newConnectionCount = IsConnectedEvent(eventGridEvent.EventType) ? 1 : 0
80+
};
81+
operation = TableOperation.Insert(entity);
82+
}
83+
else
84+
{
85+
entity = (ConnectionCountEntity)result.Result;
86+
entity.Count = newConnectionCount = entity.Count + (IsConnectedEvent(eventGridEvent.EventType) ? 1 : -1);
87+
operation = TableOperation.Replace(entity);
88+
}
89+
90+
await cloudTable.ExecuteAsync(operation);
91+
isSuccess = false;
92+
}
93+
catch (Exception ex)
94+
{
95+
log.LogError(ex, "Failed to complete operation with storage");
96+
}
97+
}
98+
99+
if (IsConnectedEvent(eventGridEvent.EventType))
100+
{
101+
await signalRMessages.AddAsync(new SignalRMessage
102+
{
103+
ConnectionId = message.ConnectionId,
104+
Target = "newMessage",
105+
Arguments = new[] { new ChatMessage
106+
{
107+
Text = "Welcome to Serverless Chat",
108+
Sender = "__SYSTEM__",
109+
}}
110+
});
111+
}
112+
113+
await signalRMessages.AddAsync(new SignalRMessage
114+
{
115+
Target = "connectionCount",
116+
Arguments = new[] { (object)newConnectionCount },
117+
});
118+
}
119+
120+
private static string GetLastPart(string data)
121+
{
122+
var index = data.LastIndexOf('/');
123+
if (index == -1)
124+
{
125+
return data;
126+
}
127+
else
128+
{
129+
return data.Substring(index + 1);
130+
}
131+
}
132+
133+
private static bool IsConnectedEvent(string name) => name == EventGridConnectedEventName;
134+
135+
public class ChatMessage
136+
{
137+
[JsonProperty("sender")]
138+
public string Sender { get; set; }
139+
[JsonProperty("text")]
140+
public string Text { get; set; }
141+
[JsonProperty("recipient")]
142+
public string Recipient { get; set; }
143+
[JsonProperty("isPrivate")]
144+
public bool IsPrivate { get; set; }
145+
}
146+
147+
public class SignalREvent
148+
{
149+
public DateTime Timestamp { get; set; }
150+
public string HubName { get; set; }
151+
public string ConnectionId { get; set; }
152+
public string UserId { get; set; }
153+
}
154+
155+
public class ConnectionCountEntity : TableEntity
156+
{
157+
public int Count { get; set; }
158+
159+
public ConnectionCountEntity()
160+
{
161+
}
162+
163+
public ConnectionCountEntity(string partitionKey, string rowKey)
164+
{
165+
PartitionKey = partitionKey;
166+
RowKey = rowKey;
167+
}
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)