microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
mehak/logger

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore/AspNetCorePlugin.cs

256lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Text.Json;
5using System.Text.Json.Serialization;
6
7using Microsoft.AspNetCore.Builder;
8using Microsoft.AspNetCore.Http;
9using Microsoft.Teams.Api.Activities;
10using Microsoft.Teams.Api.Auth;
11using Microsoft.Teams.Api.Clients;
12using Microsoft.Teams.Apps;
13using Microsoft.Teams.Apps.Events;
14using Microsoft.Teams.Apps.Plugins;
15using Microsoft.Teams.Common.Http;
16using Microsoft.Extensions.Logging;
17using Microsoft.Extensions.Logging.Abstractions;
18
19using HttpRequest = Microsoft.AspNetCore.Http.HttpRequest;
20
21namespace Microsoft.Teams.Plugins.AspNetCore;
22
23[Plugin]
24public partial class AspNetCorePlugin : ISenderPlugin, IAspNetCorePlugin
25{
26 private readonly ILogger<AspNetCorePlugin> _logger;
27
28 [Dependency("Token", optional: true)]
29 public IToken? Token { get; set; }
30
31 [Dependency]
32 public IHttpClient Client { get; set; }
33
34 public event EventFunction Events;
35
36 private static readonly JsonSerializerOptions _jsonSerializerOptions = new()
37 {
38 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
39 };
40
41 public AspNetCorePlugin(ILogger<AspNetCorePlugin>? logger = null)
42 {
43 _logger = logger ?? NullLogger<AspNetCorePlugin>.Instance;
44 }
45
46 public IApplicationBuilder Configure(IApplicationBuilder builder)
47 {
48 return builder;
49 }
50
51 public Task OnInit(App app, CancellationToken cancellationToken = default)
52 {
53 return Task.CompletedTask;
54 }
55
56 public Task OnStart(App app, CancellationToken cancellationToken = default)
57 {
58 _logger.LogDebug("OnStart");
59 return Task.CompletedTask;
60 }
61
62 public Task OnError(App app, IPlugin plugin, ErrorEvent @event, CancellationToken cancellationToken = default)
63 {
64 _logger.LogDebug("OnError");
65 return Task.CompletedTask;
66 }
67
68 public Task OnActivity(App app, ISenderPlugin sender, ActivityEvent @event, CancellationToken cancellationToken = default)
69 {
70 _logger.LogDebug("OnActivity");
71 return Task.CompletedTask;
72 }
73
74 public Task OnActivitySent(App app, ISenderPlugin sender, ActivitySentEvent @event, CancellationToken cancellationToken = default)
75 {
76 _logger.LogDebug("OnActivitySent");
77 return Task.CompletedTask;
78 }
79
80 public Task OnActivityResponse(App app, ISenderPlugin sender, ActivityResponseEvent @event, CancellationToken cancellationToken = default)
81 {
82 _logger.LogDebug("OnActivityResponse");
83 return Task.CompletedTask;
84 }
85
86 public Task<IActivity> Send(IActivity activity, Api.ConversationReference reference, CancellationToken cancellationToken = default)
87 {
88 return Send<IActivity>(activity, reference, isTargeted: false, cancellationToken);
89 }
90
91 public Task<IActivity> Send(IActivity activity, Api.ConversationReference reference, bool isTargeted, CancellationToken cancellationToken = default)
92 {
93 return Send<IActivity>(activity, reference, isTargeted, cancellationToken);
94 }
95
96 public Task<TActivity> Send<TActivity>(TActivity activity, Api.ConversationReference reference, CancellationToken cancellationToken = default) where TActivity : IActivity
97 {
98 return Send<TActivity>(activity, reference, isTargeted: false, cancellationToken);
99 }
100
101 public async Task<TActivity> Send<TActivity>(TActivity activity, Api.ConversationReference reference, bool isTargeted, CancellationToken cancellationToken = default) where TActivity : IActivity
102 {
103 var client = new ApiClient(reference.ServiceUrl, Client, cancellationToken);
104
105 activity.Conversation = reference.Conversation;
106 activity.From = reference.Bot;
107 activity.Recipient = reference.User;
108 activity.ChannelId = reference.ChannelId;
109
110 if (activity.Id is not null && !activity.IsStreaming)
111 {
112 await client
113 .Conversations
114 .Activities
115 .UpdateAsync(reference.Conversation.Id, activity.Id, activity, isTargeted);
116
117 return activity;
118 }
119
120 var res = await client
121 .Conversations
122 .Activities
123 .CreateAsync(reference.Conversation.Id, activity, isTargeted);
124
125 activity.Id = res?.Id;
126 return activity;
127 }
128
129 public IStreamer CreateStream(Api.ConversationReference reference, CancellationToken cancellationToken = default)
130 {
131 return new Stream()
132 {
133 Send = async activity =>
134 {
135 var res = await Send(activity, reference, false, cancellationToken);
136 return res;
137 }
138 };
139 }
140
141 public async Task<Response> Do(ActivityEvent @event, CancellationToken cancellationToken = default)
142 {
143 try
144 {
145 var @out = await Events(
146 this,
147 "activity",
148 @event,
149 cancellationToken
150 );
151
152 var res = (Response?)@out ?? throw new Exception("expected activity response");
153 _logger.LogDebug("res: {Response}", res);
154 return res;
155 }
156 catch (Exception ex)
157 {
158 _logger.LogError(ex, "Activity event error");
159 await Events(
160 this,
161 "error",
162 new ErrorEvent() { Exception = ex },
163 cancellationToken
164 );
165
166 return new Response(System.Net.HttpStatusCode.InternalServerError, ex.ToString());
167 }
168 }
169
170 public async Task<IResult> Do(HttpContext httpContext, CancellationToken cancellationToken = default)
171 {
172 try
173 {
174 var request = httpContext.Request;
175 var token = ExtractToken(request);
176 var activity = await ParseActivity(request);
177
178 if (activity is null)
179 {
180 return Results.BadRequest("Missing activity");
181 }
182
183 var data = new Dictionary<string, object?>
184 {
185 ["Request.TraceId"] = httpContext.TraceIdentifier
186 };
187
188 foreach (var pair in httpContext.Items)
189 {
190 var key = pair.Key.ToString();
191
192 if (key is null) continue;
193
194 data[key] = pair.Value;
195 }
196
197 var res = await Do(new ActivityEvent()
198 {
199 Token = token,
200 Activity = activity,
201 Extra = data,
202 Services = httpContext.RequestServices
203 }, cancellationToken);
204
205 // convert response metadata to headers
206 foreach (var (key, value) in res.Meta)
207 {
208 var str = value?.ToString();
209 if (string.IsNullOrEmpty(str)) continue;
210 httpContext.Response.Headers.Append($"X-Teams-{char.ToUpper(key[0]) + key[1..]}", str);
211 }
212
213 return Results.Json(
214 res.Body,
215 _jsonSerializerOptions,
216 contentType: null,
217 statusCode: (int)res.Status
218 );
219 }
220 catch (Exception ex)
221 {
222 _logger.LogError(ex, "HTTP activity error");
223 await Events(
224 this,
225 "error",
226 new ErrorEvent() { Exception = ex },
227 cancellationToken
228 );
229
230 return Results.Problem(detail: ex.Message, statusCode: 500);
231 }
232 }
233
234 public JsonWebToken ExtractToken(HttpRequest httpRequest)
235 {
236 var authHeader = httpRequest.Headers.Authorization.FirstOrDefault() ?? throw new UnauthorizedAccessException();
237 return new JsonWebToken(authHeader.Replace("Bearer ", ""));
238 }
239
240 public async Task<Activity?> ParseActivity(HttpRequest httpRequest)
241 {
242 httpRequest.EnableBuffering();
243
244 if (httpRequest.Body.CanSeek)
245 {
246 // reset the stream position to the beginning in case it was read before
247 httpRequest.Body.Position = 0;
248 }
249
250 using StreamReader sr = new(httpRequest.Body);
251 var body = await sr.ReadToEndAsync();
252 Activity? activity = JsonSerializer.Deserialize<Activity>(body);
253
254 return activity;
255 }
256}
257