diff --git a/Source/CompanyCommunicator.Common/Extensions/SendQueueMessageContentExtension.cs b/Source/CompanyCommunicator.Common/Extensions/SendQueueMessageContentExtension.cs
index 15f621bce..3b49b4521 100644
--- a/Source/CompanyCommunicator.Common/Extensions/SendQueueMessageContentExtension.cs
+++ b/Source/CompanyCommunicator.Common/Extensions/SendQueueMessageContentExtension.cs
@@ -7,6 +7,7 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Extensions
{
using System;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MessageQueues.SendQueue;
+ using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MicrosoftGraph;
///
/// Extension class for .
@@ -44,5 +45,28 @@ public static string GetConversationId(this SendQueueMessageContent message)
_ => throw new ArgumentException("Invalid recipient type"),
};
}
+
+ ///
+ /// Check if recipient guest user.
+ ///
+ /// Send Queue message.
+ /// Boolean indicating if it is a guest user.
+ public static bool IsRecipientGuestUser(this SendQueueMessageContent message)
+ {
+ var recipient = message.RecipientData;
+ if (recipient.RecipientType == RecipientDataType.User)
+ {
+ if (string.IsNullOrEmpty(recipient.UserData.UserType))
+ {
+ throw new ArgumentNullException(nameof(recipient.UserData.UserType));
+ }
+ else if (recipient.UserData.UserType.Equals(UserType.Guest, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/Source/CompanyCommunicator.Common/Extensions/UserExtensions.cs b/Source/CompanyCommunicator.Common/Extensions/UserExtensions.cs
index cb54b93cf..967489378 100644
--- a/Source/CompanyCommunicator.Common/Extensions/UserExtensions.cs
+++ b/Source/CompanyCommunicator.Common/Extensions/UserExtensions.cs
@@ -7,6 +7,7 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Extensions
{
using System;
using System.Collections.Generic;
+ using Microsoft.Graph;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories.UserData;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MicrosoftGraph;
@@ -45,7 +46,7 @@ public static IEnumerable> AsGroups(this IList userI
}
///
- /// Get the user type for a user.
+ /// Get the userType for a user.
///
/// the user principal name.
/// the user type such as Member or Guest.
@@ -58,5 +59,25 @@ public static string GetUserType(this string userPrincipalName)
return userPrincipalName.ToLower().Contains("#ext#") ? UserType.Guest : UserType.Member;
}
+
+ ///
+ /// Get the userType for a user.
+ ///
+ /// the microsoft graph user.
+ /// the user type such as Member or Guest.
+ public static string GetUserType(this User user)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ if (!string.IsNullOrEmpty(user.UserType))
+ {
+ return user.UserType;
+ }
+
+ return user.UserPrincipalName.GetUserType();
+ }
}
}
diff --git a/Source/CompanyCommunicator.Common/Policies/PollyPolicy.cs b/Source/CompanyCommunicator.Common/Policies/PollyPolicy.cs
new file mode 100644
index 000000000..70fe072a0
--- /dev/null
+++ b/Source/CompanyCommunicator.Common/Policies/PollyPolicy.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+//
+
+namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Policies
+{
+ using System;
+ using System.Net;
+ using Microsoft.Graph;
+ using Polly;
+ using Polly.Contrib.WaitAndRetry;
+ using Polly.Retry;
+
+ ///
+ /// Polly policies.
+ ///
+ public class PollyPolicy
+ {
+ ///
+ /// Get the graph retry policy.
+ ///
+ /// the number of max attempts.
+ /// A retry policy that can be applied to async delegates.
+ public static AsyncRetryPolicy GetGraphRetryPolicy(int maxAttempts)
+ {
+ var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: maxAttempts);
+
+ // Only Handling 502 Bad Gateway Exception
+ // Other exception such as 429, 503, 504 is handled by default by Graph SDK.
+ return Policy
+ .Handle(e =>
+ e.StatusCode == HttpStatusCode.BadGateway)
+ .WaitAndRetryAsync(delay);
+ }
+ }
+}
diff --git a/Source/CompanyCommunicator.Common/Repositories/SentNotificationData/SentNotificationDataEntity.cs b/Source/CompanyCommunicator.Common/Repositories/SentNotificationData/SentNotificationDataEntity.cs
index 1b4ff48b9..9ce36a3eb 100644
--- a/Source/CompanyCommunicator.Common/Repositories/SentNotificationData/SentNotificationDataEntity.cs
+++ b/Source/CompanyCommunicator.Common/Repositories/SentNotificationData/SentNotificationDataEntity.cs
@@ -40,6 +40,12 @@ public class SentNotificationDataEntity : TableEntity
///
public static readonly int FinalFaultedStatusCode = -2;
+ ///
+ /// This value indicates that operation is not supported by Azure Function and will
+ /// not be processed further.
+ ///
+ public static readonly int NotSupportedStatusCode = -3;
+
///
/// String indicating the recipient type for the given notification was a user.
///
@@ -169,6 +175,11 @@ public class SentNotificationDataEntity : TableEntity
///
public string TenantId { get; set; }
+ ///
+ /// Gets or sets the user type for the recipient.
+ ///
+ public string UserType { get; set; }
+
///
/// Gets or sets the user id for the recipient.
///
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.Designer.cs b/Source/CompanyCommunicator.Common/Resources/Strings.Designer.cs
index c32ebaa71..c7c035340 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.Designer.cs
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.Designer.cs
@@ -384,6 +384,15 @@ public static string Guest {
}
}
+ ///
+ /// Looks up a localized string similar to Guest User not supported.
+ ///
+ public static string GuestUserNotSupported {
+ get {
+ return ResourceManager.GetString("GuestUserNotSupported", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Member.
///
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.ar-SA.resx b/Source/CompanyCommunicator.Common/Resources/Strings.ar-SA.resx
index 93b62d1af..8db19a1fb 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.ar-SA.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.ar-SA.resx
@@ -248,12 +248,15 @@
مقيّد
- User Type
+ نوع المستخدم
- Guest
+ الضيف
- Member
+ العضو
+
+
+ المستخدم الضيف غير مدعم
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.de-DE.resx b/Source/CompanyCommunicator.Common/Resources/Strings.de-DE.resx
index 4b0f61799..903c78d0e 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.de-DE.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.de-DE.resx
@@ -160,7 +160,7 @@
{0} (kopieren)
- Da hat etwas nicht geklappt. Versuchen Sie noch Mal, die Ergebnisse zu exportieren.
+ Etwas ist schief gegangen. Versuchen Sie noch Mal, die Ergebnisse zu exportieren.
Fehlgeschlagen
@@ -221,7 +221,7 @@
Ihre Datei kann jetzt herunter geladen werden. Eine Kopie ist auch auf OneDrive verfügbar.
- Da hat etwas nicht geklappt. Versuchen Sie noch Mal, die Ergebnisse zu exportieren.
+ Etwas ist schief gegangen. Versuchen Sie noch Mal, die Ergebnisse zu exportieren.
Die Benachrichtigung hat {0} Gruppen als Ihre Empfänger. Sie sollte {1} Gruppen nicht über schreiten.
@@ -256,4 +256,7 @@
Mitglied
+
+ Gastbenutzer nicht unterstützt
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.es-ES.resx b/Source/CompanyCommunicator.Common/Resources/Strings.es-ES.resx
index eb9c0ac4d..a10e9f240 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.es-ES.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.es-ES.resx
@@ -248,12 +248,15 @@
Limitado
- User Type
+ Tipo de usuario
- Guest
+ Invitado
- Member
+ Miembro
+
+
+ No se admite el usuario invitado
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.fr-FR.resx b/Source/CompanyCommunicator.Common/Resources/Strings.fr-FR.resx
index 532a7037b..f3e3446af 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.fr-FR.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.fr-FR.resx
@@ -256,4 +256,7 @@
Membre
+
+ Utilisateur invité non pris en charge
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.he-IL.resx b/Source/CompanyCommunicator.Common/Resources/Strings.he-IL.resx
index e2cf20e0d..14d6186b4 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.he-IL.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.he-IL.resx
@@ -256,4 +256,7 @@
חבר
+
+ משתמש אורח אינו נתמך
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.ja-JP.resx b/Source/CompanyCommunicator.Common/Resources/Strings.ja-JP.resx
index 6014cbccd..d767a315c 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.ja-JP.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.ja-JP.resx
@@ -256,4 +256,7 @@
メンバー
+
+ ゲスト ユーザーはサポートされていません
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.ko-KR.resx b/Source/CompanyCommunicator.Common/Resources/Strings.ko-KR.resx
index 18e66d935..d17abbc8e 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.ko-KR.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.ko-KR.resx
@@ -256,4 +256,7 @@
구성원
+
+ 게스트 사용자가 지원되지 않음
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.pt-BR.resx b/Source/CompanyCommunicator.Common/Resources/Strings.pt-BR.resx
index d27cfc847..8a3795015 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.pt-BR.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.pt-BR.resx
@@ -248,12 +248,15 @@
Limitado
- User Type
+ Tipo de Usuário
- Guest
+ Convidado
- Member
+ Membro
+
+
+ Não há suporte para o Usuário Convidado
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploc.resx b/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploc.resx
index 128ace755..ed9fb26e6 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploc.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploc.resx
@@ -118,142 +118,145 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- _Cбйтд©т чфџя ЇЋ дdмїл fфґ pёгмїssїюи тб vїзщ тнїs dдтд.ЍѝцзджфЍ_
+ _Cфйтд©т чфцґ ЇЋ дdмїй fбг pэямїssїбп тю vїзш тнїs dдтд.ЍѝцзджфЍ_
- _Дpp Йфт ЇиsтдllзdЍѝц_
+ _Дpp Лфт ЇпsтдllёdЍѝц_
- _Dэlїvёяу SтдтµsЍѝ_
+ _Dёlїvзґу SтдтµsЍѝ_
- _Эжpбгтэd БчЍѝ_
+ _Эжpюятзd БўЍѝ_
- _Эжpфгт ЋїмэSтдмpЍѝ_
+ _Эжpюгт ЋїмзSтдмpЍѝ_
- _Mэssдgз ЋїтlёЍѝ_
+ _Mёssдgэ ЋїтlёЍѝ_
- _Sєлт ЋїмёSтдмpЍѝ_
+ _Sёпт ЋїмёSтдмpЍѝ_
- _Sтдтµs ЃёдsюпЍѝ_
+ _Sтдтџs ЯєдsблЍѝ_
- _Ћёдм ЇDЍ_
+ _Ћздм ЇDЍ_
- _Ћёдм ИдмэЍ_
+ _Ћэдм ИдмёЍ_
_ЦPП_
- _Џsєя ЇDЍ_
+ _Џsзґ ЇDЍ_
- _ИдмёЍ_
+ _ПдмзЍ_
- _{0} (©фpу)Ѝѝ_
+ _{0} (©юpў)Ѝѝ_
- _Sбмэтћїпg щэлт шяюпg. Ћґч эжpбґтїйg тнё гёsµlтs дgдїй.ЍѝцзджфЍ_
+ _Sфмєтнїиg щзит щяблg. Ћґў єжpбятїпg тнз яєsџlтs дgдїп.ЍѝцзджфЍ_
- _FдїlєdЍ_
+ _FдїlэdЍ_
- _Цsёя дpplї©дтїюй лбт fфцпd. Mдкё sџґз тћз Цsэг дpp їs µplбдdэd тб убџґ фґgдлїzдтїбл's дpp ©дтдlфg.ЍѝцзджфЍѝцзджфЍ_
+ _Џsєя дpplї©дтїби пфт fфџйd. Mдкз sцгз тћз Цsєг дpp їs цplюдdзd тф чбџґ фґgдпїzдтїби's дpp ©дтдlюg.ЍѝцзджфЍѝцзджфЍ_
- _Fдїlєd тю fїлd тћэ Џsэґ дpplї©дтїфй: {0} їй тнє югgдиїzдтїюл's дpp ©дтдlбg.ЍѝцзджфЍѝцз_
+ _Fдїlєd тю fїйd тћз Џsёґ дpplї©дтїюп: {0} їп тнє бгgдиїzдтїфй's дpp ©дтдlбg.ЍѝцзджфЍѝцз_
{0} - app Id. (GUID).
- _Fдїlєd тф ©гёдтє ©блvзгsдтїюл. Єґябг мєssдgё: {0}Ѝѝцзджф_
+ _Fдїlєd тф ©ґєдтэ ©бпvёґsдтїбл. Єгяфя мєssдgё: {0}Ѝѝцзджф_
- _Fдїlєd тф ©гёдтз ©фиvєгsдтїби щїтћ тёдмs цsзя: {0}. Ёж©єpтїюи: {1}ЍѝцзджфЍѝц_
+ _Fдїlєd тф ©яздтё ©юпvєяsдтїбл щїтћ тёдмs џsєг: {0}. Єж©зpтїюп: {1}ЍѝцзджфЍѝц_
- _Fдїlёd тф ©гэдтє ©бйvзгsдтїюй. Гёqµзsт тћгфттlэd. Єягюґ мєssдgё: {0}"ЍѝцзджфЍѝц_
+ _Fдїlєd тю ©ґэдтё ©юлvєґsдтїбй. Язqџзsт тнгюттlзd. Ёггфг мзssдgє: {0}"ЍѝцзджфЍѝц_
- _Fдїlэd тф fїйd тнз тэдм {0} їй DЪ.Ѝѝцзд_
+ _Fдїlэd тб fїпd тћє тєдм {0} їй DБ.Ѝѝцзд_
- _Fдїlэd тю sул© дll цsєяs. Sтдтџs Cфdз: {0} Ёж©зpтїфп: {1}ЍѝцзджфЍѝ_
+ _Fдїlєd тю sўп© дll цsэґs. Sтдтцs Cбdё: {0} Ёж©зpтїбп: {1}ЍѝцзджфЍѝ_
- _Fдїlзd тф gёт ©бпvєяsдтїюл їd fбг цsэґ: {0}. Sтдтµs Cюdє: {1} Эж©єpтїюл: {2}ЍѝцзджфЍѝцз_
+ _Fдїlёd тб gёт ©бпvєгsдтїюп їd fбя џsэя: {0}. Sтдтµs Cфdє: {1} Ёж©єpтїфи: {2}ЍѝцзджфЍѝцз_
- _Fдїlёd тф gёт мзмьзґs fфґ gгюџp {0}: {1}Ѝѝцздж_
+ _Fдїlєd тф gэт мёмьзяs fюя gябџp {0}: {1}Ѝѝцздж_
- _Fдїlёd тб gєт мэмъзґs fюг тэдм {0}: {1}Ѝѝцздж_
+ _Fдїlёd тю gёт мємьэґs fюґ тєдм {0}: {1}Ѝѝцздж_
- _Fдїlєd тб їиsтдll дpplї©дтїюй fюя џsєґ: {0}. Эж©эpтїфл: {1}ЍѝцзджфЍѝ_
+ _Fдїlзd тф їиsтдll дpplї©дтїбй fбг џsзг: {0}. Зж©ёpтїбл: {1}ЍѝцзджфЍѝ_
- _Fдїlэd тб pґзpдгэ тнё мёssдgє fфя sэлdїлg:{0}Ѝѝцзджф_
+ _Fдїlёd тю pґёpдґё тћє мєssдgё fфґ sєлdїлg:{0}Ѝѝцзджф_
- _Ћћїs fїlз ©фйтдїпs тнё ґёsџlтs ўфц ёжpбґтёd.Ѝѝцзджф_
+ _Ћћїs fїlэ ©бптдїпs тнё ґєsцlтs чфц ёжpфгтэd.Ѝѝцзджф_
- _Ћћё lїиќ fбг тћїs dфшйlюдd ндs зжpїяєd. Єжpюят тћё яёsцlтs дgдїп.ЍѝцзджфЍѝц_
+ _Ћћё lїиќ fфя тћїs dфшлlбдd ћдs єжpїґєd. Зжpбят тћэ ґєsџlтs дgдїй.ЍѝцзджфЍѝц_
- _ЄжpбятDдтдЍѝ_
+ _ЗжpюгтDдтдЍѝ_
- _Mєssдgє_DэlїvзгуЍѝ_
+ _Mёssдgз_DёlїvэячЍѝ_
- _MзтдdдтдЍ_
+ _MётдdдтдЍ_
- _Чфµг fїlэ їs яздdч тю dфшиlбдd. Д ©фpч їs дlsб дvдїlдъlэ їп ЮиэDяїvз.ЍѝцзджфЍѝц_
+ _Чфµґ fїlє їs яэдdу тб dющпlбдd. Д ©фpў їs дlsб дvдїlдьlз їй ЮйзDгїvё.ЍѝцзджфЍѝц_
- _Sюмєтћїлg щёлт щгбиg. Ћяч зжpюґтїиg тнз яэsцlтs дgдїи.ЍѝцзджфЍ_
+ _Sюмєтнїлg шёлт шґбпg. Ћяч єжpфятїиg тнє язsцlтs дgдїй.ЍѝцзджфЍ_
- _Ћћэ йбтїfї©дтїюи ндs {0} gяюµps дs їтs гё©їpїєйтs. Їт sнбџldл'т єж©эєd {1} gґбµps.ЍѝцзджфЍѝцзд_
+ _Ћћё ифтїfї©дтїюл ћдs {0} gяюцps дs їтs яё©їpїзитs. Їт sнфџldл'т ёж©єэd {1} gябµps.ЍѝцзджфЍѝцзд_
- _Ћнё пбтїfї©дтїюй ндs {0} яфsтєґs дs їтs гз©їpїёптs. Їт sћфцldи'т єж©ззd {1} яюsтзгs.ЍѝцзджфЍѝцздж_
+ _Ћћз йбтїfї©дтїфи ћдs {0} ґюsтзяs дs їтs яє©їpїзптs. Їт sнфџldл'т зж©зєd {1} ґфsтёгs.ЍѝцзджфЍѝцздж_
- _Ћнє лфтїfї©дтїюи ћдs {0} тєдмs дs їтs яє©їpїёйтs. Їт sнюџldи'т эж©єэd {1} тєдмs.ЍѝцзджфЍѝцзд_
+ _Ћнё йбтїfї©дтїюи ндs {0} тэдмs дs їтs ґє©їpїэйтs. Їт sнфµldй'т зж©єэd {1} тэдмs.ЍѝцзджфЍѝцзд_
_ЮЌ_
- _Pзґмїssїбл dє©lїлёd. Шё шїll пбт pгб©ёэd шїтн тћё ёжpюгт.ЍѝцзджфЍѝ_
+ _Pёґмїssїбп dз©lїлзd. Щэ щїll лбт pяф©єэd шїтћ тнё зжpфґт.ЍѝцзджфЍѝ_
- _Ґз©їpїёит Ифт FфµйdЍѝц_
+ _Гз©їpїєит Иют FбµпdЍѝц_
- _Sµ©©ёёdзdЍ_
+ _Sџ©©эєdэdЍ_
- _ЋћябттlзdЍ_
+ _ЋнґбттlёdЍ_
- _Цsэґ ЋўpэЍ_
+ _Цsёя ЋўpєЍ_
_GµёsтЍ_
- _MємьёяЍ_
+ _MзмвэгЍ_
+
+
+ _Gµэsт Џsэя йфт sцppфґтэdЍѝцз_
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploca.resx b/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploca.resx
index d392634b0..c7d092c9f 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploca.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.qps-ploca.resx
@@ -118,142 +118,145 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- _Cфлтд©т ўбµг ЇЋ дdмїл fфг pёгмїssїфп тю vїєщ тћїs dдтд.ЍѝцзджфЍ_
+ _Cблтд©т уфцґ ЇЋ дdмїп fюя pёґмїssїфп тб vїзш тнїs dдтд.ЍѝцзджфЍ_
- _Дpp Лфт ЇлsтдllёdЍѝц_
+ _Дpp Пфт ЇиsтдllзdЍѝц_
- _Dєlїvэяў SтдтцsЍѝ_
+ _Dзlїvзгч SтдтџsЍѝ_
- _Єжpфятёd bўЍѝ_
+ _Эжpбгтєd bўЍѝ_
- _Зжpфгт ЋїмзSтдмpЍѝ_
+ _Эжpфят ЋїмёSтдмpЍѝ_
- _Mєssдgё ЋїтlёЍѝ_
+ _Mёssдgё ЋїтlєЍѝ_
- _Sєлт ЋїмёSтдмpЍѝ_
+ _Sёлт ЋїмёSтдмpЍѝ_
- _Sтдтµs ЯєдsблЍѝ_
+ _Sтдтџs ЯёдsфйЍѝ_
- _Ћэдм ЇDЍ_
+ _Ћздм ЇDЍ_
- _Ћздм ПдмзЍ_
+ _Ћэдм ПдмєЍ_
_ЦPЛ_
- _Џsзг ЇDЍ_
+ _Цsзґ ЇDЍ_
- _ЛдмєЍ_
+ _ЙдмэЍ_
- _{0} (©юpу)Ѝѝ_
+ _{0} (©фpу)Ѝѝ_
- _Sюмётнїпg шєйт щґюиg. Ћгч ёжpбятїлg тћз язsџlтs дgдїи.ЍѝцзджфЍ_
+ _Sфмєтћїйg шэпт щяюлg. Ћгч єжpюятїлg тћє ґєsџlтs дgдїл.ЍѝцзджфЍ_
- _FдїlёdЍ_
+ _FдїlэdЍ_
- _Цsєґ дpplї©дтїюй ифт fюцйd. Mдќэ sџґє тнз Џsёг дpp їs µplфдdєd тб ўюця фгgдйїzдтїфп's дpp ©дтдlфg.ЍѝцзджфЍѝцзджфЍ_
+ _Џsэґ дpplї©дтїюй йют fфµпd. Mдќє sµяє тнє Џsєя дpp їs µplфдdёd тф ўюцг бяgдлїzдтїби's дpp ©дтдlфg.ЍѝцзджфЍѝцзджфЍ_
- _Fдїlєd тю fїпd тнэ Џsзг дpplї©дтїюл: {0} їп тнє юґgдйїzдтїбл's дpp ©дтдlфg.ЍѝцзджфЍѝцз_
+ _Fдїlэd тб fїиd тћз Цsзг дpplї©дтїфй: {0} їл тнє бяgдпїzдтїфй's дpp ©дтдlбg.ЍѝцзджфЍѝцз_
{0} - app Id. (GUID).
- _Fдїlэd тю ©яёдтэ ©фиvёгsдтїюй. Зґяфґ мэssдgё: {0}Ѝѝцзджф_
+ _Fдїlзd тю ©гэдтё ©бпvёґsдтїюи. Зяґфг мёssдgэ: {0}Ѝѝцзджф_
- _Fдїlэd тю ©ґздтё ©фиvзґsдтїюл шїтћ тэдмs џsзя: {0}. Эж©зpтїюл: {1}ЍѝцзджфЍѝц_
+ _Fдїlёd тф ©гздтё ©бпvзгsдтїюл шїтћ тёдмs µsэґ: {0}. Эж©зpтїюп: {1}ЍѝцзджфЍѝц_
- _Fдїlєd тф ©яздтё ©фиvєгsдтїюп. Ѓзqџєsт тнгфттlзd. Эґгюг мёssдgэ: {0}"ЍѝцзджфЍѝц_
+ _Fдїlёd тб ©ґёдтє ©фпvзґsдтїфп. Язqџэsт тнґфттlзd. Ёґгфґ мэssдgэ: {0}"ЍѝцзджфЍѝц_
- _Fдїlэd тю fїиd тнэ тздм {0} їп DБ.Ѝѝцзд_
+ _Fдїlёd тб fїпd тћэ тёдм {0} їй Db.Ѝѝцзд_
- _Fдїlєd тю sуп© дll µsзяs. Sтдтцs Cбdэ: {0} Ёж©эpтїфл: {1}ЍѝцзджфЍѝ_
+ _Fдїlэd тб sчл© дll цsєґs. Sтдтџs Cюdэ: {0} Зж©єpтїфй: {1}ЍѝцзджфЍѝ_
- _Fдїlзd тб gёт ©бпvёґsдтїюй їd fфґ µsэг: {0}. Sтдтµs Cфdє: {1} Єж©ёpтїюй: {2}ЍѝцзджфЍѝцз_
+ _Fдїlєd тю gэт ©бпvзяsдтїфп їd fбя цsэя: {0}. Sтдтџs Cюdз: {1} Эж©зpтїюи: {2}ЍѝцзджфЍѝцз_
- _Fдїlєd тб gзт мємьёяs fюг gґбџp {0}: {1}Ѝѝцздж_
+ _Fдїlєd тф gёт мємьёяs fбг gяфµp {0}: {1}Ѝѝцздж_
- _Fдїlєd тю gєт мёмъэґs fюя тєдм {0}: {1}Ѝѝцздж_
+ _Fдїlзd тб gєт мэмъєгs fюя тэдм {0}: {1}Ѝѝцздж_
- _Fдїlэd тю їиsтдll дpplї©дтїбп fбґ цsзґ: {0}. Эж©зpтїфп: {1}ЍѝцзджфЍѝ_
+ _Fдїlёd тю їпsтдll дpplї©дтїфи fфґ µsзґ: {0}. Ёж©зpтїбп: {1}ЍѝцзджфЍѝ_
- _Fдїlєd тю pяёpдяэ тћэ мёssдgё fюг sэиdїйg:{0}Ѝѝцзджф_
+ _Fдїlєd тю pяёpдяє тћз мзssдgё fюґ sєпdїпg:{0}Ѝѝцзджф_
- _Ћнїs fїlз ©юлтдїйs тнє яєsџlтs ўюџ ёжpбґтєd.Ѝѝцзджф_
+ _Ћнїs fїlз ©юлтдїйs тћэ гєsµlтs уюц зжpбґтєd.Ѝѝцзджф_
- _Ћћз lїиќ fфг тћїs dфшлlфдd ћдs єжpїгєd. Єжpюят тнё яэsµlтs дgдїи.ЍѝцзджфЍѝц_
+ _Ћћё lїик fюг тћїs dфшлlюдd ћдs ёжpїґёd. Єжpюґт тнз яэsµlтs дgдїи.ЍѝцзджфЍѝц_
- _ЗжpфгтDдтдЍѝ_
+ _ЁжpюгтDдтдЍѝ_
- _Mєssдgз_DєlїvэґўЍѝ_
+ _Mєssдgё_DёlїvэяуЍѝ_
- _MэтдdдтдЍ_
+ _MётдdдтдЍ_
- _Уюџг fїlз їs гєдdў тю dфшлlбдd. Д ©фpу їs дlsф дvдїlдьlє їй ФлєDґїvз.ЍѝцзджфЍѝц_
+ _Чюµя fїlё їs гздdу тф dюшпlюдd. Д ©юpў їs дlsб дvдїlдвlэ їи ЮпзDяїvё.ЍѝцзджфЍѝц_
- _Sфмзтћїпg шэйт шгюлg. Ћґў эжpбгтїиg тћз ґёsµlтs дgдїп.ЍѝцзджфЍ_
+ _Sюмётћїиg щёит щгбиg. Ћгў єжpюґтїлg тћз ґєsµlтs дgдїй.ЍѝцзджфЍ_
- _Ћћз пютїfї©дтїюи ћдs {0} gгбцps дs їтs ґэ©їpїёптs. Їт sћфµldй'т зж©ёєd {1} gґюµps.ЍѝцзджфЍѝцзд_
+ _Ћнє пютїfї©дтїфл ндs {0} gґбµps дs їтs яє©їpїэитs. Їт sћбџldп'т зж©єєd {1} gяюџps.ЍѝцзджфЍѝцзд_
- _Ћћз пбтїfї©дтїюй ндs {0} гфsтєяs дs їтs гє©їpїєитs. Їт sнюµldл'т зж©єёd {1} ґбsтэяs.ЍѝцзджфЍѝцздж_
+ _Ћнё пютїfї©дтїфй ћдs {0} яюsтёгs дs їтs гё©їpїэптs. Їт sћбцldи'т єж©ёєd {1} ґфsтєяs.ЍѝцзджфЍѝцздж_
- _Ћћё пбтїfї©дтїюп ћдs {0} тэдмs дs їтs ґз©їpїэитs. Їт sнюµldл'т єж©эзd {1} тєдмs.ЍѝцзджфЍѝцзд_
+ _Ћћз пфтїfї©дтїюп ћдs {0} тэдмs дs їтs яэ©їpїёлтs. Їт sнфџldп'т ёж©ээd {1} тздмs.ЍѝцзджфЍѝцзд_
_ФЌ_
- _Pёґмїssїюи dз©lїйёd. Щэ щїll лют pґб©єзd щїтн тћз єжpюгт.ЍѝцзджфЍѝ_
+ _Pзґмїssїфи dз©lїлёd. Щє щїll лбт pгю©ёєd шїтн тћє єжpбґт.ЍѝцзджфЍѝ_
- _Ґз©їpїєпт Йют FбцпdЍѝц_
+ _Яё©їpїзйт Пфт FюцпdЍѝц_
- _Sµ©©ёзdёdЍ_
+ _Sц©©ээdзdЍ_
- _ЋнгфттlэdЍ_
+ _ЋћяюттlёdЍ_
- _Цsёя ЋуpёЍ_
+ _Цsзґ ЋуpёЍ_
- _GцёsтЍ_
+ _GµєsтЍ_
- _MємьёгЍ_
+ _MємвёяЍ_
+
+
+ _Gµэsт Цsёя ифт sџppюятзdЍѝцз_
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.qps-plocm.resx b/Source/CompanyCommunicator.Common/Resources/Strings.qps-plocm.resx
index 03486eb26..65d196353 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.qps-plocm.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.qps-plocm.resx
@@ -118,142 +118,145 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- _Cфлтд©т чюџґ ЇЋ дdмїп fбг pёгмїssїюи тб vїзш тћїs dдтд.ЍѝцзджфЍ_
+ _Cфлтд©т уфџг ЇЋ дdмїп fфя pэґмїssїюи тф vїєш тћїs dдтд.ЍѝцзджфЍ_
- _Дpp Иют ЇлsтдllєdЍѝц_
+ _Дpp Йфт ЇпsтдllзdЍѝц_
- _Dэlїvзяу SтдтµsЍѝ_
+ _Dэlїvзяу SтдтџsЍѝ_
- _Ёжpфґтзd БуЍѝ_
+ _Ёжpюгтєd bуЍѝ_
- _Ёжpюгт ЋїмэSтдмpЍѝ_
+ _Єжpбят ЋїмёSтдмpЍѝ_
- _Mзssдgэ ЋїтlзЍѝ_
+ _Mэssдgэ ЋїтlэЍѝ_
- _Sэпт ЋїмзSтдмpЍѝ_
+ _Sэит ЋїмєSтдмpЍѝ_
- _Sтдтцs ҐєдsфлЍѝ_
+ _Sтдтµs ЃэдsюйЍѝ_
- _Ћёдм ЇDЍ_
+ _Ћздм ЇDЍ_
_Ћэдм ЙдмзЍ_
- _ЦPИ_
+ _ЏPЛ_
- _Цsёґ ЇDЍ_
+ _Џsзґ ЇDЍ_
- _ПдмёЍ_
+ _ЛдмэЍ_
- _{0} (©фpу)Ѝѝ_
+ _{0} (©фpч)Ѝѝ_
- _Sюмэтћїпg шзпт щґфлg. Ћгч эжpюгтїиg тнє яєsџlтs дgдїп.ЍѝцзджфЍ_
+ _Sбмєтнїпg шэпт щгфйg. Ћґу зжpюятїлg тнё язsџlтs дgдїл.ЍѝцзджфЍ_
- _FдїlєdЍ_
+ _FдїlёdЍ_
- _Џsєя дpplї©дтїбп ибт fюциd. Mдкє sџяз тнё Џsёґ дpp їs џplюдdєd тб ўюцг фгgдиїzдтїфп's дpp ©дтдlюg.ЍѝцзджфЍѝцзджфЍ_
+ _Цsэґ дpplї©дтїюй иют fюџиd. Mдќє sџяз тћє Цsэг дpp їs цplюдdёd тю чбця югgдпїzдтїфл's дpp ©дтдlбg.ЍѝцзджфЍѝцзджфЍ_
- _Fдїlзd тф fїлd тнэ Џsєґ дpplї©дтїфй: {0} їл тћз бґgдйїzдтїфп's дpp ©дтдlфg.ЍѝцзджфЍѝцз_
+ _Fдїlєd тф fїлd тћэ Цsёя дpplї©дтїюи: {0} їп тнє фґgдлїzдтїюп's дpp ©дтдlбg.ЍѝцзджфЍѝцз_
{0} - app Id. (GUID).
- _Fдїlэd тб ©гёдтє ©фйvёяsдтїби. Зяґюг мёssдgє: {0}Ѝѝцзджф_
+ _Fдїlзd тб ©ґэдтё ©юйvзгsдтїюп. Зггбя мзssдgё: {0}Ѝѝцзджф_
- _Fдїlёd тб ©гєдтэ ©юлvєгsдтїюи шїтн тздмs џsєя: {0}. Єж©зpтїюй: {1}ЍѝцзджфЍѝц_
+ _Fдїlэd тб ©яздтз ©юиvєґsдтїби шїтн тєдмs µsёґ: {0}. Ёж©эpтїюл: {1}ЍѝцзджфЍѝц_
- _Fдїlєd тб ©гэдтз ©фйvёяsдтїфл. Яэqџзsт тћгфттlёd. Єяяюг мзssдgз: {0}"ЍѝцзджфЍѝц_
+ _Fдїlєd тю ©гёдтз ©блvзґsдтїфл. Ґєqµзsт тнґбттlєd. Єяґюя мзssдgз: {0}"ЍѝцзджфЍѝц_
- _Fдїlзd тю fїлd тћё тздм {0} їп Db.Ѝѝцзд_
+ _Fдїlэd тф fїлd тћэ тёдм {0} їл DЪ.Ѝѝцзд_
- _Fдїlєd тю sчл© дll цsёґs. Sтдтцs Cбdэ: {0} Зж©ёpтїфп: {1}ЍѝцзджфЍѝ_
+ _Fдїlзd тю sўл© дll µsзяs. Sтдтµs Cбdз: {0} Єж©єpтїфи: {1}ЍѝцзджфЍѝ_
- _Fдїlзd тф gєт ©бйvзяsдтїбл їd fюґ цsэг: {0}. Sтдтџs Cбdє: {1} Єж©зpтїюй: {2}ЍѝцзджфЍѝцз_
+ _Fдїlзd тю gэт ©фйvзгsдтїби їd fбґ џsзя: {0}. Sтдтцs Cбdє: {1} Эж©ёpтїюй: {2}ЍѝцзджфЍѝцз_
- _Fдїlэd тб gєт мзмъєгs fюя gгбцp {0}: {1}Ѝѝцздж_
+ _Fдїlзd тб gэт мзмъэяs fюг gябµp {0}: {1}Ѝѝцздж_
- _Fдїlєd тф gёт мёмьєяs fфг тёдм {0}: {1}Ѝѝцздж_
+ _Fдїlэd тю gёт мёмьёгs fюя тёдм {0}: {1}Ѝѝцздж_
- _Fдїlёd тф їпsтдll дpplї©дтїфл fбя џsєг: {0}. Зж©эpтїбл: {1}ЍѝцзджфЍѝ_
+ _Fдїlёd тю їлsтдll дpplї©дтїбл fюг µsэг: {0}. Зж©зpтїфи: {1}ЍѝцзджфЍѝ_
- _Fдїlзd тф pяєpдґз тнэ мзssдgё fюг sэлdїпg:{0}Ѝѝцзджф_
+ _Fдїlёd тф pгёpдгэ тћэ мёssдgз fбя sєлdїлg:{0}Ѝѝцзджф_
- _Ћћїs fїlз ©юлтдїпs тнз ґєsцlтs чюц єжpфґтєd.Ѝѝцзджф_
+ _Ћћїs fїlэ ©фйтдїиs тнз гёsцlтs ўюџ зжpбятзd.Ѝѝцзджф_
- _Ћнэ lїпк fфґ тнїs dющйlбдd ћдs эжpїґэd. Єжpбґт тћё язsµlтs дgдїи.ЍѝцзджфЍѝц_
+ _Ћнё lїйк fбя тнїs dфшпlбдd ндs ёжpїгёd. Зжpфґт тћё яэsџlтs дgдїл.ЍѝцзджфЍѝц_
- _ЭжpфґтDдтдЍѝ_
+ _ЄжpфятDдтдЍѝ_
- _Mёssдgз_DєlїvёґчЍѝ_
+ _Mзssдgз_DёlїvєгўЍѝ_
- _MэтдdдтдЍ_
+ _MётдdдтдЍ_
- _Ўбџґ fїlё їs гєдdў тб dбшиlбдd. Д ©бpч їs дlsф дvдїlдъlё їй ЮиёDґїvз.ЍѝцзджфЍѝц_
+ _Чфµя fїlё їs яздdу тб dющлlфдd. Д ©фpў їs дlsю дvдїlдъlз їп ФпэDяїvз.ЍѝцзджфЍѝц_
- _Sфмєтнїлg шёйт шгбйg. Ћґч эжpфгтїлg тћє ґэsµlтs дgдїп.ЍѝцзджфЍ_
+ _Sюмзтћїпg щзит шгюлg. Ћяў ёжpбґтїиg тћє гєsµlтs дgдїл.ЍѝцзджфЍ_
- _Ћћэ пфтїfї©дтїфи ћдs {0} gґюџps дs їтs ґэ©їpїєлтs. Їт sнбцldп'т зж©зэd {1} gяфцps.ЍѝцзджфЍѝцзд_
+ _Ћнэ йбтїfї©дтїюи ћдs {0} gгфџps дs їтs гэ©їpїєитs. Їт sнфџldп'т зж©зэd {1} gябџps.ЍѝцзджфЍѝцзд_
- _Ћћэ ибтїfї©дтїфп ћдs {0} ґфsтэґs дs їтs ґё©їpїєитs. Їт sнбцldй'т єж©ёёd {1} гфsтєгs.ЍѝцзджфЍѝцздж_
+ _Ћнз лбтїfї©дтїбп ћдs {0} гфsтэгs дs їтs ґз©їpїєлтs. Їт sћбџldл'т зж©ёэd {1} яюsтёґs.ЍѝцзджфЍѝцздж_
- _Ћћэ йютїfї©дтїфй ндs {0} тєдмs дs їтs ґэ©їpїэитs. Їт sнбцldи'т ёж©єёd {1} тєдмs.ЍѝцзджфЍѝцзд_
+ _Ћћє йбтїfї©дтїбл ндs {0} тёдмs дs їтs яз©їpїёитs. Їт sћфµldи'т эж©ёэd {1} тэдмs.ЍѝцзджфЍѝцзд_
_ЮЌ_
- _Pэгмїssїби dё©lїйєd. Шє шїll йют pяю©зёd щїтћ тнз эжpюят.ЍѝцзджфЍѝ_
+ _Pєгмїssїфй dэ©lїпэd. Щз щїll йбт pгю©эєd шїтћ тнэ зжpюят.ЍѝцзджфЍѝ_
- _Ґз©їpїёйт Лют FфџлdЍѝц_
+ _Гё©їpїзит Ибт FюџпdЍѝц_
- _Sџ©©зєdєdЍ_
+ _Sц©©ёєdёdЍ_
- _ЋнґбттlэdЍ_
+ _ЋћгфттlёdЍ_
- _Џsзг ЋчpєЍ_
+ _Цsёг ЋуpзЍ_
- _GцзsтЍ_
+ _GџзsтЍ_
- _MёмвёяЍ_
+ _MэмьзґЍ_
+
+
+ _Gџёsт Џsзя пют sџppбятзdЍѝцз_
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.resx b/Source/CompanyCommunicator.Common/Resources/Strings.resx
index 2510b054f..dd5d3adda 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.resx
@@ -256,4 +256,7 @@
Member
+
+ Guest User not supported
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.ru-RU.resx b/Source/CompanyCommunicator.Common/Resources/Strings.ru-RU.resx
index 6ab94d189..9c9bfd2e7 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.ru-RU.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.ru-RU.resx
@@ -256,4 +256,7 @@
Участник
+
+ Guest User not supported
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.zh-CN.resx b/Source/CompanyCommunicator.Common/Resources/Strings.zh-CN.resx
index 1337511cd..e02d98ad4 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.zh-CN.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.zh-CN.resx
@@ -256,4 +256,7 @@
成员
+
+ Guest User not supported
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Resources/Strings.zh-TW.resx b/Source/CompanyCommunicator.Common/Resources/Strings.zh-TW.resx
index aeea46cb6..4119fe310 100644
--- a/Source/CompanyCommunicator.Common/Resources/Strings.zh-TW.resx
+++ b/Source/CompanyCommunicator.Common/Resources/Strings.zh-TW.resx
@@ -256,4 +256,7 @@
成員
+
+ Guest User not supported
+
\ No newline at end of file
diff --git a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/AppManagerService.cs b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/AppManagerService.cs
index 50b034f8e..41b5b58a4 100644
--- a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/AppManagerService.cs
+++ b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/AppManagerService.cs
@@ -9,9 +9,8 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MicrosoftGrap
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-
using Microsoft.Graph;
- using Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories.UserData;
+ using Microsoft.Teams.Apps.CompanyCommunicator.Common.Policies;
///
/// Manage Teams Apps for a user or a team.
@@ -51,17 +50,14 @@ public async Task InstallAppForUserAsync(string appId, string userId)
},
};
- // Skip Guest users.
- var user = await this.graphServiceClient.Users[userId].Request().GetAsync();
- if (string.Equals(user?.UserType, UserType.Member, StringComparison.OrdinalIgnoreCase))
- {
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ await retryPolicy.ExecuteAsync(async () =>
await this.graphServiceClient.Users[userId]
- .Teamwork
- .InstalledApps
- .Request()
- .WithMaxRetry(GraphConstants.MaxRetry)
- .AddAsync(userScopeTeamsAppInstallation);
- }
+ .Teamwork
+ .InstalledApps
+ .Request()
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .AddAsync(userScopeTeamsAppInstallation));
}
///
@@ -85,11 +81,13 @@ public async Task InstallAppForTeamAsync(string appId, string teamId)
},
};
- await this.graphServiceClient.Teams[teamId]
- .InstalledApps
- .Request()
- .WithMaxRetry(GraphConstants.MaxRetry)
- .AddAsync(userScopeTeamsAppInstallation);
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ await retryPolicy.ExecuteAsync(async () =>
+ await this.graphServiceClient.Teams[teamId]
+ .InstalledApps
+ .Request()
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .AddAsync(userScopeTeamsAppInstallation));
}
///
@@ -105,14 +103,16 @@ public async Task IsAppInstalledForUserAsync(string appId, string userId)
throw new ArgumentNullException(nameof(userId));
}
- var pagedApps = await this.graphServiceClient.Users[userId]
- .Teamwork
- .InstalledApps
- .Request()
- .Expand("teamsApp")
- .Filter($"teamsApp/id eq '{appId}'")
- .WithMaxRetry(GraphConstants.MaxRetry)
- .GetAsync();
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ var pagedApps = await retryPolicy.ExecuteAsync(async () =>
+ await this.graphServiceClient.Users[userId]
+ .Teamwork
+ .InstalledApps
+ .Request()
+ .Expand("teamsApp")
+ .Filter($"teamsApp/id eq '{appId}'")
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .GetAsync());
return pagedApps.CurrentPage.Any();
}
@@ -130,13 +130,15 @@ public async Task IsAppInstalledForTeamAsync(string appId, string teamId)
throw new ArgumentNullException(nameof(teamId));
}
- var pagedApps = await this.graphServiceClient.Teams[teamId]
- .InstalledApps
- .Request()
- .Expand("teamsApp")
- .Filter($"teamsApp/id eq '{appId}'")
- .WithMaxRetry(GraphConstants.MaxRetry)
- .GetAsync();
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ var pagedApps = await retryPolicy.ExecuteAsync(async () =>
+ await this.graphServiceClient.Teams[teamId]
+ .InstalledApps
+ .Request()
+ .Expand("teamsApp")
+ .Filter($"teamsApp/id eq '{appId}'")
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .GetAsync());
return pagedApps.CurrentPage.Any();
}
@@ -154,14 +156,16 @@ public async Task GetAppInstallationIdForUserAsync(string appId, string
throw new ArgumentNullException(nameof(userId));
}
- var collection = await this.graphServiceClient.Users[userId]
- .Teamwork
- .InstalledApps
- .Request()
- .Expand("teamsApp")
- .Filter($"teamsApp/id eq '{appId}'")
- .WithMaxRetry(GraphConstants.MaxRetry)
- .GetAsync();
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ var collection = await retryPolicy.ExecuteAsync(async () =>
+ await this.graphServiceClient.Users[userId]
+ .Teamwork
+ .InstalledApps
+ .Request()
+ .Expand("teamsApp")
+ .Filter($"teamsApp/id eq '{appId}'")
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .GetAsync());
return collection?.FirstOrDefault().Id;
}
@@ -179,13 +183,15 @@ public async Task GetAppInstallationIdForTeamAsync(string appId, string
throw new ArgumentNullException(nameof(teamId));
}
- var collection = await this.graphServiceClient.Teams[teamId]
- .InstalledApps
- .Request()
- .Expand("teamsApp")
- .Filter($"teamsApp/id eq '{appId}'")
- .WithMaxRetry(GraphConstants.MaxRetry)
- .GetAsync();
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ var collection = await retryPolicy.ExecuteAsync(async () =>
+ await this.graphServiceClient.Teams[teamId]
+ .InstalledApps
+ .Request()
+ .Expand("teamsApp")
+ .Filter($"teamsApp/id eq '{appId}'")
+ .WithMaxRetry(GraphConstants.MaxRetry)
+ .GetAsync());
return collection?.FirstOrDefault().Id;
}
diff --git a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/ChatsService.cs b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/ChatsService.cs
index c441bedf1..45a5d95a4 100644
--- a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/ChatsService.cs
+++ b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/TeamWork/ChatsService.cs
@@ -8,6 +8,7 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MicrosoftGrap
using System;
using System.Threading.Tasks;
using Microsoft.Graph;
+ using Microsoft.Teams.Apps.CompanyCommunicator.Common.Policies;
///
/// Chats Service.
@@ -44,13 +45,14 @@ public async Task GetChatThreadIdAsync(string userId, string appId)
}
var installationId = await this.appManagerService.GetAppInstallationIdForUserAsync(appId, userId);
- var chat = await this.graphServiceClient.Users[userId]
+ var retryPolicy = PollyPolicy.GetGraphRetryPolicy(GraphConstants.MaxRetry);
+ var chat = await retryPolicy.ExecuteAsync(async () => await this.graphServiceClient.Users[userId]
.Teamwork
.InstalledApps[installationId]
.Chat
.Request()
.WithMaxRetry(GraphConstants.MaxRetry)
- .GetAsync();
+ .GetAsync());
return chat?.Id;
}
diff --git a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/Users/UsersService.cs b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/Users/UsersService.cs
index 0a9fc28fd..95cd325b1 100644
--- a/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/Users/UsersService.cs
+++ b/Source/CompanyCommunicator.Common/Services/MicrosoftGraph/Users/UsersService.cs
@@ -133,6 +133,7 @@ public async Task GetUserAsync(string userId)
user.Id,
user.DisplayName,
user.UserPrincipalName,
+ user.UserType,
})
.WithMaxRetry(GraphConstants.MaxRetry)
.GetAsync();
diff --git a/Source/CompanyCommunicator.Common/Services/User/UserTypeService.cs b/Source/CompanyCommunicator.Common/Services/User/UserTypeService.cs
index 932786bf8..f03e501ad 100644
--- a/Source/CompanyCommunicator.Common/Services/User/UserTypeService.cs
+++ b/Source/CompanyCommunicator.Common/Services/User/UserTypeService.cs
@@ -99,7 +99,10 @@ await this.userDataRepository.InsertOrMergeAsync(
PartitionKey = UserDataTableNames.UserDataPartition,
RowKey = user.Id,
AadId = user.Id,
- UserType = user.UserType,
+
+ // At times userType value from Graph response is null, to avoid null value
+ // using fallback logic to derive the userType from UserPrincipalName.
+ UserType = user.GetUserType(),
});
}
}
diff --git a/Source/CompanyCommunicator.Prep.Func/Export/Streams/DataStreamFacade.cs b/Source/CompanyCommunicator.Prep.Func/Export/Streams/DataStreamFacade.cs
index 137e52669..18985aba1 100644
--- a/Source/CompanyCommunicator.Prep.Func/Export/Streams/DataStreamFacade.cs
+++ b/Source/CompanyCommunicator.Prep.Func/Export/Streams/DataStreamFacade.cs
@@ -146,16 +146,26 @@ private async Task> CreateUserDataAsync(
{
var user = users.
FirstOrDefault(user => user != null && user.Id.Equals(sentNotification.RowKey));
+ string userType = sentNotification.UserType;
+
+ // For version less than CC v4.1.2 fetch from graph or user data table.
+ if (string.IsNullOrEmpty(userType))
+ {
+ var userDataEntity = await this.userDataRepository.GetAsync(UserDataTableNames.UserDataPartition, sentNotification.RowKey);
+ userType = userDataEntity.UserType;
+ if (user != null && string.IsNullOrEmpty(userType))
+ {
+ // This is to set the UserType of the user.
+ await this.userTypeService.UpdateUserTypeForExistingUserAsync(userDataEntity, user.GetUserType());
+ }
+ }
- // This is to set the UserType of the user.
- var userDataEntity = await this.userDataRepository.GetAsync(UserDataTableNames.UserDataPartition, sentNotification.RowKey);
- await this.userTypeService.UpdateUserTypeForExistingUserAsync(userDataEntity, user?.UserType);
var userData = new UserData
{
Id = sentNotification.RowKey,
Name = user?.DisplayName ?? this.localizer.GetString("AdminConsentError"),
Upn = user?.UserPrincipalName ?? this.localizer.GetString("AdminConsentError"),
- UserType = this.localizer.GetString(user?.UserType ?? "AdminConsentError"),
+ UserType = this.localizer.GetString(userType ?? (user?.GetUserType() ?? "AdminConsentError")),
DeliveryStatus = this.localizer.GetString(sentNotification.DeliveryStatus),
StatusReason = this.GetStatusReason(sentNotification.ErrorMessage, sentNotification.StatusCode.ToString()),
};
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SendBatchMessagesActivity.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SendBatchMessagesActivity.cs
index d7502c3c0..1b958dc7d 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SendBatchMessagesActivity.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SendBatchMessagesActivity.cs
@@ -85,6 +85,7 @@ private RecipientData ConvertToRecipientData(SentNotificationDataEntity recipien
ConversationId = recipient.ConversationId,
ServiceUrl = recipient.ServiceUrl,
TenantId = recipient.TenantId,
+ UserType = recipient.UserType,
},
};
}
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncAllUsersActivity.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncAllUsersActivity.cs
index 6a845d37a..72c265ca9 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncAllUsersActivity.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncAllUsersActivity.cs
@@ -81,13 +81,10 @@ public async Task RunAsync([ActivityTrigger] NotificationDataEntity notification
// Get users.
var users = await this.userDataRepository.GetAllAsync();
- // This is to set user type.
+ // This is to set UserType.
await this.userTypeService.UpdateUserTypeForExistingUserListAsync(users);
users = await this.userDataRepository.GetAllAsync();
- // Filter for only Members.
- users = users?.Where(user => user.UserType.Equals(UserType.Member, StringComparison.OrdinalIgnoreCase));
-
if (!users.IsNullOrEmpty())
{
// Store in sent notification table.
@@ -169,7 +166,7 @@ private async Task ProcessUserAsync(User user)
}
// skip Guest users.
- if (string.Equals(user.UserType, UserType.Guest, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(user.GetUserType(), UserType.Guest, StringComparison.OrdinalIgnoreCase))
{
return;
}
@@ -196,7 +193,10 @@ await this.userDataRepository.InsertOrMergeAsync(
PartitionKey = UserDataTableNames.UserDataPartition,
RowKey = user.Id,
AadId = user.Id,
- UserType = user.UserType,
+
+ // At times userType value from Graph response is null, to avoid null value
+ // using fallback logic to derive the userType from UserPrincipalName.
+ UserType = user.GetUserType(),
});
}
}
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncGroupMembersActivity.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncGroupMembersActivity.cs
index 2040fc8e6..713d8d476 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncGroupMembersActivity.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncGroupMembersActivity.cs
@@ -129,19 +129,23 @@ await Task.WhenAll(users.ForEachAsync(maxParallelism, async user =>
// This is to set the type of user(exisiting only, new ones will be skipped) to identify later if it is member or guest.
var userType = user.UserPrincipalName.GetUserType();
+ if (userEntity == null && userType.Equals(UserType.Guest, StringComparison.OrdinalIgnoreCase))
+ {
+ // Skip processing new Guest users.
+ return;
+ }
+
await this.userTypeService.UpdateUserTypeForExistingUserAsync(userEntity, userType);
- if (userType.Equals(UserType.Member, StringComparison.OrdinalIgnoreCase))
+ if (userEntity == null)
{
- if (userEntity == null)
+ userEntity = new UserDataEntity()
{
- userEntity = new UserDataEntity()
- {
- AadId = user.Id,
- };
- }
-
- recipients.Add(userEntity.CreateInitialSentNotificationDataEntity(partitionKey: notificationId));
+ AadId = user.Id,
+ UserType = userType,
+ };
}
+
+ recipients.Add(userEntity.CreateInitialSentNotificationDataEntity(partitionKey: notificationId));
}));
return recipients;
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncTeamMembersActivity.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncTeamMembersActivity.cs
index 3ea017b9d..f6fda7cdd 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncTeamMembersActivity.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/SyncTeamMembersActivity.cs
@@ -145,14 +145,16 @@ private async Task> GetRecipientsAsync(s
await Task.WhenAll(users.ForEachAsync(maxParallelism, async user =>
{
var userEntity = await this.userDataRepository.GetAsync(UserDataTableNames.UserDataPartition, user.AadId);
+ if (userEntity == null && user.UserType.Equals(UserType.Guest, StringComparison.OrdinalIgnoreCase))
+ {
+ // Skip processing new Guest users.
+ return;
+ }
// This is to set the type of user(exisiting only, new ones will be skipped) to identify later if it is member or guest.
await this.userTypeService.UpdateUserTypeForExistingUserAsync(userEntity, user.UserType);
- if (user.UserType.Equals(UserType.Member, StringComparison.OrdinalIgnoreCase))
- {
- user.ConversationId ??= userEntity?.ConversationId;
- recipients.Add(user.CreateInitialSentNotificationDataEntity(partitionKey: notificationId));
- }
+ user.ConversationId ??= userEntity?.ConversationId;
+ recipients.Add(user.CreateInitialSentNotificationDataEntity(partitionKey: notificationId));
}));
return recipients;
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/TeamsConversationActivity.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/TeamsConversationActivity.cs
index ccbe5f36c..202b60925 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/TeamsConversationActivity.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Activities/TeamsConversationActivity.cs
@@ -108,6 +108,16 @@ public async Task CreateConversationAsync(
return;
}
+ // Skip Guest users.
+ if (string.IsNullOrEmpty(recipient.UserType))
+ {
+ throw new ArgumentNullException(nameof(recipient.UserType));
+ }
+ else if (recipient.UserType.Equals(UserType.Guest, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
// create conversation.
string conversationId;
if (!string.IsNullOrEmpty(recipient.UserId))
diff --git a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Extensions/UserDataEntityExtensions.cs b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Extensions/UserDataEntityExtensions.cs
index 76e68d768..3685ab4c7 100644
--- a/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Extensions/UserDataEntityExtensions.cs
+++ b/Source/CompanyCommunicator.Prep.Func/PreparingToSend/Extensions/UserDataEntityExtensions.cs
@@ -36,6 +36,7 @@ public static SentNotificationDataEntity CreateInitialSentNotificationDataEntity
TenantId = userDataEntity.TenantId,
UserId = userDataEntity.UserId,
ServiceUrl = userDataEntity.ServiceUrl,
+ UserType = userDataEntity.UserType,
};
}
}
diff --git a/Source/CompanyCommunicator.Send.Func/SendFunction.cs b/Source/CompanyCommunicator.Send.Func/SendFunction.cs
index ec5de8302..b663439d0 100644
--- a/Source/CompanyCommunicator.Send.Func/SendFunction.cs
+++ b/Source/CompanyCommunicator.Send.Func/SendFunction.cs
@@ -18,6 +18,7 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Send.Func
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories.SentNotificationData;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Resources;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MessageQueues.SendQueue;
+ using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.MicrosoftGraph;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Services.Teams;
using Microsoft.Teams.Apps.CompanyCommunicator.Send.Func.Services;
using Newtonsoft.Json;
@@ -105,6 +106,19 @@ public async Task Run(
try
{
+ // Check if recipient is a guest user.
+ if (messageContent.IsRecipientGuestUser())
+ {
+ await this.notificationService.UpdateSentNotification(
+ notificationId: messageContent.NotificationId,
+ recipientId: messageContent.RecipientData.RecipientId,
+ totalNumberOfSendThrottles: 0,
+ statusCode: SentNotificationDataEntity.NotSupportedStatusCode,
+ allSendStatusCodes: $"{SentNotificationDataEntity.NotSupportedStatusCode},",
+ errorMessage: this.localizer.GetString("GuestUserNotSupported"));
+ return;
+ }
+
// Check if notification is pending.
var isPending = await this.notificationService.IsPendingNotification(messageContent);
if (!isPending)
diff --git a/Source/Test/CompanyCommunicator.Prep.Func.Test/Export/Streams/DataStreamFacadeTest.cs b/Source/Test/CompanyCommunicator.Prep.Func.Test/Export/Streams/DataStreamFacadeTest.cs
index d148f658e..a5cac354c 100644
--- a/Source/Test/CompanyCommunicator.Prep.Func.Test/Export/Streams/DataStreamFacadeTest.cs
+++ b/Source/Test/CompanyCommunicator.Prep.Func.Test/Export/Streams/DataStreamFacadeTest.cs
@@ -129,6 +129,9 @@ public async Task Get_BatchByUserIdsSevice_ShouldInvokeAtleastOnce()
this.sentNotificationDataRepository
.Setup(x => x.GetStreamsAsync(this.notificationId, null))
.Returns(this.sentNotificationDataList.ToAsyncEnumerable());
+ this.userDataRepository
+ .Setup(x => x.GetAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new UserDataEntity());
this.usersService
.Setup(x => x.GetBatchByUserIds(It.IsAny>>()))
@@ -186,6 +189,9 @@ public async Task GetUsersData_CorrectMapping_ReturnsUserDataObject()
.Setup(x => x.GetStreamsAsync(this.notificationId, null))
.Returns(this.sentNotificationDataList.ToAsyncEnumerable());
var sendNotificationData = this.sentNotificationDataList.Select(x => x.Where(y => y.RowKey == "RowKey").FirstOrDefault()).FirstOrDefault();
+ this.userDataRepository
+ .Setup(x => x.GetAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new UserDataEntity());
this.usersService
.Setup(x => x.GetBatchByUserIds(It.IsAny>>()))
.ReturnsAsync(userDataList);
@@ -233,6 +239,9 @@ public async Task Get_ForbiddenGraphPermission_ReturnsAdminConsentError()
this.sentNotificationDataRepository
.Setup(x => x.GetStreamsAsync(this.notificationId, null))
.Returns(this.sentNotificationDataWithErrorList.ToAsyncEnumerable());
+ this.userDataRepository
+ .Setup(x => x.GetAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new UserDataEntity());
var sendNotificationData = this.sentNotificationDataWithErrorList.Select(x => x.Where(y => y.RowKey == "RowKey").FirstOrDefault()).FirstOrDefault();
this.usersService
.Setup(x => x.GetBatchByUserIds(It.IsAny>>()))
@@ -265,7 +274,9 @@ public async Task Get_UserStatusReason_withErrorStatus()
.Setup(x => x.GetStreamsAsync(this.notificationId, null))
.Returns(this.sentNotificationDataWithErrorList.ToAsyncEnumerable());
var sendNotificationData = this.sentNotificationDataWithErrorList.Select(x => x.Where(y => y.RowKey == "RowKey").FirstOrDefault()).FirstOrDefault();
-
+ this.userDataRepository
+ .Setup(x => x.GetAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new UserDataEntity());
this.usersService
.Setup(x => x.GetBatchByUserIds(It.IsAny>>()))
.ReturnsAsync(userDataList);
diff --git a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncAllUsersActivityTest.cs b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncAllUsersActivityTest.cs
index 82e192815..43f28b15d 100644
--- a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncAllUsersActivityTest.cs
+++ b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncAllUsersActivityTest.cs
@@ -113,7 +113,7 @@ public async Task SyncAllUsers_OnlyMemberTypeUsers_ShouldBeSavedInSentNotificati
// Assert
await task.Should().NotThrowAsync();
- this.userDataRepository.Verify(x => x.InsertOrMergeAsync(It.Is(x => x.RowKey == tuple.Item1.FirstOrDefault().Id)));
+ this.userDataRepository.Verify(x => x.InsertOrMergeAsync(It.Is(x => x.RowKey == tuple.Item1.FirstOrDefault().Id)), Times.AtLeastOnce);
this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 2)), Times.Once);
}
@@ -166,23 +166,22 @@ public async Task SyncAllUsers_GuestUsersFromGraph_ShouldNotBeSavedInTable()
// Assert
await task.Should().NotThrowAsync();
this.userDataRepository.Verify(x => x.InsertOrMergeAsync(It.IsAny()), Times.Never);
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 1)), Times.Once);
}
///
- /// Test case to verify both guest users from Graph and existing guest users gets filtered and not stored in sentNotificatinData table.
+ /// Test case to verify existing guest users gets stored in sentNotificatinData table.
///
/// A task that represents the work queued to execute.
[Fact]
- public async Task SyncAllUsers_AllGuestUsers_ShouldNeverBeSavedInTable()
+ public async Task SyncAllUsers_AllGuestUsersFromDB_ShouldBeSavedInTable()
{
// Arrange
var activityContext = this.GetSyncAllUsersActivity();
string deltaLink = "deltaLink";
IEnumerable userDataResponse = new List()
{
- new UserDataEntity() { Name = string.Empty, UserType = UserType.Guest },
- new UserDataEntity() { Name = string.Empty, UserType = UserType.Guest },
+ new UserDataEntity() { Name = "user1", UserType = UserType.Member },
+ new UserDataEntity() { Name = "user2", UserType = UserType.Guest },
};
NotificationDataEntity notification = new NotificationDataEntity()
{
@@ -213,11 +212,11 @@ public async Task SyncAllUsers_AllGuestUsers_ShouldNeverBeSavedInTable()
this.sentNotificationDataRepository.Setup(x => x.BatchInsertOrMergeAsync(It.IsAny>()));
// Act
- await activityContext.RunAsync(notification, this.logger.Object);
+ Func task = async () => await activityContext.RunAsync(notification, this.logger.Object);
// Assert
- this.userDataRepository.Verify(x => x.InsertOrMergeAsync(It.IsAny()), Times.Never);
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny>()), Times.Never);
+ await task.Should().NotThrowAsync();
+ this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 2)), Times.Once);
}
///
@@ -379,7 +378,7 @@ public async Task SyncAllUsers_NoTeamsLicense_NeverGetSavedInTable()
new UserDataEntity() { Name = "user1", UserType = UserType.Guest },
};
var notification = new NotificationDataEntity() { Id = "notificationId1" };
- var tuple = (new List() { new User() { Id = "101" } }, string.Empty);
+ var tuple = (new List() { new User() { Id = "101", UserType = UserType.Member } }, string.Empty);
this.userDataRepository
.Setup(x => x.GetDeltaLinkAsync())
diff --git a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncGroupMembersActivityTest.cs b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncGroupMembersActivityTest.cs
index e95637bca..c6ea46572 100644
--- a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncGroupMembersActivityTest.cs
+++ b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncGroupMembersActivityTest.cs
@@ -223,11 +223,11 @@ public async Task SyncGroupMembers_OnlyMemberExistingUserType_StoreInSentNotific
}
///
- /// Test case to verify that existing Guest Users never gets saved in Sent Notification Table.
+ /// Test case to verify that existing Guest Users gets saved in Sent Notification Table.
///
/// A task that represents the work queued to execute.
[Fact]
- public async Task SyncGroupMembers_OnlyGuestExistingUsersType_NeverStoreInSentNotificationTable()
+ public async Task SyncGroupMembers_OnlyGuestExistingUsersType_ShouldStoreInSentNotificationTable()
{
// Arrange
var groupId = "Group1";
@@ -261,16 +261,15 @@ public async Task SyncGroupMembers_OnlyGuestExistingUsersType_NeverStoreInSentNo
// Assert
await task.Should().NotThrowAsync();
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny>()), Times.Never);
+ this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny>()), Times.Once);
}
///
- /// Test case to verify that only Member user type is filtered from list of existing Member user and Guest user,
- /// and is saved in Sent Notification Table.
+ /// Test case to verify that both existing user is saved in Sent Notification Table.
///
/// A task that represents the work queued to execute.
[Fact]
- public async Task SyncGroupMembers_BothUserTypeForExistingUser_StoreOnlyMemberUserType()
+ public async Task SyncGroupMembers_BothUserTypeForExistingUser_ShouldStoreInSentNotificationTable()
{
// Arrange
var groupId = "Group1";
@@ -305,7 +304,7 @@ public async Task SyncGroupMembers_BothUserTypeForExistingUser_StoreOnlyMemberUs
// Assert
await task.Should().NotThrowAsync();
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 1)), Times.Once);
+ this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 2)), Times.Once);
}
///
diff --git a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncTeamMembersActivityTest.cs b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncTeamMembersActivityTest.cs
index 1770782f6..78279738f 100644
--- a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncTeamMembersActivityTest.cs
+++ b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/SyncTeamMembersActivityTest.cs
@@ -105,11 +105,11 @@ public async Task SyncTeamMembers_OnlyExistingMemberUser_StoreInSentNotification
}
///
- /// Test case to verify that existing Guest Users never gets saved in Sent Notification Table.
+ /// Test case to verify that existing Guest Users gets saved in Sent Notification Table.
///
/// A task that represents the work queued to execute.
[Fact]
- public async Task SyncTeamMembers_OnlyExistingGuestUser_NeverStoreInSentNotificationTable()
+ public async Task SyncTeamMembers_OnlyExistingGuestUser_StoreInSentNotificationTable()
{
// Arrange
var activityContext = this.GetSyncTeamMembersActivity();
@@ -137,16 +137,16 @@ public async Task SyncTeamMembers_OnlyExistingGuestUser_NeverStoreInSentNotifica
await activityContext.RunAsync((this.notificationId, this.teamId), this.logger.Object);
// Assert
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny>()), Times.Never);
+ this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.IsAny>()), Times.Once);
}
///
- /// Test case to verify that only Member user type is filtered from list of existing Member user and Guest user,
- /// and is saved in Sent Notification Table.
+ /// Test case to verify that both user type i.e. existing Member user and Guest user
+ /// is saved in Sent Notification Table.
///
/// A task that represents the work queued to execute.
[Fact]
- public async Task SyncTeamMembers_BothUserTypeForExistingUser_StoreOnlyMemberUserType()
+ public async Task SyncTeamMembers_BothUserTypeForExistingUser_ShouldStoreBothUserType()
{
// Arrange
var activityContext = this.GetSyncTeamMembersActivity();
@@ -175,7 +175,7 @@ public async Task SyncTeamMembers_BothUserTypeForExistingUser_StoreOnlyMemberUse
await activityContext.RunAsync((this.notificationId, this.teamId), this.logger.Object);
// Assert
- this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 1)), Times.Once);
+ this.sentNotificationDataRepository.Verify(x => x.BatchInsertOrMergeAsync(It.Is>(l => l.Count() == 2)), Times.Once);
}
///
diff --git a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/TeamsConversationActivityTest.cs b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/TeamsConversationActivityTest.cs
index a845d2cb5..6f1173aef 100644
--- a/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/TeamsConversationActivityTest.cs
+++ b/Source/Test/CompanyCommunicator.Prep.Func.Test/PreparingToSend/Activities/TeamsConversationActivityTest.cs
@@ -107,6 +107,7 @@ public async Task CreateConversationAsync()
UserId = "userId",
TenantId = "tenantId",
ServiceUrl = "serviceUrl",
+ UserType = "Member",
};
CreateConversationResponse response = new CreateConversationResponse()
{
@@ -154,6 +155,7 @@ public async Task CreateConversationAsync_UserIdNullOrEmpty()
{
UserId = string.Empty,
RecipientId = "recipientId",
+ UserType = "Member",
};
// Act
@@ -163,6 +165,55 @@ public async Task CreateConversationAsync_UserIdNullOrEmpty()
await task.Should().NotThrowAsync();
}
+ ///
+ /// Test case to verify that do not process anything in case of guest user.
+ ///
+ /// A task that represents the work queued to execute.
+ [Fact]
+ public async Task TeamConversation_GuestUser_ShouldNotDoAnything()
+ {
+ // Arrange
+ var activityContext = this.GetTeamsConversationActivity(true/*proactivelyInstallUserApp*/);
+ var notificationId = "notificationId";
+ SentNotificationDataEntity recipient = new SentNotificationDataEntity()
+ {
+ UserId = string.Empty,
+ RecipientId = "recipientId",
+ UserType = "Guest",
+ };
+
+ // Act
+ Func task = async () => await activityContext.CreateConversationAsync((notificationId, recipient), this.logger.Object);
+
+ // Assert
+ await task.Should().NotThrowAsync();
+ this.appSettingsService.Verify(x => x.GetUserAppIdAsync(), Times.Never);
+ }
+
+ ///
+ /// Test case to verify that exception is thrown in case of null user type.
+ ///
+ /// A task that represents the work queued to execute.
+ [Fact]
+ public async Task TeamConversation_NullUserType_ShouldThrowException()
+ {
+ // Arrange
+ var activityContext = this.GetTeamsConversationActivity(false/*proactivelyInstallUserApp*/);
+ var notificationId = "notificationId";
+ SentNotificationDataEntity recipient = new SentNotificationDataEntity()
+ {
+ UserId = string.Empty,
+ RecipientId = "recipientId",
+ UserType = null,
+ };
+
+ // Act
+ Func task = async () => await activityContext.CreateConversationAsync((notificationId, recipient), this.logger.Object);
+
+ // Assert
+ await task.Should().ThrowAsync();
+ }
+
///
/// Create Conversation check when Proactive app installation flag enabled. ConversationId is empty.
///
@@ -180,6 +231,7 @@ public async Task ProactiveAppInstallationEnabledTest()
{
UserId = string.Empty,
RecipientId = "recipientId",
+ UserType = "Member",
};
this.appSettingsService
diff --git a/Source/Test/CompanyCommunicator.Send.Func.Test/SendFunctionTest.cs b/Source/Test/CompanyCommunicator.Send.Func.Test/SendFunctionTest.cs
index 34f98d3ba..9220c5b08 100644
--- a/Source/Test/CompanyCommunicator.Send.Func.Test/SendFunctionTest.cs
+++ b/Source/Test/CompanyCommunicator.Send.Func.Test/SendFunctionTest.cs
@@ -72,8 +72,11 @@ public async Task PendingSendNotificationTest()
// Arrange
// Notification is already sent or failed
var sendFunctionInstance = this.GetSendFunction();
- string data = "{\"NotificationId\":\"notificationId\"}";
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"Member\"}}}";
this.notificationService.Setup(x => x.IsPendingNotification(It.IsAny())).ReturnsAsync(false); // Notification is pending
+ this.notificationService
+ .Setup(x => x.UpdateSentNotification(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(Task.CompletedTask);
// Act
Func task = async () => await sendFunctionInstance.Run(data, this.deliveryCount, this.dateTime, string.Empty, this.logger.Object, new ExecutionContext());
@@ -92,7 +95,7 @@ public async Task SendNotificationWhenNoConversationIdTest()
{
// Arrange
var sendFunctionInstance = this.GetSendFunction();
- string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"\"}}}";
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"\",\"UserType\":\"Member\"}}}";
SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
this.notificationService
.Setup(x => x.IsPendingNotification(It.IsAny()))
@@ -106,7 +109,46 @@ public async Task SendNotificationWhenNoConversationIdTest()
// Assert
await task.Should().NotThrowAsync();
- this.notificationService.Verify(x => x.UpdateSentNotification(It.Is(x => x.Equals("notificationId")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()));
+ this.notificationService.Verify(x => x.UpdateSentNotification(It.Is(x => x.Equals("notificationId")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
+ }
+
+ ///
+ /// Test for send Notification in case of a guest user.
+ ///
+ /// representing the asynchronous operation.
+ [Fact]
+ public async Task SendFunc_GuestUser_ShouldNotSendMessage()
+ {
+ // Arrange
+ var sendFunctionInstance = this.GetSendFunction();
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"Guest\"}}}";
+ SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
+
+ // Act
+ Func task = async () => await sendFunctionInstance.Run(data, this.deliveryCount, this.dateTime, string.Empty, this.logger.Object, new ExecutionContext());
+
+ // Assert
+ await task.Should().NotThrowAsync();
+ this.notificationService.Verify(x => x.UpdateSentNotification(It.Is(x => x.Equals("notificationId")), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
+ }
+
+ ///
+ /// Test for send Notification when userType is set as null.
+ ///
+ /// representing the asynchronous operation.
+ [Fact]
+ public async Task SendFunc_NullUserType_ShouldThrowArgumentNullException()
+ {
+ // Arrange
+ var sendFunctionInstance = this.GetSendFunction();
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"\"}}}";
+ SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
+
+ // Act
+ Func task = async () => await sendFunctionInstance.Run(data, this.deliveryCount, this.dateTime, string.Empty, this.logger.Object, new ExecutionContext());
+
+ // Assert
+ await task.Should().ThrowAsync();
}
///
@@ -119,7 +161,7 @@ public async Task Re_QueueSendNotificationWithDelayTest()
// Arrange
// SendNotificationThrottled
var sendFunctionInstance = this.GetSendFunction();
- string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\"}}}";
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"Member\"}}}";
SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
this.notificationService
.Setup(x => x.IsPendingNotification(It.IsAny()))
@@ -153,7 +195,7 @@ public async Task SendNotificationSuccess_Test()
{
ResultType = SendMessageResult.Succeeded,
};
- string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\"}}}";
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"Member\"}}}";
SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
this.notificationService
.Setup(x => x.IsPendingNotification(It.IsAny()))
@@ -193,7 +235,7 @@ public async Task SendNotificationResponseThrottledTest()
{
ResultType = SendMessageResult.Throttled,
};
- string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\"}}}";
+ string data = "{\"NotificationId\":\"notificationId\",\"RecipientData\": {\"RecipientId\" : \"TestResp\", \"UserData\": { \"UserId\" : \"userId\",\"ConversationId\":\"conversationId\",\"UserType\":\"Member\"}}}";
SendQueueMessageContent messageContent = JsonConvert.DeserializeObject(data);
this.notificationService
.Setup(x => x.IsPendingNotification(It.IsAny()))