This repository has been archived by the owner on Nov 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from anujab/master
Adding support for subscription renewal
- Loading branch information
Showing
9 changed files
with
227 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
using System.Collections.Generic; | ||
using System.Runtime.Caching; | ||
using System.Timers; | ||
|
||
namespace GraphWebhooks.Helpers | ||
{ | ||
public class SubscriptionCache | ||
{ | ||
static SubscriptionCache cache = null; | ||
|
||
private static ObjectCache objCache = MemoryCache.Default; | ||
private static CacheItemPolicy defaultPolicy = new CacheItemPolicy(); | ||
|
||
Timer timer; | ||
private SubscriptionCache() | ||
{ | ||
// Renew subscriptions every 10 minute. | ||
Timer renewalTimer = new Timer(10 * 60 * 1000) | ||
{ | ||
AutoReset = false | ||
}; | ||
renewalTimer.Elapsed += OnRenewal; | ||
renewalTimer.Start(); | ||
timer = renewalTimer; | ||
} | ||
|
||
public static SubscriptionCache GetSubscriptionCache() | ||
{ | ||
if(cache != null) | ||
{ | ||
return cache; | ||
} | ||
|
||
cache = new SubscriptionCache(); | ||
return cache; | ||
} | ||
|
||
|
||
private async void OnRenewal(object sender, ElapsedEventArgs e) | ||
{ | ||
var subscriptionstore = objCache.Get("subscription_store") as Dictionary<string, SubscriptionDetails>; | ||
|
||
foreach (var item in subscriptionstore) | ||
{ | ||
var response = await SubscriptionHelper.CheckSubscription(item.Key, item.Value.UserId, item.Value.RedirectUrl); | ||
if (response != null) | ||
{ | ||
await SubscriptionHelper.RenewSubscription(item.Key, item.Value.UserId, item.Value.RedirectUrl); | ||
} | ||
else | ||
{ | ||
await SubscriptionHelper.CreateSubscription(item.Value.UserId, item.Value.RedirectUrl); | ||
} | ||
} | ||
|
||
timer.Start(); | ||
} | ||
|
||
// This sample temporarily stores the current subscription ID, client state, user object ID, and tenant ID. | ||
// This info is required so the NotificationController can retrieve an access token from the cache and validate the subscription. | ||
// Production apps typically use some method of persistent storage. | ||
public void SaveSubscriptionInfo(SubscriptionDetails subscriptionDetails) | ||
{ | ||
if (objCache["subscription_store"] == null) | ||
{ | ||
var subscriptionstore = new Dictionary<string, SubscriptionDetails>(); | ||
subscriptionstore.Add(subscriptionDetails.SubscriptionId, subscriptionDetails); | ||
|
||
objCache.Set(new CacheItem("subscription_store", subscriptionstore), defaultPolicy); | ||
} | ||
else | ||
{ | ||
var subscriptionstore = objCache.Get("subscription_store") as Dictionary<string, SubscriptionDetails>; | ||
subscriptionstore.Add(subscriptionDetails.SubscriptionId, subscriptionDetails); | ||
} | ||
} | ||
|
||
public SubscriptionDetails GetSubscriptionInfo(string subscriptionId) | ||
{ | ||
var subscriptionstore = objCache.Get("subscription_store") as Dictionary<string, SubscriptionDetails>; | ||
return subscriptionstore[subscriptionId]; | ||
} | ||
|
||
public Dictionary<string, SubscriptionDetails> DeleteAllSubscriptions() | ||
{ | ||
return objCache.Remove("subscription_store") as Dictionary<string, SubscriptionDetails>; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. | ||
* See LICENSE in the source repository root for complete license information. | ||
*/ | ||
|
||
namespace GraphWebhooks.Helpers | ||
{ | ||
public class SubscriptionDetails | ||
{ | ||
public string SubscriptionId { get; set; } | ||
public string ClientState { get; set; } | ||
public string UserId { get; set; } | ||
public string RedirectUrl { get; set; } | ||
|
||
internal SubscriptionDetails(string subscriptionId, string clientState, string userId, string redirectUrl) | ||
{ | ||
SubscriptionId = subscriptionId; | ||
ClientState = clientState; | ||
UserId = userId; | ||
RedirectUrl = redirectUrl; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
using Microsoft.Graph; | ||
using System; | ||
using System.Configuration; | ||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
|
||
namespace GraphWebhooks.Helpers | ||
{ | ||
public class SubscriptionHelper | ||
{ | ||
internal static string CurrentUserId | ||
{ | ||
get | ||
{ | ||
return ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; | ||
} | ||
} | ||
|
||
internal static async Task<Subscription> CreateSubscription(string baseUrl, string userId = null) | ||
{ | ||
var graphClient = GraphHelper.GetAuthenticatedClient(string.IsNullOrEmpty(userId) ? CurrentUserId : userId, baseUrl); | ||
|
||
var subscription = new Subscription | ||
{ | ||
Resource = "me/mailFolders('Inbox')/messages", | ||
ChangeType = "created", | ||
NotificationUrl = ConfigurationManager.AppSettings["ida:NotificationUrl"], | ||
ClientState = Guid.NewGuid().ToString(), | ||
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 15, 0) // shorter duration useful for testing | ||
}; | ||
|
||
var newSubscription = await graphClient.Subscriptions.Request().AddAsync(subscription); | ||
|
||
// This sample temporarily stores the current subscription ID, client state, and user object ID. | ||
// This info is required so the NotificationController, which is not authenticated, can retrieve | ||
// an access token from the cache and validate the subscription. | ||
// Production apps typically use some method of persistent storage. | ||
var subscriptionDetails = new SubscriptionDetails( | ||
newSubscription.Id, | ||
newSubscription.ClientState, | ||
CurrentUserId, | ||
baseUrl); | ||
|
||
SubscriptionCache.GetSubscriptionCache().SaveSubscriptionInfo(subscriptionDetails); | ||
|
||
return newSubscription; | ||
} | ||
|
||
|
||
internal static async Task<Subscription> RenewSubscription(string subscriptionId, string userId, string baseUrl) | ||
{ | ||
var graphClient = GraphHelper.GetAuthenticatedClient(userId, baseUrl); | ||
|
||
Subscription subscription = new Subscription | ||
{ | ||
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 15, 0) // shorter duration useful for testing | ||
}; | ||
|
||
return await graphClient.Subscriptions[subscriptionId].Request().UpdateAsync(subscription); | ||
} | ||
|
||
internal static async Task<Subscription> CheckSubscription(string subscriptionId, string userId, string baseUrl) | ||
{ | ||
var graphClient = GraphHelper.GetAuthenticatedClient(userId, baseUrl); | ||
return await graphClient.Subscriptions[subscriptionId].Request().GetAsync(); | ||
} | ||
|
||
internal static async Task DeleteSubscription(string subscriptionId, string baseUrl) | ||
{ | ||
var graphClient = GraphHelper.GetAuthenticatedClient(CurrentUserId, baseUrl); | ||
await graphClient.Subscriptions[subscriptionId].Request().DeleteAsync(); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.