├── .gitignore ├── Deployment └── azuredeploy.json ├── LICENSE ├── Manifest ├── color.png ├── manifest.json └── outline.png ├── README.md ├── Source ├── GlobalSuppressions.cs ├── Microsoft.Teams.Apps.Common │ ├── CommonConfig.cs │ ├── CommonConstant.cs │ ├── Configuration │ │ ├── IConfigProvider.cs │ │ └── LocalConfigProvider.cs │ ├── Exceptions │ │ └── SettingNotFoundException.cs │ ├── Extensions │ │ └── LoggingExtensions.cs │ ├── Logging │ │ ├── AppInsightsLogProvider.cs │ │ ├── ILogProvider.cs │ │ └── LogLevel.cs │ ├── Microsoft.Teams.Apps.Common.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Telemetry │ │ └── UserTelemetryInitializer.cs │ ├── app.config │ └── packages.config ├── Microsoft.Teams.Apps.ListSearch.Common │ ├── Constants.cs │ ├── Filters │ │ └── AiHandleErrorAttribute.cs │ ├── Helpers │ │ ├── BlobHelper.cs │ │ ├── CamelCaseExceptDictionaryKeysResolver.cs │ │ ├── GraphHelper.cs │ │ ├── IQnAMakerService.cs │ │ ├── JwtHelper.cs │ │ ├── KBInfoHelper.cs │ │ ├── KnowledgeBaseRefreshHelper.cs │ │ ├── QnAMakerService.cs │ │ └── TokenHelper.cs │ ├── Microsoft.Teams.Apps.ListSearch.Common.csproj │ ├── Models │ │ ├── Add.cs │ │ ├── AzureADTokenResponse.cs │ │ ├── ColumnInfo.cs │ │ ├── ColumnMapping.cs │ │ ├── CreateKBRequest.cs │ │ ├── Delete.cs │ │ ├── DeserializedAnswer.cs │ │ ├── Details.cs │ │ ├── Error.cs │ │ ├── ErrorResponse.cs │ │ ├── ExtractionOptions.cs │ │ ├── File.cs │ │ ├── GenerateAnswerRequest.cs │ │ ├── GenerateAnswerResponse.cs │ │ ├── GetKnowledgeBaseDetailsResponse.cs │ │ ├── GetListContentsColumnsResponse.cs │ │ ├── GetListContentsResponse.cs │ │ ├── KBInfo.cs │ │ ├── ParentReference.cs │ │ ├── QnAAnswer.cs │ │ ├── QnAMakerEndpointResponse.cs │ │ ├── QnAMakerOperationStates.cs │ │ ├── QnAMakerResponse.cs │ │ ├── RankerTypes.cs │ │ ├── RefreshTokenResponse.cs │ │ ├── SelectedSearchResult.cs │ │ ├── StorageInfo.cs │ │ ├── TokenEntity.cs │ │ ├── TokenTypes.cs │ │ ├── Update.cs │ │ └── UpdateKBRequest.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Settings.StyleCop │ ├── app.config │ └── packages.config ├── Microsoft.Teams.Apps.ListSearch.Configuration │ ├── .gitignore │ ├── App_Start │ │ ├── AutofacConfig.cs │ │ ├── BundleConfig.cs │ │ ├── FilterConfig.cs │ │ ├── RouteConfig.cs │ │ └── Startup.Auth.cs │ ├── ApplicationInsights.config │ ├── Content │ │ ├── Images │ │ │ ├── loader.gif │ │ │ └── userIcon.png │ │ └── Site.css │ ├── Controllers │ │ ├── AccountController.cs │ │ └── HomeController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Microsoft.Teams.Apps.ListSearch.Configuration.csproj │ ├── Models │ │ └── HomeViewModel.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Startup.cs │ ├── Views │ │ ├── Account │ │ │ ├── InvalidUser.cshtml │ │ │ └── SignOutCallback.cshtml │ │ ├── Home │ │ │ ├── ConfigureList.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ └── _Layout.cshtml │ │ ├── Web.config │ │ └── _ViewStart.cshtml │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── libman.json │ └── packages.config ├── Microsoft.Teams.Apps.ListSearch.sln ├── Microsoft.Teams.Apps.ListSearch │ ├── .gitignore │ ├── App_Start │ │ ├── AutofacConfig.cs │ │ ├── BundleConfig.cs │ │ ├── FilterConfig.cs │ │ ├── RouteConfig.cs │ │ └── WebApiConfig.cs │ ├── ApplicationInsights.config │ ├── Content │ │ ├── customTeamsStyle.css │ │ ├── customTheme.css │ │ ├── spinner.css │ │ └── teams-custom.css │ ├── Controllers │ │ ├── ErrorController.cs │ │ ├── MessagesController.cs │ │ ├── RefreshController.cs │ │ └── SearchController.cs │ ├── Filters │ │ └── JwtExceptionFilter.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Microsoft.Teams.Apps.ListSearch.csproj │ ├── Models │ │ ├── AuthenticationFailureResult.cs │ │ ├── ComposeExtensionResultType.cs │ │ ├── ConfigEntity.cs │ │ ├── GetListContentsResponse.cs │ │ ├── JWTExceptions.cs │ │ ├── KBInfo.cs │ │ ├── RefreshTokenResponse.cs │ │ ├── StorageInfo.cs │ │ ├── TaskContinueResult.cs │ │ ├── TaskInfo.cs │ │ ├── TaskSubmitResponse.cs │ │ ├── TokenEntity.cs │ │ └── TokenTypes.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── Strings.Designer.cs │ │ └── Strings.resx │ ├── Settings.StyleCop │ ├── Views │ │ ├── Error │ │ │ ├── ErrorPartial.cshtml │ │ │ └── Index.cshtml │ │ ├── Search │ │ │ ├── Index.cshtml │ │ │ ├── ResultCard.cshtml │ │ │ ├── ResultCardPartial.cshtml │ │ │ ├── SearchList.cshtml │ │ │ └── SearchResults.cshtml │ │ ├── Shared │ │ │ └── _Spinner.cshtml │ │ └── Web.config │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── libman.json │ └── packages.config ├── Resources.txt └── stylecop.json └── global.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /Manifest/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-list-search/830dd2199549947a020a8f7870194d707d8a9022/Manifest/color.png -------------------------------------------------------------------------------- /Manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.4/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.4", 4 | "version": "1.0.0", 5 | "id": "53be03ee-7339-4d0b-b4dc-124336355ea1", 6 | "packageName": "com.microsoft.teams.listsearch", 7 | "developer": { 8 | "name": "", 9 | "websiteUrl": "", 10 | "privacyUrl": "", 11 | "termsOfUseUrl": "" 12 | }, 13 | "icons": { 14 | "color": "color.png", 15 | "outline": "outline.png" 16 | }, 17 | "name": { 18 | "short": "List Search" 19 | }, 20 | "description": { 21 | "short": "Search and share individual records in a SharePoint list", 22 | "full": "Search for an item in a SharePoint list and contextually share it within a conversation" 23 | }, 24 | "accentColor": "#64A2CC", 25 | "composeExtensions": [ 26 | { 27 | "botId": "", 28 | "canUpdateConfiguration": false, 29 | "commands": [ 30 | { 31 | "id": "fetchLists", 32 | "description": "Search lists", 33 | "title": "Search lists", 34 | "type": "action", 35 | "fetchTask": true, 36 | "parameters": [ 37 | { 38 | "name": "param1", 39 | "title": "param1" 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | ], 46 | "permissions": [ 47 | ], 48 | "validDomains": [ 49 | "" 50 | ] 51 | } -------------------------------------------------------------------------------- /Manifest/outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-list-search/830dd2199549947a020a8f7870194d707d8a9022/Manifest/outline.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # List Search App Template 2 | 3 | | [Documentation](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki) | [Deployment guide](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki/Deployment-guide) | [Architecture](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki/Solution-overview) | 4 | | ---- | ---- | ---- | 5 | 6 | Collaboration in Microsoft Teams often references information contained within items in a SharePoint list. Simply pasting a link to the item forces everyone to switch context away from the conversation, find the needed information, then return to Teams to continue the conversation. As the conversation continues, people might have to switch back to the reference item multiple times to verify new comments and refresh their memory of what was in the list item. All this context switching creates a barrier to smooth collaboration, and is an opportunity for things to fall through the cracks. 7 | 8 | To help alleviate this pain, we're happy to bring you the List Search app template. Millions of users use SharePoint to power some of the core workflows in their organizations. Using the List Search app in Microsoft Teams, users can insert information from SharePoint list items directly within a chat conversation. The data is inserted as an easy-to-read card, helping your users stay engaged in the conversation. 9 | 10 | ![List Search in action](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki/images/ListSearch.gif) 11 | 12 | ## Legal notice 13 | 14 | This app template is provided under the [MIT License](https://github.com/OfficeDev/microsoft-teams-list-search-app/blob/master/LICENSE) terms. In addition to these terms, by using this app template you agree to the following: 15 | 16 | - You, not Microsoft, will license the use of your app to users or organization. 17 | 18 | - This app template is not intended to substitute your own regulatory due diligence or make you or your app compliant with respect to any applicable regulations, including but not limited to privacy, healthcare, employment, or financial regulations. 19 | 20 | - You are responsible for complying with all applicable privacy and security regulations including those related to use, collection and handling of any personal data by your app. This includes complying with all internal privacy and security policies of your organization if your app is developed to be sideloaded internally within your organization. Where applicable, you may be responsible for data related incidents or data subject requests for data collected through your app. 21 | 22 | - Any trademarks or registered trademarks of Microsoft in the United States and/or other countries and logos included in this repository are the property of Microsoft, and the license for this project does not grant you rights to use any Microsoft names, logos or trademarks outside of this repository. Microsoft’s general trademark guidelines can be found [here](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general.aspx). 23 | 24 | - If the app template enables access to any Microsoft Internet-based services (e.g., Office365), use of those services will be subject to the separately-provided terms of use. In such cases, Microsoft may collect telemetry data related to app template usage and operation. Use and handling of telemetry data will be performed in accordance with such terms of use. 25 | 26 | - Use of this template does not guarantee acceptance of your app to the Teams app store. To make this app available in the Teams app store, you will have to comply with the [submission and validation process](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/appsource/publish), and all associated requirements such as including your own privacy statement and terms of use for your app. 27 | 28 | ## Get started 29 | 30 | Begin with the [Solution overview](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki/Solution-overview) to read about what the app does and how it works. 31 | 32 | When you're ready to try out List Search, or to use it in your own organization, follow the steps in the [Deployment guide](https://github.com/OfficeDev/microsoft-teams-list-search-app/wiki/Deployment-guide). 33 | 34 | ## Feedback 35 | 36 | Thoughts? Questions? Ideas? Share them with us on [Teams UserVoice](https://microsoftteams.uservoice.com/forums/555103-public)! 37 | 38 | Please report bugs and other code issues [here](https://github.com/OfficeDev/microsoft-teams-list-search-app/issues/new). 39 | 40 | ## Contributing 41 | 42 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 43 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 44 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 45 | 46 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 47 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 48 | provided by the bot. You will only need to do this once across all repos using our CLA. 49 | 50 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 51 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 52 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 53 | -------------------------------------------------------------------------------- /Source/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Convention")] 6 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/CommonConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common 6 | { 7 | /// 8 | /// Configurations that are used across multiple projects. 9 | /// 10 | public enum CommonConfig 11 | { 12 | /// 13 | /// Represents the current deployment environment. 14 | /// 15 | Environment, 16 | 17 | /// 18 | /// Represents an active directory audience. 19 | /// 20 | ActiveDirectoryAudience, 21 | 22 | /// 23 | /// Represents an active directory authority. 24 | /// 25 | ActiveDirectoryAuthority, 26 | 27 | /// 28 | /// Represents an active directory certificate location. 29 | /// 30 | ActiveDirectoryCertificateLocation, 31 | 32 | /// 33 | /// Represents an active directory certificate name. 34 | /// 35 | ActiveDirectoryCertificateName, 36 | 37 | /// 38 | /// Represents an active directory client identifier. 39 | /// 40 | ActiveDirectoryClientId, 41 | 42 | /// 43 | /// Represents an active directory tenant. 44 | /// 45 | ActiveDirectoryTenant, 46 | 47 | /// 48 | /// Represents an application insights instrumentation key. 49 | /// 50 | ApplicationInsightsInstrumentationKey, 51 | 52 | /// 53 | /// Represents an application insights log level. 54 | /// 55 | ApplicationInsightsLogLevel, 56 | 57 | /// 58 | /// Determines whether to use the key vault provider. 59 | /// 60 | UseKeyVault, 61 | 62 | /// 63 | /// Represents a key vault uri. 64 | /// 65 | KeyVaultUri, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/CommonConstant.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common 6 | { 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | /// 11 | /// Store common constants used in project. 12 | /// 13 | [ExcludeFromCodeCoverage] 14 | public static class CommonConstant 15 | { 16 | /// 17 | /// The correlation identifier. 18 | /// 19 | public const string CorrelationId = "CorrelationId"; 20 | 21 | /// 22 | /// Azure storage partition key. 23 | /// 24 | public const string PartitionKey = "PartitionKey"; 25 | 26 | /// 27 | /// Azure storage row key. 28 | /// 29 | public const string RowKey = "RowKey"; 30 | 31 | /// 32 | /// The epoch value. 33 | /// 34 | public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Configuration/IConfigProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Configuration 6 | { 7 | using System; 8 | 9 | /// 10 | /// Interface for implementing get setting values. 11 | /// 12 | public interface IConfigProvider 13 | { 14 | /// 15 | /// Gets the setting value. 16 | /// 17 | /// The type of the configuration keys. 18 | /// The config key. 19 | /// The config value. 20 | string GetSetting(TConfigKeys key) 21 | where TConfigKeys : struct, IConvertible; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Configuration/LocalConfigProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Configuration 6 | { 7 | using System; 8 | using System.Configuration; 9 | using Microsoft.Teams.Apps.Common.Exceptions; 10 | 11 | /// 12 | /// Provides configuration that are stored locally. 13 | /// 14 | /// 15 | [Serializable] 16 | public class LocalConfigProvider : IConfigProvider 17 | { 18 | /// 19 | /// Gets the setting value. 20 | /// 21 | /// The type of the configuration keys. 22 | /// The config key. 23 | /// The config value. 24 | /// 25 | /// Specific key configuration not found. 26 | /// 27 | public string GetSetting(TConfigKeys key) 28 | where TConfigKeys : struct, IConvertible 29 | { 30 | string value = ConfigurationManager.AppSettings[key.ToString()]; 31 | if (value != null) 32 | { 33 | return value; 34 | } 35 | 36 | throw new SettingNotFoundException($"{key} configuration not found"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Exceptions/SettingNotFoundException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Exceptions 6 | { 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.Runtime.Serialization; 10 | 11 | /// 12 | /// Exception for setting not found. 13 | /// 14 | /// 15 | [Serializable] 16 | [ExcludeFromCodeCoverage] 17 | public class SettingNotFoundException : Exception 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public SettingNotFoundException() 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The message that describes the error. 30 | public SettingNotFoundException(string message) 31 | : base(message) 32 | { 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | /// The error message that explains the reason for the exception. 39 | /// 40 | /// The exception that is the cause of the current exception. 41 | /// If no inner exception is specified then returns null reference. 42 | /// 43 | public SettingNotFoundException(string message, Exception innerException) 44 | : base(message, innerException) 45 | { 46 | // Add any type-specific logic for inner exceptions. 47 | } 48 | 49 | /// 50 | /// Initializes a new instance of the class. 51 | /// 52 | /// The SerializationInfo that holds the serialized object data about the exception being thrown. 53 | /// The StreamingContext that contains contextual information about the source or destination. 54 | protected SettingNotFoundException(SerializationInfo info, StreamingContext context) 55 | : base(info, context) 56 | { 57 | // Implement type-specific serialization constructor logic. 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Logging/ILogProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Logging 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | /// 11 | /// Interface to implement different log information. 12 | /// 13 | public interface ILogProvider 14 | { 15 | /// 16 | /// Log a message of the given type. 17 | /// 18 | /// Type of log 19 | /// The debug message 20 | /// An exception to capture and correlate to the debug event 21 | /// A collection of properties that should be associated to the telemetry entry 22 | /// This is used for tracing a series of events. 23 | /// The source 24 | void Log(LogLevel logType, string message, Exception exception, Dictionary properties, Guid correlationId, Func source); 25 | 26 | /// 27 | /// Log a custom event. 28 | /// A custom event log provides insight to system administrators by logging information that may not be captured by other log levels. 29 | /// 30 | /// The information message 31 | /// A collection of properties that should be associated to the telemetry entry 32 | /// The metrics 33 | /// This is used for tracing a series of events. 34 | /// The source 35 | void LogEvent(string eventName, Dictionary properties, Dictionary metrics, Guid correlationId, Func source); 36 | 37 | /// 38 | /// Log a metric. Metrics are aggregated double values that can be processed for the system as a whole 39 | /// 40 | /// Metric Name 41 | /// The value that you are logging 42 | /// [Optional] A collection of properties that should be associated to the metric entry 43 | /// [Optional] The loglevel that this metric should be logged at 44 | void LogMetric(string name, double value, Dictionary properties = null, LogLevel logLevel = LogLevel.Metric); 45 | 46 | /// 47 | /// Log a dependency. 48 | /// 49 | /// External dependency type. Very low cardinality value for logical grouping and interpretation of fields. Examples are SQL, Azure table, and HTTP. 50 | /// Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template. 51 | /// Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with all query parameters. 52 | /// The time when the dependency was called. 53 | /// The time taken by the external dependency to handle the call. 54 | /// True if the dependency call was handled successfully. 55 | void LogDependency(string dependencyTypeName, string dependencyName, string data, DateTimeOffset startTime, TimeSpan duration, bool success); 56 | } 57 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Logging/LogLevel.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Logging 6 | { 7 | /// 8 | /// Types of log level. 9 | /// 10 | public enum LogLevel 11 | { 12 | /// 13 | /// Represents debug. 14 | /// 15 | Debug, 16 | 17 | /// 18 | /// Represents information. 19 | /// 20 | Info, 21 | 22 | /// 23 | /// Represents warning. 24 | /// 25 | Warning, 26 | 27 | /// 28 | /// Represents metric. 29 | /// 30 | Metric, 31 | 32 | /// 33 | /// Represents an event. 34 | /// 35 | Event, 36 | 37 | /// 38 | /// Represents an error. 39 | /// 40 | Error, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | // General Information about an assembly is controlled through the following 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.Common")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.Common")] 16 | [assembly: AssemblyCopyright("Copyright © 2019")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | 20 | // Setting ComVisible to false makes the types in this assembly not visible 21 | // to COM components. If you need to access a type in this assembly from 22 | // COM, set the ComVisible attribute to true on that type. 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | [assembly: Guid("7fa2791f-e894-4876-8773-a5318631365c")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Build and Revision Numbers 36 | // by using the '*' as shown below: 37 | // [assembly: AssemblyVersion("1.0.*")] 38 | [assembly: AssemblyVersion("1.0.0.0")] 39 | [assembly: AssemblyFileVersion("1.0.0.0")] 40 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Telemetry/UserTelemetryInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Telemetry 6 | { 7 | using System; 8 | using System.Web; 9 | using Microsoft.ApplicationInsights.Channel; 10 | using Microsoft.ApplicationInsights.Extensibility; 11 | 12 | /// 13 | /// Initializes user property of telemetry 14 | /// 15 | public class UserTelemetryInitializer : ITelemetryInitializer 16 | { 17 | private const string TelemetryUserId = "TelemetryUserId"; 18 | 19 | /// 20 | /// Set the user id in the platform HTTP context. 21 | /// 22 | /// The HTTP context 23 | /// The user id 24 | public static void SetTelemetryUserId(HttpContext platformContext, string userId) 25 | { 26 | platformContext.Items[TelemetryUserId] = userId; 27 | } 28 | 29 | /// 30 | public void Initialize(ITelemetry telemetry) 31 | { 32 | try 33 | { 34 | var platformContext = HttpContext.Current; 35 | if ((platformContext != null) && 36 | platformContext.Items[TelemetryUserId] is string userId) 37 | { 38 | telemetry.Context.User.Id = userId; 39 | } 40 | } 41 | catch (Exception) 42 | { 43 | // Ignore failure to add user id 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Constants.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common 5 | { 6 | /// 7 | /// Class contains the constants 8 | /// 9 | public class Constants 10 | { 11 | /// 12 | /// Constant QnaMaker subscription key 13 | /// 14 | public const string OcpApimSubscriptionKey = "Ocp-Apim-Subscription-Key"; 15 | 16 | /// 17 | /// AuthenticationType value for SharePoint login 18 | /// 19 | public const string SharePointAppLoginAuthenticationType = "SharePointAppLogin"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Filters/AiHandleErrorAttribute.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Filters 6 | { 7 | using System; 8 | using System.Web.Mvc; 9 | using Microsoft.Teams.Apps.Common.Extensions; 10 | using Microsoft.Teams.Apps.Common.Logging; 11 | 12 | /// 13 | /// Application Insights error filter 14 | /// 15 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 16 | public class AiHandleErrorAttribute : HandleErrorAttribute 17 | { 18 | /// 19 | public override void OnException(ExceptionContext filterContext) 20 | { 21 | if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null) 22 | { 23 | // If customError is Off, then AI HTTPModule will report the exception 24 | if (filterContext.HttpContext.IsCustomErrorEnabled) 25 | { 26 | var logProvider = DependencyResolver.Current.GetService(); 27 | logProvider.LogWarning("Unhandled exception", exception: filterContext.Exception); 28 | } 29 | } 30 | 31 | base.OnException(filterContext); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Helpers/BlobHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Helpers 6 | { 7 | using System; 8 | using System.Threading.Tasks; 9 | using Microsoft.Teams.Apps.ListSearch.Common.Models; 10 | using Microsoft.WindowsAzure.Storage; 11 | using Microsoft.WindowsAzure.Storage.Blob; 12 | 13 | /// 14 | /// Helper for blob. 15 | /// 16 | public class BlobHelper 17 | { 18 | private readonly CloudBlobContainer cloudBlobContainer; 19 | private readonly Lazy initializeTask; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// Connection string of storage. 25 | public BlobHelper(string connectionString) 26 | { 27 | var storageAccount = CloudStorageAccount.Parse(connectionString); 28 | var cloudBlobClient = storageAccount.CreateCloudBlobClient(); 29 | this.cloudBlobContainer = cloudBlobClient.GetContainerReference(StorageInfo.BlobContainerName); 30 | 31 | this.initializeTask = new Lazy(() => this.InitializeAsync()); 32 | } 33 | 34 | /// 35 | /// Upload blob 36 | /// 37 | /// Contents of file to be uploaded. 38 | /// Name of the blob 39 | /// That represents upload operation. 40 | public async Task UploadBlobAsync(string fileContents, string blobName) 41 | { 42 | await this.initializeTask.Value; 43 | 44 | CloudBlockBlob cloudBlockBlob = this.cloudBlobContainer.GetBlockBlobReference(blobName); 45 | await cloudBlockBlob.UploadTextAsync(fileContents); 46 | 47 | return cloudBlockBlob.Uri.ToString(); 48 | } 49 | 50 | /// 51 | /// Delete blob. 52 | /// 53 | /// Name of the blob to be deleted. 54 | /// That represents the delete operation. 55 | public async Task DeleteBlobAsync(string blobName) 56 | { 57 | await this.initializeTask.Value; 58 | 59 | CloudBlockBlob cloudBlockBlob = this.cloudBlobContainer.GetBlockBlobReference(blobName); 60 | await cloudBlockBlob.DeleteIfExistsAsync(); 61 | } 62 | 63 | private async Task InitializeAsync() 64 | { 65 | if (await this.cloudBlobContainer.CreateIfNotExistsAsync()) 66 | { 67 | BlobContainerPermissions permissions = new BlobContainerPermissions 68 | { 69 | PublicAccess = BlobContainerPublicAccessType.Blob, 70 | }; 71 | await this.cloudBlobContainer.SetPermissionsAsync(permissions); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Helpers/CamelCaseExceptDictionaryKeysResolver.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Helpers 6 | { 7 | using System; 8 | using Newtonsoft.Json.Serialization; 9 | 10 | /// 11 | /// Camel Case for Dictionary Resolver 12 | /// 13 | public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver 14 | { 15 | /// 16 | /// Create Dictionary Contract 17 | /// 18 | /// Type of object. 19 | /// Json Dictionary Contract 20 | protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) 21 | { 22 | JsonDictionaryContract contract = base.CreateDictionaryContract(objectType); 23 | 24 | contract.DictionaryKeyResolver = propertyName => propertyName; 25 | 26 | return contract; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Helpers/IQnAMakerService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Helpers 6 | { 7 | using System.Threading.Tasks; 8 | using Microsoft.Teams.Apps.ListSearch.Common.Models; 9 | 10 | /// 11 | /// Interface of QnA Maker Service 12 | /// 13 | public interface IQnAMakerService 14 | { 15 | /// 16 | /// Gets Answer from QnA Maker API. 17 | /// 18 | /// knowledge base id 19 | /// request for GenerateAnswer API. 20 | /// Task that resolves to for the searched question. 21 | Task GenerateAnswerAsync(string kbId, GenerateAnswerRequest request); 22 | 23 | /// 24 | /// Updates KB using QnA Maker API. 25 | /// 26 | /// knowledge base id 27 | /// request to be sent to QnA Maker API. 28 | /// Task that resolves to . 29 | Task UpdateKB(string kbId, UpdateKBRequest body); 30 | 31 | /// 32 | /// Publishes KB. 33 | /// 34 | /// knowledge base id 35 | /// Task that resolves to which represents success or failure of API call. 36 | Task PublishKB(string kbId); 37 | 38 | /// 39 | /// Creates KB using QnA Maker API. 40 | /// 41 | /// request to be sent to QnA Maker API. 42 | /// Task that resolves to . 43 | Task CreateKB(CreateKBRequest body); 44 | 45 | /// 46 | /// Deletes a KB. 47 | /// 48 | /// knowledge base id 49 | /// Task that resolves to which represents success or failure of API call. 50 | Task DeleteKB(string kbId); 51 | 52 | /// 53 | /// Gets operation status of QnA Maker Operation. 54 | /// 55 | /// Id of operation to retrieve status. 56 | /// Task that resolves to . 57 | Task GetOperationDetails(string operationId); 58 | 59 | /// 60 | /// Gets Knowledge base details. 61 | /// 62 | /// knowledge base id 63 | /// Task that resolves to . 64 | Task GetKnowledgeBaseDetails(string kbId); 65 | 66 | /// 67 | /// Wait for the QnAMaker operation to complete. 68 | /// 69 | /// response to be awaited. 70 | /// Operation state after completion. 71 | Task AwaitOperationCompletionState(QnAMakerResponse response); 72 | 73 | /// 74 | /// Wait for the QnAMaker operation to complete. 75 | /// 76 | /// response to be awaited. 77 | /// Completed QnAMaker response. 78 | Task AwaitOperationCompletionResponse(QnAMakerResponse response); 79 | 80 | /// 81 | /// Check if operation is successful. 82 | /// 83 | /// state of operation to be checked. 84 | /// that represents if operation is complete. 85 | bool IsOperationSuccessful(string operationState); 86 | } 87 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Helpers/JwtHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Helpers 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IdentityModel.Tokens.Jwt; 10 | using System.Security.Claims; 11 | using System.Text; 12 | using Microsoft.IdentityModel.Tokens; 13 | 14 | /// 15 | /// Helper class for JWT 16 | /// 17 | public class JwtHelper 18 | { 19 | private const string JwtAuthenticationType = "Custom"; 20 | private const string ClaimTypeSender = "Sender"; 21 | private const string ClaimTypeUserTeamsId = "UserTeamsId"; 22 | private const string ClaimTypeUserAadId = "UserAadId"; 23 | private const string ClaimTypeTenant = "Tenant"; 24 | 25 | private readonly string jwtSecurityKey; 26 | private readonly string appId; 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// security key 32 | /// microsoft app id of bot 33 | public JwtHelper(string jwtSecurityKey, string botId) 34 | { 35 | this.jwtSecurityKey = jwtSecurityKey; 36 | this.appId = botId; 37 | } 38 | 39 | /// 40 | /// Generates JWT 41 | /// 42 | /// Teams id of sender 43 | /// Aad object Id of sender 44 | /// Tenant Id of bot 45 | /// minutes in which jwt expires 46 | /// that represents generated jwt 47 | public string GenerateToken(string userTeamsId, string userAadId, string botTenantId, int jwtExpiryMinutes) 48 | { 49 | SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(this.jwtSecurityKey)); 50 | SigningCredentials signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); 51 | 52 | SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor() 53 | { 54 | Subject = new ClaimsIdentity( 55 | new List() 56 | { 57 | new Claim(ClaimTypeSender, "bot"), 58 | new Claim(ClaimTypeUserTeamsId, userTeamsId), 59 | new Claim(ClaimTypeUserAadId, userAadId), 60 | new Claim(ClaimTypeTenant, botTenantId), 61 | }, JwtAuthenticationType), 62 | NotBefore = DateTime.UtcNow, 63 | SigningCredentials = signingCredentials, 64 | Issuer = this.appId, 65 | IssuedAt = DateTime.UtcNow, 66 | Expires = DateTime.UtcNow.AddMinutes(jwtExpiryMinutes), 67 | }; 68 | 69 | JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); 70 | SecurityToken token = tokenHandler.CreateToken(securityTokenDescriptor); 71 | 72 | return tokenHandler.WriteToken(token); 73 | } 74 | 75 | /// 76 | /// Validate JWT 77 | /// 78 | /// jwt to be validated 79 | /// TenantId of web app 80 | /// representing success/failure of jwt validation 81 | public bool ValidateToken(string jwt, string acceptingTenantId) 82 | { 83 | SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(this.jwtSecurityKey)); 84 | TokenValidationParameters validationParameters = new TokenValidationParameters() 85 | { 86 | ValidateAudience = false, 87 | ValidateIssuer = true, 88 | ValidIssuer = this.appId, 89 | ValidateIssuerSigningKey = true, 90 | IssuerSigningKey = signingKey, 91 | RequireExpirationTime = true, 92 | ValidateLifetime = true, 93 | ClockSkew = TimeSpan.Zero, 94 | }; 95 | 96 | SecurityToken mytoken = new JwtSecurityToken(); 97 | JwtSecurityTokenHandler myTokenHandler = new JwtSecurityTokenHandler(); 98 | ClaimsPrincipal myPrincipal = myTokenHandler.ValidateToken(jwt, validationParameters, out mytoken); 99 | 100 | JwtSecurityToken claimsValidator = (JwtSecurityToken)mytoken; 101 | if (!myPrincipal.HasClaim(ClaimTypeTenant, acceptingTenantId)) 102 | { 103 | throw new SecurityTokenException($"Claim for {ClaimTypeTenant} does not match the expected value."); 104 | } 105 | 106 | return true; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Helpers/KBInfoHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Helpers 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using Microsoft.Teams.Apps.ListSearch.Common.Models; 11 | using Microsoft.WindowsAzure.Storage; 12 | using Microsoft.WindowsAzure.Storage.Table; 13 | 14 | /// 15 | /// Helper class for accessing KB Info 16 | /// 17 | public class KBInfoHelper 18 | { 19 | private const int InsertSuccessResponseCode = 204; 20 | 21 | private readonly CloudTable cloudTable; 22 | private readonly Lazy initializeTask; 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// connection string of storage. 28 | public KBInfoHelper(string connectionString) 29 | { 30 | CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); 31 | CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); 32 | this.cloudTable = tableClient.GetTableReference(StorageInfo.KBInfoTableName); 33 | 34 | this.initializeTask = new Lazy(() => this.InitializeAsync()); 35 | } 36 | 37 | /// 38 | /// Get KB Info item from storage. 39 | /// 40 | /// Kb Id 41 | /// Task that resolves to object for the searched kbId. 42 | public async Task GetKBInfo(string kbId) 43 | { 44 | await this.initializeTask.Value; 45 | 46 | TableOperation searchOperation = TableOperation.Retrieve(StorageInfo.KBInfoTablePartitionKey, kbId); 47 | TableResult searchResult = await this.cloudTable.ExecuteAsync(searchOperation); 48 | 49 | return (KBInfo)searchResult.Result; 50 | } 51 | 52 | /// 53 | /// Returns all specified fields for entries from the table. 54 | /// 55 | /// Fields to be retrieved. 56 | /// that resolves to . 57 | public async Task> GetAllKBs(string[] fields) 58 | { 59 | await this.initializeTask.Value; 60 | 61 | List kbList = new List(); 62 | TableQuery projectionQuery = new TableQuery().Select(fields); 63 | TableContinuationToken token = null; 64 | 65 | do 66 | { 67 | TableQuerySegment seg = await this.cloudTable.ExecuteQuerySegmentedAsync(projectionQuery, token); 68 | token = seg.ContinuationToken; 69 | kbList.AddRange(seg.Results); 70 | } 71 | while (token != null); 72 | 73 | return kbList; 74 | } 75 | 76 | /// 77 | /// Insert or merge KBInfo entity. 78 | /// 79 | /// Kb Info entity. 80 | /// that represents Insert or Merge function. 81 | public async Task InsertOrMergeKBInfo(KBInfo kBInfo) 82 | { 83 | await this.initializeTask.Value; 84 | 85 | TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(kBInfo); 86 | TableResult insertOrMergeResult = await this.cloudTable.ExecuteAsync(insertOrMergeOperation); 87 | if (insertOrMergeResult.HttpStatusCode != InsertSuccessResponseCode) 88 | { 89 | throw new Exception($"HTTP Error code - {insertOrMergeResult.HttpStatusCode}"); 90 | } 91 | } 92 | 93 | /// 94 | /// Deletes KB from KBInfo Storage table 95 | /// 96 | /// Kb id 97 | /// representing the asynchronous operation 98 | public async Task DeleteKB(string kbId) 99 | { 100 | await this.initializeTask.Value; 101 | 102 | var entity = new DynamicTableEntity(StorageInfo.KBInfoTablePartitionKey, kbId); 103 | entity.ETag = "*"; 104 | 105 | await this.cloudTable.ExecuteAsync(TableOperation.Delete(entity)); 106 | } 107 | 108 | private async Task InitializeAsync() 109 | { 110 | await this.cloudTable.CreateIfNotExistsAsync(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/Add.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Add data 12 | /// 13 | public class Add 14 | { 15 | /// 16 | /// Gets or sets the list of files 17 | /// 18 | [JsonProperty("files")] 19 | public List Files { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/AzureADTokenResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Refresh Token Response. 11 | /// 12 | public class AzureADTokenResponse 13 | { 14 | /// 15 | /// Gets or sets Token Type. 16 | /// 17 | [JsonProperty("token_type")] 18 | public string TokenType { get; set; } 19 | 20 | /// 21 | /// Gets or sets scope. 22 | /// 23 | [JsonProperty("scope")] 24 | public string Scope { get; set; } 25 | 26 | /// 27 | /// Gets or sets Expires In. 28 | /// 29 | [JsonProperty("expires_in")] 30 | public double ExpiresIn { get; set; } 31 | 32 | /// 33 | /// Gets or sets Ext Expires In. 34 | /// 35 | [JsonProperty("ext_expires_in")] 36 | public string ExtExpiresIn { get; set; } 37 | 38 | /// 39 | /// Gets or sets Access Token. 40 | /// 41 | [JsonProperty("access_token")] 42 | public string AccessToken { get; set; } 43 | 44 | /// 45 | /// Gets or sets Refresh Token. 46 | /// 47 | [JsonProperty("refresh_token")] 48 | public string RefreshToken { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/ColumnInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Column Info for SharePoint response 11 | /// 12 | public class ColumnInfo 13 | { 14 | /// 15 | /// Gets or sets display name 16 | /// 17 | [JsonProperty("displayName")] 18 | public string DisplayName { get; set; } 19 | 20 | /// 21 | /// Gets or sets name 22 | /// 23 | [JsonProperty("name")] 24 | public string Name { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/ColumnMapping.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Column mapping 12 | /// 13 | public class ColumnMapping 14 | { 15 | /// 16 | /// Gets or sets question field 17 | /// 18 | [JsonProperty("question")] 19 | public string Question { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/CreateKBRequest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Create KB Request 12 | /// 13 | public class CreateKBRequest 14 | { 15 | /// 16 | /// Gets or sets name 17 | /// 18 | [JsonProperty("name")] 19 | public string Name { get; set; } 20 | 21 | /// 22 | /// Gets or sets files 23 | /// 24 | [JsonProperty("files")] 25 | public List Files { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/Delete.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Delete data 12 | /// 13 | public class Delete 14 | { 15 | /// 16 | /// Gets or sets sources 17 | /// 18 | [JsonProperty("sources")] 19 | public List Sources { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/DeserializedAnswer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | /// 8 | /// Deserialize Answer 9 | /// 10 | public class DeserializedAnswer 11 | { 12 | /// 13 | /// Gets or sets question 14 | /// 15 | public string Question { get; set; } 16 | 17 | /// 18 | /// Gets or sets answer 19 | /// 20 | public string Answer { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/Details.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Details 11 | /// 12 | public class Details 13 | { 14 | /// 15 | /// Gets or sets code 16 | /// 17 | [JsonProperty("code")] 18 | public string Code { get; set; } 19 | 20 | /// 21 | /// Gets or sets message 22 | /// 23 | [JsonProperty("message")] 24 | public string Message { get; set; } 25 | 26 | /// 27 | /// Gets or sets target 28 | /// 29 | [JsonProperty("target")] 30 | public string Target { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/Error.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Error 12 | /// 13 | public class Error 14 | { 15 | /// 16 | /// Gets or sets code 17 | /// 18 | [JsonProperty("code")] 19 | public string Code { get; set; } 20 | 21 | /// 22 | /// Gets or sets code 23 | /// 24 | [JsonProperty("message")] 25 | public string Message { get; set; } 26 | 27 | /// 28 | /// Gets or sets details 29 | /// 30 | [JsonProperty("details")] 31 | public List
Details { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/ErrorResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Error Response 11 | /// 12 | public class ErrorResponse 13 | { 14 | /// 15 | /// Gets or sets error 16 | /// 17 | [JsonProperty("error")] 18 | public Error Error { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/ExtractionOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Extraction Option 11 | /// 12 | public class ExtractionOptions 13 | { 14 | /// 15 | /// Fixed string for format 16 | /// 17 | [JsonProperty("format")] 18 | public const string Format = "SharepointListJson"; 19 | 20 | /// 21 | /// Gets or sets column mapping 22 | /// 23 | [JsonProperty("columnMapping")] 24 | public ColumnMapping ColumnMapping { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/File.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// File 12 | /// 13 | public class File 14 | { 15 | /// 16 | /// Gets or sets file name 17 | /// 18 | [JsonProperty("fileName")] 19 | public string FileName { get; set; } 20 | 21 | /// 22 | /// Gets or sets file uri 23 | /// 24 | [JsonProperty("fileUri")] 25 | public string FileUri { get; set; } 26 | 27 | /// 28 | /// Gets or sets list of extraction options 29 | /// 30 | [JsonProperty("extractionOptions")] 31 | public ExtractionOptions ExtractionOptions { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/GenerateAnswerRequest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 5 | { 6 | using Newtonsoft.Json; 7 | 8 | /// 9 | /// Structure of Request for Generate Answer API. 10 | /// 11 | public class GenerateAnswerRequest 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// Question asked by the user 17 | /// Number of results to be fetched 18 | /// Score to filter the search result 19 | public GenerateAnswerRequest(string question = null, int? top = null, int? score = null) 20 | { 21 | this.RankerType = "AutoSuggestQuestion"; 22 | this.Question = question; 23 | this.Top = top; 24 | this.ScoreThreshold = score; 25 | } 26 | 27 | /// 28 | /// Gets or sets question asked by the user 29 | /// 30 | [JsonProperty("question")] 31 | public string Question { get; set; } 32 | 33 | /// 34 | /// Gets or sets number of results to be fetched 35 | /// 36 | [JsonProperty("top")] 37 | public int? Top { get; set; } 38 | 39 | /// 40 | /// Gets or sets ScoreTherhold 41 | /// 42 | [JsonProperty("scoreThreshold")] 43 | public int? ScoreThreshold { get; set; } 44 | 45 | /// 46 | /// Gets or sets Ranker type 47 | /// 48 | [JsonProperty("RankerType")] 49 | public string RankerType { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/GenerateAnswerResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | 11 | /// 12 | /// Structure of Response from Generate Answer API. 13 | /// 14 | public class GenerateAnswerResponse 15 | { 16 | /// 17 | /// Gets or sets list of answers. 18 | /// 19 | [JsonProperty("answers")] 20 | public List Answers { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/GetKnowledgeBaseDetailsResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Get Knowledge Base Details Response 12 | /// 13 | public class GetKnowledgeBaseDetailsResponse 14 | { 15 | /// 16 | /// Gets or sets id 17 | /// 18 | [JsonProperty("id")] 19 | public string Id { get; set; } 20 | 21 | /// 22 | /// Gets or sets hostname 23 | /// 24 | [JsonProperty("hostName")] 25 | public string HostName { get; set; } 26 | 27 | /// 28 | /// Gets or sets last accessed time stamp 29 | /// 30 | [JsonProperty("lastAccessedTimestamp")] 31 | public string LastAccessedTimestamp { get; set; } 32 | 33 | /// 34 | /// Gets or sets last changed time stamp 35 | /// 36 | [JsonProperty("lastChangedTimestamp")] 37 | public string LastChangedTimestamp { get; set; } 38 | 39 | /// 40 | /// Gets or sets name 41 | /// 42 | [JsonProperty("name")] 43 | public string Name { get; set; } 44 | 45 | /// 46 | /// Gets or sets user id 47 | /// 48 | [JsonProperty("userId")] 49 | public string UserId { get; set; } 50 | 51 | /// 52 | /// Gets or sets urls 53 | /// 54 | [JsonProperty("urls")] 55 | public List Urls { get; set; } 56 | 57 | /// 58 | /// Gets or sets sources 59 | /// 60 | [JsonProperty("sources")] 61 | public List Sources { get; set; } 62 | 63 | /// 64 | /// Gets or sets language 65 | /// 66 | [JsonProperty("language")] 67 | public string Language { get; set; } 68 | 69 | /// 70 | /// Gets or sets created time stamp 71 | /// 72 | [JsonProperty("createdTimestamp")] 73 | public string CreatedTimestamp { get; set; } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/GetListContentsColumnsResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 5 | { 6 | using Newtonsoft.Json; 7 | 8 | /// 9 | /// Get List Contents Columns Response 10 | /// 11 | public class GetListContentsColumnsResponse 12 | { 13 | /// 14 | /// Gets or sets odata context 15 | /// 16 | [JsonProperty("@odata.context")] 17 | public string ODataContext { get; set; } 18 | 19 | /// 20 | /// Gets or sets listId 21 | /// 22 | [JsonProperty("id")] 23 | public string ListId { get; set; } 24 | 25 | /// 26 | /// Gets or sets SiteId 27 | /// 28 | [JsonProperty("parentReference")] 29 | public ParentReference ParentReference { get; set; } 30 | 31 | /// 32 | /// Gets or sets List display name 33 | /// 34 | [JsonProperty("displayName")] 35 | public string ListDisplayName { get; set; } 36 | 37 | /// 38 | /// Gets or sets Columns array 39 | /// 40 | [JsonProperty("columns")] 41 | public ColumnInfo[] Columns { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/GetListContentsResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Get List Contents Response 11 | /// 12 | public class GetListContentsResponse 13 | { 14 | /// 15 | /// Gets or sets odata context 16 | /// 17 | [JsonProperty("@odata.context")] 18 | public string ODataContext { get; set; } 19 | 20 | /// 21 | /// Gets or sets odata next link 22 | /// 23 | [JsonProperty("@odata.nextLink")] 24 | public string ODataNextLink { get; set; } 25 | 26 | /// 27 | /// Gets or sets value 28 | /// 29 | [JsonProperty("value")] 30 | public object Value { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/KBInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System; 8 | using Microsoft.WindowsAzure.Storage.Table; 9 | using Newtonsoft.Json; 10 | 11 | /// 12 | /// Represents KB Info in storage. 13 | /// 14 | public class KBInfo : TableEntity 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public KBInfo() 20 | { 21 | } 22 | 23 | /// 24 | /// Gets the kb id 25 | /// 26 | public string KBId 27 | { 28 | get 29 | { 30 | return this.RowKey; 31 | } 32 | } 33 | 34 | /// 35 | /// Gets or sets name of the kb 36 | /// 37 | [JsonProperty("KBName")] 38 | public string KBName { get; set; } 39 | 40 | /// 41 | /// Gets or sets SharePoint url of the list 42 | /// 43 | [JsonProperty("SharePointUrl")] 44 | public string SharePointUrl { get; set; } 45 | 46 | /// 47 | /// Gets or sets SharePoint site id. 48 | /// 49 | [JsonProperty("SharePointSiteId")] 50 | public string SharePointSiteId { get; set; } 51 | 52 | /// 53 | /// Gets or sets question field 54 | /// 55 | [JsonProperty("QuestionField")] 56 | public string QuestionField { get; set; } 57 | 58 | /// 59 | /// Gets encoded question field 60 | /// 61 | public string QuestionFieldDisplayName 62 | { 63 | get 64 | { 65 | return (this.QuestionField != null) ? 66 | JsonConvert.DeserializeObject(this.QuestionField)?.DisplayName : string.Empty; 67 | } 68 | } 69 | 70 | /// 71 | /// Gets or sets answer field 72 | /// 73 | [JsonProperty("AnswerFields")] 74 | public string AnswerFields { get; set; } 75 | 76 | /// 77 | /// Gets or sets last refresh date time 78 | /// 79 | [JsonProperty("LastRefreshDateTime")] 80 | public DateTime LastRefreshDateTime { get; set; } 81 | 82 | /// 83 | /// Gets or sets refresh frequency in hours 84 | /// 85 | [JsonProperty("RefreshFrequencyInHours")] 86 | public int RefreshFrequencyInHours { get; set; } 87 | 88 | /// 89 | /// Gets or sets SharePoint list id 90 | /// 91 | [JsonProperty("SharePointListId")] 92 | public string SharePointListId { get; set; } 93 | 94 | /// 95 | /// Gets or sets last refresh attempt timestamp 96 | /// 97 | [JsonProperty("LastRefreshAttemptDateTime")] 98 | public DateTime LastRefreshAttemptDateTime { get; set; } 99 | 100 | /// 101 | /// Gets or sets last refresh attempt error (will be null/empty if successful) 102 | /// 103 | [JsonProperty("LastRefreshAttemptError")] 104 | public string LastRefreshAttemptError { get; set; } 105 | 106 | /// 107 | /// Gets or sets the ranker type to use when querying this knowledge base 108 | /// 109 | [JsonProperty("RankerType")] 110 | public string RankerType { get; set; } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/ParentReference.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 5 | { 6 | using Newtonsoft.Json; 7 | 8 | /// 9 | /// List Parent Reference 10 | /// 11 | public class ParentReference 12 | { 13 | /// 14 | /// Gets or sets SiteId 15 | /// 16 | [JsonProperty("siteId")] 17 | public string SiteId { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/QnAAnswer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | 11 | /// 12 | /// Structure of QnA Answer. 13 | /// 14 | public class QnAAnswer 15 | { 16 | /// 17 | /// Gets or sets Questions 18 | /// 19 | [JsonProperty("questions")] 20 | public List Questions { get; set; } 21 | 22 | /// 23 | /// Gets or sets Answer 24 | /// 25 | [JsonProperty("answer")] 26 | public string Answer { get; set; } 27 | 28 | /// 29 | /// Gets or sets Score 30 | /// 31 | [JsonProperty("score")] 32 | public double Score { get; set; } 33 | 34 | /// 35 | /// Gets or sets Id 36 | /// 37 | [JsonProperty("id")] 38 | public string Id { get; set; } 39 | 40 | /// 41 | /// Gets or sets Source 42 | /// 43 | [JsonProperty("source")] 44 | public string Source { get; set; } 45 | 46 | /// 47 | /// Gets or sets Top 48 | /// 49 | [JsonProperty("top")] 50 | public string Top { get; set; } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/QnAMakerEndpointResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 5 | { 6 | using Newtonsoft.Json; 7 | 8 | /// 9 | /// QNAMaker EndPoint Keys Response 10 | /// 11 | public class QnAMakerEndpointResponse 12 | { 13 | /// 14 | /// Gets or sets primaryEndpointKey 15 | /// 16 | [JsonProperty("primaryEndpointKey")] 17 | public string PrimaryEndpointKey { get; set; } 18 | 19 | /// 20 | /// Gets or sets secondaryEndpointKey 21 | /// 22 | [JsonProperty("secondaryEndpointKey")] 23 | public string SecondaryEndpointKey { get; set; } 24 | 25 | /// 26 | /// Gets or sets installedVersion 27 | /// 28 | [JsonProperty("installedVersion")] 29 | public string InstalledVersion { get; set; } 30 | 31 | /// 32 | /// Gets or sets lastStableVersion 33 | /// 34 | [JsonProperty("lastStableVersion")] 35 | public string LastStableVersion { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/QnAMakerOperationStates.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | /// 8 | /// Possible operation states of QnA Maker Response 9 | /// 10 | public class QnAMakerOperationStates 11 | { 12 | /// 13 | /// Not Started 14 | /// 15 | public const string NotStarted = "NotStarted"; 16 | 17 | /// 18 | /// Running 19 | /// 20 | public const string Running = "Running"; 21 | 22 | /// 23 | /// Succeeded 24 | /// 25 | public const string Succeeded = "Succeeded"; 26 | 27 | /// 28 | /// Failed 29 | /// 30 | public const string Failed = "Failed"; 31 | 32 | /// 33 | /// Bad Argument 34 | /// 35 | public const string BadArgument = "BadArgument"; 36 | 37 | /// 38 | /// Unauthorized 39 | /// 40 | public const string Unauthorized = "Unauthorized"; 41 | 42 | /// 43 | /// Forbidden 44 | /// 45 | public const string Forbidden = "Forbidden"; 46 | 47 | /// 48 | /// NotFound 49 | /// 50 | public const string NotFound = "NotFound"; 51 | 52 | /// 53 | /// Unspecified 54 | /// 55 | public const string Unspecified = "Unspecified"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/QnAMakerResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Get operation details response 11 | /// 12 | public class QnAMakerResponse 13 | { 14 | /// 15 | /// Gets or sets operation state 16 | /// 17 | [JsonProperty("operationState")] 18 | public string OperationState { get; set; } 19 | 20 | /// 21 | /// Gets or sets created time stamp 22 | /// 23 | [JsonProperty("createdTimestamp")] 24 | public string CreatedTimestamp { get; set; } 25 | 26 | /// 27 | /// Gets or sets last action timestamp 28 | /// 29 | [JsonProperty("lastActionTimestamp")] 30 | public string LastActionTimestamp { get; set; } 31 | 32 | /// 33 | /// Gets or sets resource location 34 | /// 35 | [JsonProperty("resourceLocation")] 36 | public string ResourceLocation { get; set; } 37 | 38 | /// 39 | /// Gets or sets user id 40 | /// 41 | [JsonProperty("userId")] 42 | public string UserId { get; set; } 43 | 44 | /// 45 | /// Gets or sets operation id 46 | /// 47 | [JsonProperty("operationId")] 48 | public string OperationId { get; set; } 49 | 50 | /// 51 | /// Gets or sets error response 52 | /// 53 | [JsonProperty("errorResponse")] 54 | public ErrorResponse ErrorResponse { get; set; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/RankerTypes.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | /// 8 | /// Represents the ranker type to use when querying a knowledge base 9 | /// 10 | public class RankerTypes 11 | { 12 | /// 13 | /// Ranks results based on the question alone. 14 | /// 15 | public const string QuestionOnly = "QuestionOnly"; 16 | 17 | /// 18 | /// Ranks results based on the question, in a matter suitable for suggestions. 19 | /// 20 | public const string AutoSuggestQuestion = "AutoSuggestQuestion"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/RefreshTokenResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Refresh Token Response. 11 | /// 12 | public class RefreshTokenResponse 13 | { 14 | /// 15 | /// Gets or sets Token Type. 16 | /// 17 | [JsonProperty("token_type")] 18 | public string TokenType { get; set; } 19 | 20 | /// 21 | /// Gets or sets scope. 22 | /// 23 | [JsonProperty("scope")] 24 | public string Scope { get; set; } 25 | 26 | /// 27 | /// Gets or sets Expires In. 28 | /// 29 | [JsonProperty("expires_in")] 30 | public double ExpiresIn { get; set; } 31 | 32 | /// 33 | /// Gets or sets Ext Expires In. 34 | /// 35 | [JsonProperty("ext_expires_in")] 36 | public string ExtExpiresIn { get; set; } 37 | 38 | /// 39 | /// Gets or sets Access Token. 40 | /// 41 | [JsonProperty("access_token")] 42 | public string AccessToken { get; set; } 43 | 44 | /// 45 | /// Gets or sets Refresh Token. 46 | /// 47 | [JsonProperty("refresh_token")] 48 | public string RefreshToken { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/SelectedSearchResult.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 5 | { 6 | using System.Collections.Generic; 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Structure of response from Task Module for generating the adaptive card. 11 | /// 12 | public class SelectedSearchResult 13 | { 14 | /// 15 | /// Gets or sets Kb id 16 | /// 17 | [JsonProperty("kbId")] 18 | public string KBId { get; set; } 19 | 20 | /// 21 | /// Gets or sets answers 22 | /// 23 | [JsonProperty("answers")] 24 | public List Answers { get; set; } 25 | 26 | /// 27 | /// Gets or sets question 28 | /// 29 | [JsonProperty("question")] 30 | public string Question { get; set; } 31 | 32 | /// 33 | /// Gets or sets the URL of the SharePoint list 34 | /// 35 | [JsonProperty("sharePointUrl")] 36 | public string SharePointListUrl { get; set; } 37 | 38 | /// 39 | /// Gets or sets the id of the list item 40 | /// 41 | [JsonProperty("listItemId")] 42 | public string ListItemId { get; set; } 43 | 44 | /// 45 | /// Gets or sets the search session id 46 | /// 47 | [JsonProperty("sessionId")] 48 | public string SessionId { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/StorageInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | /// 8 | /// References to storage tables. 9 | /// 10 | public class StorageInfo 11 | { 12 | /// 13 | /// Config Table 14 | /// 15 | public const string ConfigTableName = "Config"; 16 | 17 | /// 18 | /// KB Info Table 19 | /// 20 | public const string KBInfoTableName = "KBInfo"; 21 | 22 | /// 23 | /// KB Info Table Partition key 24 | /// 25 | public const string KBInfoTablePartitionKey = "KbInfo"; 26 | 27 | /// 28 | /// Token Table 29 | /// 30 | public const string TokenTableName = "Token"; 31 | 32 | /// 33 | /// Blob Container 34 | /// 35 | public const string BlobContainerName = "list-search-blob"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/TokenEntity.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System; 8 | using Microsoft.WindowsAzure.Storage.Table; 9 | using Newtonsoft.Json; 10 | 11 | /// 12 | /// Represents Token entity in storage. 13 | /// 14 | public class TokenEntity : TableEntity 15 | { 16 | /// 17 | /// Gets the token type 18 | /// 19 | public string TokenType 20 | { 21 | get { return this.RowKey; } 22 | } 23 | 24 | /// 25 | /// Gets or sets email 26 | /// 27 | [JsonProperty("UserPrincipalName")] 28 | public string UserPrincipalName { get; set; } 29 | 30 | /// 31 | /// Gets or sets access token 32 | /// 33 | [JsonProperty("AccessToken")] 34 | public string AccessToken { get; set; } 35 | 36 | /// 37 | /// Gets or sets refresh token 38 | /// 39 | [JsonProperty("RefreshToken")] 40 | public string RefreshToken { get; set; } 41 | 42 | /// 43 | /// Gets or sets the time when the access token expires 44 | /// 45 | [JsonProperty("ExpiresIn")] 46 | public DateTime ExpiryDateTime { get; set; } 47 | 48 | /// 49 | /// Gets or sets token scopes 50 | /// 51 | [JsonProperty("Scopes")] 52 | public string Scopes { get; set; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/TokenTypes.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | /// 8 | /// Token Types 9 | /// 10 | public class TokenTypes 11 | { 12 | /// 13 | /// Token of type Graph. 14 | /// 15 | public const string GraphTokenType = "Graph"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/Update.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Update data 12 | /// 13 | public class Update 14 | { 15 | /// 16 | /// Gets or sets the list of files 17 | /// 18 | [JsonProperty("files")] 19 | public List Files { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Models/UpdateKBRequest.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Common.Models 6 | { 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Update KB request 12 | /// 13 | public class UpdateKBRequest 14 | { 15 | /// 16 | /// Gets or sets the add operation 17 | /// 18 | [JsonProperty("add")] 19 | public Add Add { get; set; } 20 | 21 | /// 22 | /// Gets or sets the delete operation 23 | /// 24 | [JsonProperty("delete")] 25 | public Delete Delete { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.ListSearch.Common")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.ListSearch")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("20fa7b4d-0c91-421f-9675-5a43de20ba5a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | preprocessor,pre-processor 5 | shortlived,short-lived 6 | 7 | 8 | 9 | 10 | 11 | 12 | \.g\.cs$ 13 | \.generated\.cs$ 14 | \.g\.i\.cs$ 15 | TemporaryGeneratedFile_.*\.cs$ 16 | AssemblyInfo.*\.cs$ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | False 27 | 28 | 29 | 30 | 31 | 32 | as 33 | do 34 | id 35 | if 36 | in 37 | ip 38 | is 39 | mx 40 | my 41 | no 42 | on 43 | to 44 | ui 45 | vs 46 | x 47 | y 48 | z 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | False 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/.gitignore: -------------------------------------------------------------------------------- 1 | # Paths with files restored by libman 2 | lib/bootstrap/ 3 | lib/jquery/ 4 | lib/jquery-validate/ 5 | lib/jquery-validation-unobtrusive/ 6 | lib/modernizr/ 7 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/App_Start/AutofacConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 6 | { 7 | using System.Configuration; 8 | using System.Net.Http; 9 | using System.Reflection; 10 | using System.Web.Mvc; 11 | using Autofac; 12 | using Autofac.Integration.Mvc; 13 | using Microsoft.Teams.Apps.Common.Configuration; 14 | using Microsoft.Teams.Apps.Common.Logging; 15 | using Microsoft.Teams.Apps.ListSearch.Common.Helpers; 16 | using Microsoft.Teams.Apps.ListSearch.Configuration.Controllers; 17 | 18 | /// 19 | /// Autofac configuration 20 | /// 21 | public class AutofacConfig 22 | { 23 | /// 24 | /// Register Autofac dependencies 25 | /// 26 | /// Autofac container 27 | public static IContainer RegisterDependencies() 28 | { 29 | var builder = new ContainerBuilder(); 30 | builder.RegisterControllers(Assembly.GetExecutingAssembly()); 31 | 32 | var config = new LocalConfigProvider(); 33 | 34 | builder.Register(c => config) 35 | .As() 36 | .SingleInstance(); 37 | 38 | builder.Register(c => new AppInsightsLogProvider(c.Resolve())) 39 | .As() 40 | .SingleInstance(); 41 | 42 | builder.Register(c => new HttpClient()) 43 | .SingleInstance(); 44 | 45 | builder.Register(c => new KBInfoHelper(ConfigurationManager.AppSettings["StorageConnectionString"])) 46 | .SingleInstance(); 47 | 48 | builder.Register(c => new TokenHelper( 49 | c.Resolve(), 50 | ConfigurationManager.AppSettings["StorageConnectionString"], 51 | ConfigurationManager.AppSettings["ida:TenantId"], 52 | ConfigurationManager.AppSettings["GraphAppClientId"], 53 | ConfigurationManager.AppSettings["GraphAppClientSecret"], 54 | ConfigurationManager.AppSettings["TokenEncryptionKey"])) 55 | .SingleInstance(); 56 | 57 | builder.Register(c => new GraphHelper( 58 | c.Resolve(), 59 | c.Resolve())) 60 | .SingleInstance(); 61 | 62 | builder.Register(c => new QnAMakerService( 63 | c.Resolve(), 64 | ConfigurationManager.AppSettings["QnaMakerApiEndpointUrl"], 65 | ConfigurationManager.AppSettings["QnAMakerSubscriptionKey"], 66 | hostUrl: null)) // Host URL is not needed in config app 67 | .SingleInstance(); 68 | 69 | builder.Register(c => new BlobHelper( 70 | ConfigurationManager.AppSettings["StorageConnectionString"])) 71 | .SingleInstance(); 72 | 73 | builder.Register(c => new KnowledgeBaseRefreshHelper( 74 | c.Resolve(), 75 | c.Resolve(), 76 | c.Resolve(), 77 | c.Resolve(), 78 | c.Resolve())) 79 | .SingleInstance(); 80 | 81 | builder.RegisterType().InstancePerRequest(); 82 | 83 | var container = builder.Build(); 84 | DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 85 | 86 | return container; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 5 | { 6 | using System.Web.Optimization; 7 | 8 | /// 9 | /// Bundle config for Task Module app. 10 | /// 11 | public class BundleConfig 12 | { 13 | /// 14 | /// Register the bundles 15 | /// 16 | /// Collection of bundles 17 | public static void RegisterBundles(BundleCollection bundles) 18 | { 19 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 20 | "~/lib/jquery/jquery.js")); 21 | 22 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 23 | // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. 24 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 25 | "~/lib/modernizr/modernizr.js")); 26 | 27 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 28 | "~/lib/bootstrap/dist/js/bootstrap.js")); 29 | 30 | bundles.Add(new StyleBundle("~/Content/css").Include( 31 | "~/lib/bootstrap/dist/css/bootstrap.css", 32 | "~/Content/site.css")); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 5 | { 6 | using System.Web.Mvc; 7 | 8 | /// 9 | /// Filter config for Task Module app. 10 | /// 11 | public class FilterConfig 12 | { 13 | /// 14 | /// Register Global Filters 15 | /// 16 | /// Collection of global filters. 17 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 18 | { 19 | filters.Add(new Common.Filters.AiHandleErrorAttribute()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 6 | { 7 | using System.Web.Mvc; 8 | using System.Web.Routing; 9 | 10 | /// 11 | /// Router Config 12 | /// 13 | public class RouteConfig 14 | { 15 | /// 16 | /// Register Routes 17 | /// 18 | /// Routes collection 19 | public static void RegisterRoutes(RouteCollection routes) 20 | { 21 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 22 | 23 | routes.MapRoute( 24 | name: "Default", 25 | url: "{controller}/{action}/{id}", 26 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Content/Images/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-list-search/830dd2199549947a020a8f7870194d707d8a9022/Source/Microsoft.Teams.Apps.ListSearch.Configuration/Content/Images/loader.gif -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Content/Images/userIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-list-search/830dd2199549947a020a8f7870194d707d8a9022/Source/Microsoft.Teams.Apps.ListSearch.Configuration/Content/Images/userIcon.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Override the default bootstrap behavior where horizontal description lists 13 | will truncate terms that are too long to fit in the left column 14 | */ 15 | .dl-horizontal dt { 16 | white-space: normal; 17 | } 18 | 19 | /* Set width on the form input elements since they're 100% wide by default */ 20 | input, 21 | select, 22 | textarea { 23 | /*max-width: 280px;*/ 24 | height: 31px !important; 25 | } 26 | 27 | .userIcon { 28 | height: 70px; 29 | width: 70px; 30 | border-radius: 50%; 31 | } 32 | 33 | .sharePointUserIcon { 34 | height: 50px; 35 | margin-left: -15px; 36 | } 37 | 38 | .userRemove { 39 | margin-top: 3%; 40 | margin-left: 0%; 41 | padding-left: 0%; 42 | float: left; 43 | } 44 | 45 | .usersCenter { 46 | margin: auto; 47 | width: 75%; 48 | } 49 | 50 | .userEmail { 51 | font-size: 12px; 52 | color: #03225C 53 | } 54 | 55 | .fontUnset { 56 | font-weight: unset; 57 | } 58 | 59 | .btnPurple { 60 | background-color: #6264A7; 61 | color: white; 62 | } 63 | 64 | input[type="radio"] { 65 | position: absolute; 66 | margin-top: -5px; 67 | margin-left: -20px; 68 | } 69 | 70 | .loader { 71 | position: absolute; 72 | z-index: 9999; 73 | opacity: 0.5; 74 | background: #e9e9e9; 75 | display: none; 76 | height: 100%; 77 | width: 100%; 78 | top: 0; 79 | bottom: 0; 80 | left: 0; 81 | right: 0; 82 | } 83 | 84 | .loader-image { 85 | margin-left: 43%; 86 | margin-top: 16%; 87 | } 88 | 89 | .disableLinkButton { 90 | pointer-events: none; 91 | } 92 | 93 | #notification { 94 | position: fixed; 95 | top: 53px; 96 | right: 5px; 97 | z-index: 1000; 98 | width: 30%; 99 | text-align: left; 100 | font-weight: normal; 101 | font-size: 13px; 102 | color: white; 103 | background-color: grey; 104 | padding: 5px; 105 | border-radius: 7px; 106 | } 107 | 108 | #notification span.dismiss { 109 | border: 1px solid #FFF; 110 | padding: 0 5px; 111 | cursor: pointer; 112 | float: right; 113 | border-radius: 7px; 114 | } 115 | 116 | #notification a { 117 | color: white; 118 | text-decoration: none; 119 | font-weight: bold 120 | } 121 | 122 | .required:after { 123 | content: "*"; 124 | font-weight: bold; 125 | color: red; 126 | } 127 | 128 | .configureListButtons { 129 | min-width: 60%; 130 | float: right !important; 131 | } 132 | 133 | .btnSubmitFloat { 134 | float: right; 135 | } 136 | 137 | .btnCancelMargin { 138 | float: right; 139 | margin-right: 3%; 140 | } 141 | 142 | .listGridRowSpaceAndLabelSpace { 143 | padding-bottom: 1%; 144 | } 145 | 146 | .listGridTextCenter { 147 | padding-top: 5px; 148 | } 149 | 150 | .accessSharePoint { 151 | float: right; 152 | } 153 | 154 | .accessSharePointChange { 155 | float: right; 156 | margin-right: -3%; 157 | margin-top: -1%; 158 | } 159 | 160 | .container { 161 | width: 70%; 162 | margin-left: 14%; 163 | margin-right: 16%; 164 | min-width: 550px; 165 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Configuration.Controllers 6 | { 7 | using System.Web; 8 | using System.Web.Mvc; 9 | using Microsoft.Owin.Security; 10 | using Microsoft.Owin.Security.Cookies; 11 | using Microsoft.Owin.Security.OpenIdConnect; 12 | 13 | /// 14 | /// Account Controller 15 | /// 16 | public class AccountController : Controller 17 | { 18 | /// 19 | /// Sign in method 20 | /// 21 | public void SignIn() 22 | { 23 | if (!this.Request.IsAuthenticated) 24 | { 25 | this.HttpContext.GetOwinContext().Authentication.Challenge( 26 | new AuthenticationProperties { RedirectUri = "/" }, 27 | new string[] { OpenIdConnectAuthenticationDefaults.AuthenticationType, "AppLogin" }); 28 | } 29 | } 30 | 31 | /// 32 | /// sign out 33 | /// 34 | public void SignOut() 35 | { 36 | string callbackUrl = this.Url.Action("SignOutCallback", "Account", routeValues: null, protocol: this.Request.Url.Scheme); 37 | 38 | this.HttpContext.GetOwinContext().Authentication.SignOut( 39 | new AuthenticationProperties { RedirectUri = callbackUrl }, new string[] { OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType, "AppLogin" }); 40 | } 41 | 42 | /// 43 | /// callback method 44 | /// 45 | /// returns view 46 | public ActionResult SignOutCallback() 47 | { 48 | if (this.Request.IsAuthenticated) 49 | { 50 | return this.RedirectToAction("Index", "Home"); 51 | } 52 | 53 | return this.View(); 54 | } 55 | 56 | /// 57 | /// Invalid User 58 | /// 59 | /// upn of unauthorized user 60 | /// Action Result 61 | public ActionResult InvalidUser(string upn) 62 | { 63 | this.ViewBag.Upn = upn; 64 | this.HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType); 65 | return this.View(); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Teams.Apps.ListSearch.Configuration.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Global.asax.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 3 | { 4 | /// 5 | /// Mvc Application 6 | /// 7 | public class MvcApplication : System.Web.HttpApplication 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Models/HomeViewModel.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Configuration.Models 6 | { 7 | using System.Collections.Generic; 8 | using Microsoft.Teams.Apps.ListSearch.Common.Models; 9 | using Newtonsoft.Json; 10 | 11 | /// 12 | /// Home page view model 13 | /// 14 | public class HomeViewModel 15 | { 16 | /// 17 | /// Gets or sets SharePointUserUpn 18 | /// 19 | [JsonProperty("SharePointUserUpn")] 20 | public string SharePointUserUpn { get; set; } 21 | 22 | /// 23 | /// Gets or sets Knowledge base List 24 | /// 25 | [JsonProperty("KBList")] 26 | public List KBList { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.ListSearch.Configuration")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.ListSearch")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("70724b6b-b57a-47be-84ac-803d56d3b3b2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Startup.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.Teams.Apps.ListSearch.Configuration 3 | { 4 | using System.Web.Mvc; 5 | using System.Web.Optimization; 6 | using System.Web.Routing; 7 | using global::Owin; 8 | 9 | /// 10 | /// Startup 11 | /// 12 | public partial class Startup 13 | { 14 | /// 15 | /// Configuration 16 | /// 17 | /// IAppBuilder app 18 | public void Configuration(IAppBuilder app) 19 | { 20 | AreaRegistration.RegisterAllAreas(); 21 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 22 | RouteConfig.RegisterRoutes(RouteTable.Routes); 23 | BundleConfig.RegisterBundles(BundleTable.Bundles); 24 | 25 | var container = AutofacConfig.RegisterDependencies(); 26 | ConfigureAuth(app, container); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Account/InvalidUser.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Invalid User"; 3 | } 4 |

@ViewBag.Title.

5 |

This user does not have the proper permissions for this action. Please sign in with a valid user's credentials. In case you are still unable to access the app please ask the app administrator to add @ViewBag.Upn to the list of users allowed to configure this app

-------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Account/SignOutCallback.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Sign Out"; 3 | } 4 |

@ViewBag.Title.

5 |

You have successfully signed out.

6 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.Teams.Apps.ListSearch.Configuration.Models 2 | @using Microsoft.Teams.Apps.Common; 3 | @{ 4 | ViewBag.Title = "Home Page"; 5 | } 6 | 7 | @model HomeViewModel 8 | 9 |
10 |
11 |

Home

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 | @if (Model.SharePointUserUpn == null) 20 | { 21 | 27 |
28 | 29 |
30 | } 31 | else 32 | { 33 |
34 | 35 | 36 |
37 | 38 | Change 39 |
40 |
41 | } 42 |
43 |
44 | 45 |
46 | 47 | @if (Model.KBList.Count == 0) 48 | { 49 |
50 | 51 |
52 | } 53 | else 54 | { 55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 | 64 |
65 |
66 |
67 |
68 |
69 |
70 | } 71 | @foreach (var kb in Model.KBList) 72 | { 73 | var unixTimeStamp = (long)(kb.LastRefreshDateTime - CommonConstant.Epoch).TotalMilliseconds; 74 |
75 |
76 | @kb.KBName 77 |
78 |
79 | 80 |
81 |
82 | 83 |
84 |
85 | @Html.ActionLink("Edit", "ConfigureList", "Home", new { id = kb.KBId }, new { @class = "btn btn-default btn-sm btnPurple disableAction" }) 86 |
87 |
88 | @Html.ActionLink("Delete", "DeleteKB", "Home", new { id = kb.KBId }, new { onclick = "return confirm('Are you sure you want to remove this list?')", @class = "btn btn-default btn-sm btnPurple disableAction" }) 89 |
90 |
91 | } 92 | 93 | 101 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - Configuration App 7 | 8 | @Styles.Render("~/Content/css") 9 | @Scripts.Render("~/bundles/modernizr") 10 | @Scripts.Render("~/bundles/jquery") 11 | @Scripts.Render("~/bundles/bootstrap") 12 | @RenderSection("scripts", required: false) 13 | 14 | 15 | 51 | 52 |
53 | @RenderBody() 54 |
55 | 60 |
61 | 62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "provider": "cdnjs", 7 | "library": "jquery@3.4.1", 8 | "destination": "lib/jquery/", 9 | "files": [ 10 | "jquery.min.js", 11 | "jquery.js" 12 | ] 13 | }, 14 | { 15 | "provider": "cdnjs", 16 | "library": "modernizr@2.8.3", 17 | "destination": "lib/modernizr/", 18 | "files": [ 19 | "modernizr.min.js", 20 | "modernizr.js" 21 | ] 22 | }, 23 | { 24 | "provider": "unpkg", 25 | "library": "bootstrap@3.4.1", 26 | "destination": "lib/bootstrap/", 27 | "files": [ 28 | "dist/css/bootstrap-theme.css", 29 | "dist/css/bootstrap-theme.css.map", 30 | "dist/css/bootstrap-theme.min.css", 31 | "dist/css/bootstrap-theme.min.css.map", 32 | "dist/css/bootstrap.css", 33 | "dist/css/bootstrap.css.map", 34 | "dist/css/bootstrap.min.css", 35 | "dist/css/bootstrap.min.css.map", 36 | "dist/fonts/glyphicons-halflings-regular.eot", 37 | "dist/fonts/glyphicons-halflings-regular.svg", 38 | "dist/fonts/glyphicons-halflings-regular.ttf", 39 | "dist/fonts/glyphicons-halflings-regular.woff", 40 | "dist/fonts/glyphicons-halflings-regular.woff2", 41 | "dist/js/bootstrap.js", 42 | "dist/js/bootstrap.min.js" 43 | ] 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.Configuration/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.ListSearch", "Microsoft.Teams.Apps.ListSearch\Microsoft.Teams.Apps.ListSearch.csproj", "{D983A353-D791-4930-A125-0B1B3AA7D621}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.ListSearch.Common", "Microsoft.Teams.Apps.ListSearch.Common\Microsoft.Teams.Apps.ListSearch.Common.csproj", "{20FA7B4D-0C91-421F-9675-5A43DE20BA5A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.ListSearch.Configuration", "Microsoft.Teams.Apps.ListSearch.Configuration\Microsoft.Teams.Apps.ListSearch.Configuration.csproj", "{8819B2A6-E4F7-4528-8AFD-5A878F73F7E6}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.Common", "Microsoft.Teams.Apps.Common\Microsoft.Teams.Apps.Common.csproj", "{7FA2791F-E894-4876-8773-A5318631365C}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {D983A353-D791-4930-A125-0B1B3AA7D621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D983A353-D791-4930-A125-0B1B3AA7D621}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D983A353-D791-4930-A125-0B1B3AA7D621}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D983A353-D791-4930-A125-0B1B3AA7D621}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {20FA7B4D-0C91-421F-9675-5A43DE20BA5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {20FA7B4D-0C91-421F-9675-5A43DE20BA5A}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {20FA7B4D-0C91-421F-9675-5A43DE20BA5A}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {20FA7B4D-0C91-421F-9675-5A43DE20BA5A}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {8819B2A6-E4F7-4528-8AFD-5A878F73F7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {8819B2A6-E4F7-4528-8AFD-5A878F73F7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {8819B2A6-E4F7-4528-8AFD-5A878F73F7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {8819B2A6-E4F7-4528-8AFD-5A878F73F7E6}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {7FA2791F-E894-4876-8773-A5318631365C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {7FA2791F-E894-4876-8773-A5318631365C}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {7FA2791F-E894-4876-8773-A5318631365C}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {7FA2791F-E894-4876-8773-A5318631365C}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {90876D38-407A-43EE-BBB8-A33A604B8CAD} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/.gitignore: -------------------------------------------------------------------------------- 1 | # Paths with files restored by libman 2 | lib/bootstrap/ 3 | lib/jquery/ 4 | lib/modernizr/ 5 | lib/microsoft-teams/ 6 | lib/msteams-ui-styles-core/ 7 | lib/msteams-ui-icons-core/ -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/App_Start/AutofacConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch 6 | { 7 | using System.Configuration; 8 | using System.Net.Http; 9 | using System.Reflection; 10 | using System.Web.Http; 11 | using System.Web.Mvc; 12 | using Autofac; 13 | using Autofac.Integration.Mvc; 14 | using Autofac.Integration.WebApi; 15 | using Microsoft.Teams.Apps.Common.Configuration; 16 | using Microsoft.Teams.Apps.Common.Logging; 17 | using Microsoft.Teams.Apps.ListSearch.Common.Helpers; 18 | 19 | /// 20 | /// Autofac configuration 21 | /// 22 | public class AutofacConfig 23 | { 24 | /// 25 | /// Register Autofac dependencies 26 | /// 27 | public static void RegisterDependencies() 28 | { 29 | var builder = new ContainerBuilder(); 30 | 31 | builder.RegisterControllers(Assembly.GetExecutingAssembly()); 32 | builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); 33 | 34 | var connectionString = ConfigurationManager.AppSettings["StorageConnectionString"]; 35 | 36 | var config = new LocalConfigProvider(); 37 | builder.Register(c => config) 38 | .As() 39 | .SingleInstance(); 40 | 41 | builder.Register(c => new AppInsightsLogProvider(c.Resolve())) 42 | .As() 43 | .SingleInstance(); 44 | 45 | builder.Register(c => new HttpClient()) 46 | .SingleInstance(); 47 | 48 | builder.Register(c => new JwtHelper( 49 | jwtSecurityKey: ConfigurationManager.AppSettings["TokenEncryptionKey"], 50 | botId: ConfigurationManager.AppSettings["MicrosoftAppId"])) 51 | .SingleInstance(); 52 | 53 | builder.Register(c => new KBInfoHelper(connectionString)) 54 | .SingleInstance(); 55 | 56 | builder.Register(c => new TokenHelper( 57 | c.Resolve(), 58 | connectionString, 59 | ConfigurationManager.AppSettings["TenantId"], 60 | ConfigurationManager.AppSettings["GraphAppClientId"], 61 | ConfigurationManager.AppSettings["GraphAppClientSecret"], 62 | ConfigurationManager.AppSettings["TokenEncryptionKey"])) 63 | .SingleInstance(); 64 | 65 | builder.Register(c => new GraphHelper( 66 | c.Resolve(), 67 | c.Resolve())) 68 | .SingleInstance(); 69 | 70 | builder.Register(c => new BlobHelper(connectionString)) 71 | .SingleInstance(); 72 | 73 | builder.Register(c => new QnAMakerService( 74 | c.Resolve(), 75 | ConfigurationManager.AppSettings["QnaMakerApiEndpointUrl"], 76 | ConfigurationManager.AppSettings["QnAMakerSubscriptionKey"], 77 | ConfigurationManager.AppSettings["QnAMakerHostUrl"])) 78 | .SingleInstance(); 79 | 80 | builder.Register(c => new KnowledgeBaseRefreshHelper( 81 | c.Resolve(), 82 | c.Resolve(), 83 | c.Resolve(), 84 | c.Resolve(), 85 | c.Resolve())) 86 | .SingleInstance(); 87 | 88 | var container = builder.Build(); 89 | DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 90 | GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch 6 | { 7 | using System.Web.Optimization; 8 | 9 | /// 10 | /// Bundle config for Task Module app. 11 | /// 12 | public class BundleConfig 13 | { 14 | /// 15 | /// For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 16 | /// 17 | /// Collection of bundles 18 | // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 19 | public static void RegisterBundles(BundleCollection bundles) 20 | { 21 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 22 | "~/lib/jquery/jquery.min.js")); 23 | 24 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 25 | // ready for production, use the build tool at https://modernizr.com to pick only the tests you need. 26 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 27 | "~/lib/modernizr/modernizr.min.js")); 28 | 29 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 30 | "~/lib/bootstrap/dist/js/bootstrap.min.js")); 31 | 32 | bundles.Add(new ScriptBundle("~/bundles/msteams").Include( 33 | "~/lib/microsoft-teams/dist/MicrosoftTeams.min.js")); 34 | 35 | bundles.Add(new StyleBundle("~/Content/css").Include( 36 | "~/lib/bootstrap/dist/css/bootstrap.min.css", 37 | "~/lib/msteams-ui-styles-core/css/msteams-10.css", 38 | "~/Content/spinner.css", 39 | "~/Content/customTeamsStyle.css", 40 | "~/Content/customTheme.css")); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch 6 | { 7 | using System.Web.Mvc; 8 | 9 | /// 10 | /// Filter config for Task Module app. 11 | /// 12 | public class FilterConfig 13 | { 14 | /// 15 | /// Register Global Filters 16 | /// 17 | /// Collection of global filters. 18 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 19 | { 20 | filters.Add(new Common.Filters.AiHandleErrorAttribute()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch 6 | { 7 | using System.Web.Mvc; 8 | using System.Web.Routing; 9 | 10 | /// 11 | /// Route Config for Task Module App 12 | /// 13 | public class RouteConfig 14 | { 15 | /// 16 | /// Register routes for the app. 17 | /// 18 | /// Collection of routes. 19 | public static void RegisterRoutes(RouteCollection routes) 20 | { 21 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 22 | 23 | routes.MapRoute( 24 | name: "Default", 25 | url: "{controller}/{action}/{id}", 26 | defaults: new { controller = "Search", action = "Index", id = UrlParameter.Optional }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch 6 | { 7 | using System.Web.Http; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Serialization; 10 | 11 | /// 12 | /// Web API config for the Bot 13 | /// 14 | public static class WebApiConfig 15 | { 16 | /// 17 | /// Register configuration 18 | /// 19 | /// Http configuration 20 | public static void Register(HttpConfiguration config) 21 | { 22 | // Json settings 23 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; 24 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 25 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 26 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 27 | { 28 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 29 | Formatting = Newtonsoft.Json.Formatting.Indented, 30 | NullValueHandling = NullValueHandling.Ignore, 31 | }; 32 | 33 | // Web API configuration and services 34 | 35 | // Web API routes 36 | config.MapHttpAttributeRoutes(); 37 | 38 | config.Routes.MapHttpRoute( 39 | name: "DefaultApi", 40 | routeTemplate: "api/{controller}/{action}/{id}", 41 | defaults: new { id = RouteParameter.Optional }); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Content/customTheme.css: -------------------------------------------------------------------------------- 1 | .theme-dark { 2 | color: #fff !important; 3 | } 4 | 5 | .theme-dark.surface { 6 | background: #2d2c2c !important; 7 | } 8 | 9 | .theme-dark .labelOverwrite { 10 | color: inherit !important; 11 | } 12 | 13 | .theme-dark .placeholder-text { 14 | color: inherit !important; 15 | } 16 | 17 | .theme-dark .select { 18 | background-color: #201f1f !important; 19 | color: inherit !important; 20 | } 21 | 22 | .theme-dark .resultItem:hover { 23 | background: #3b3a39; 24 | } 25 | 26 | .theme-dark .resultItem:focus { 27 | background: #3b3a39; 28 | outline: none; 29 | } 30 | 31 | .theme-dark .select-items { 32 | background-color: #2d2c2c !important; 33 | border-top: .1rem solid #6264a7 !important; 34 | } 35 | 36 | .theme-dark .select-item:hover { 37 | background-color: #3b3a39; 38 | } 39 | 40 | 41 | .theme-dark .select-item:focus { 42 | background-color: #3b3a39; 43 | outline: none; 44 | } 45 | 46 | .theme-contrast { 47 | color: #fff !important; 48 | } 49 | 50 | .theme-contrast.surface { 51 | background: #000 !important; 52 | } 53 | 54 | .theme-contrast .labelOverwrite { 55 | color: inherit !important; 56 | } 57 | 58 | .theme-contrast .placeholder-text { 59 | color: inherit !important; 60 | } 61 | 62 | .theme-contrast .select { 63 | background-color: #000 !important; 64 | border: .1rem solid #fff; 65 | color: inherit !important; 66 | } 67 | 68 | .theme-contrast .resultItem:hover { 69 | background: rgb(255, 255, 1) !important; 70 | color: rgb(0, 0, 0) !important; 71 | } 72 | 73 | .theme-contrast .resultItem:focus { 74 | background: rgb(255, 255, 1) !important; 75 | color: rgb(0, 0, 0) !important; 76 | outline: none; 77 | } 78 | 79 | .theme-contrast .select-items { 80 | background-color: #000 !important; 81 | border: .1rem solid #fff; 82 | border-top: .1rem solid rgb(255, 255, 1) !important; 83 | } 84 | 85 | .theme-contrast .select-item:hover { 86 | background-color: rgb(255, 255, 1) !important; 87 | color: rgb(0, 0, 0) !important; 88 | } 89 | 90 | .theme-contrast .select-item:focus { 91 | background-color: rgb(255, 255, 1) !important; 92 | color: rgb(0, 0, 0) !important; 93 | } 94 | 95 | .theme-contrast .input-field { 96 | border: .1rem solid #fff; 97 | } 98 | .theme-contrast .select-wrapper:focus { 99 | outline-color: #FFFF01; 100 | } 101 | .theme-contrast #resultItems:focus { 102 | outline-color: #FFFF01; 103 | } 104 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Content/spinner.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes translate-small { 2 | to { 3 | -webkit-transform: translate3d(0, -144rem, 0); 4 | transform: translate3d(0, -144rem, 0); 5 | } 6 | } 7 | 8 | @keyframes translate-small { 9 | to { 10 | -webkit-transform: translate3d(0, -144rem, 0); 11 | transform: translate3d(0, -144rem, 0); 12 | } 13 | } 14 | 15 | @-webkit-keyframes translate-medium { 16 | to { 17 | -webkit-transform: translate3d(0, -216rem, 0); 18 | transform: translate3d(0, -216rem, 0); 19 | } 20 | } 21 | 22 | @keyframes translate-medium { 23 | to { 24 | -webkit-transform: translate3d(0, -216rem, 0); 25 | transform: translate3d(0, -216rem, 0); 26 | } 27 | } 28 | 29 | @-webkit-keyframes translate-large { 30 | to { 31 | -webkit-transform: translate3d(0, -432rem, 0); 32 | transform: translate3d(0, -432rem, 0); 33 | } 34 | } 35 | 36 | @keyframes translate-large { 37 | to { 38 | -webkit-transform: translate3d(0, -432rem, 0); 39 | transform: translate3d(0, -432rem, 0); 40 | } 41 | } 42 | 43 | @-webkit-keyframes fade-spinner-in { 44 | to { 45 | opacity: 1; 46 | } 47 | } 48 | 49 | @keyframes fade-spinner-in { 50 | to { 51 | opacity: 1; 52 | } 53 | } 54 | 55 | busy-animation { 56 | display: inline-block; 57 | } 58 | 59 | .ts-spinner-internal { 60 | overflow: hidden; 61 | } 62 | 63 | .ts-spinner-internal.size-1x { 64 | height: 2.4rem; 65 | width: 2.4rem; 66 | } 67 | 68 | .ts-spinner-internal.size-1x .spinner-animate { 69 | width: 2.4rem; 70 | height: 146.4rem; 71 | -webkit-animation: 2s steps(60) both infinite translate-small; 72 | animation: 2s steps(60) both infinite translate-small; 73 | } 74 | 75 | .ts-spinner-internal.size-2x { 76 | height: 3.6rem; 77 | width: 3.6rem; 78 | margin: auto; 79 | } 80 | 81 | .ts-spinner-internal.size-2x .spinner-animate { 82 | width: 3.6rem; 83 | height: 219.6rem; 84 | -webkit-animation: 2s steps(60) both infinite translate-medium; 85 | animation: 2s steps(60) both infinite translate-medium; 86 | } 87 | 88 | .ts-spinner-internal.size-3x { 89 | height: 7.2rem; 90 | width: 7.2rem; 91 | } 92 | 93 | .ts-spinner-internal.size-3x .spinner-animate { 94 | width: 7.2rem; 95 | height: 439.2rem; 96 | -webkit-animation: 2s steps(60) both infinite translate-large; 97 | animation: 2s steps(60) both infinite translate-large; 98 | } 99 | 100 | .ts-spinner-container { 101 | overflow: hidden; 102 | position: relative; 103 | } 104 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Content/teams-custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI'; 3 | margin: 1em; 4 | } 5 | 6 | .greyCustomColor { 7 | color: rgb(127,127,127); 8 | margin: 0 !important; 9 | } 10 | 11 | .listImage { 12 | padding-right: 1em; 13 | width: 30px; 14 | vertical-align: middle; 15 | } 16 | 17 | .row { 18 | cursor: pointer 19 | } 20 | 21 | .row:after { 22 | content: ""; 23 | display: table; 24 | clear: both; 25 | } 26 | 27 | .column { 28 | float: left; 29 | width: 50%; 30 | } 31 | 32 | .listText { 33 | font-size: small; 34 | } 35 | 36 | .parentDiv { 37 | cursor: default; 38 | } 39 | 40 | .searchDiv { 41 | overflow-y: scroll; 42 | height: 65%; 43 | } 44 | 45 | .centerAlignText { 46 | text-align: center; 47 | padding-top: 10em; 48 | } 49 | 50 | .searchResults { 51 | border-bottom: black solid; 52 | cursor: pointer; 53 | } 54 | 55 | .feedbackButton { 56 | background-color: rgb(68,114,196) !important; 57 | border: solid thin rgb(47,82,143) !important; 58 | color: white !important; 59 | } 60 | 61 | .button { 62 | color: rgb(95,95,95); 63 | background-color: transparent; 64 | height: 3em; 65 | width: auto; 66 | cursor: pointer; 67 | padding: 1em; 68 | } 69 | 70 | .shareButton { 71 | border-color: rgb(128,128,128); 72 | border-radius: 0.5em; 73 | border-style: solid; 74 | border-width: thin; 75 | float: right; 76 | } 77 | 78 | .backButton { 79 | border: none; 80 | float: left; 81 | } 82 | 83 | 84 | .loader { 85 | color: #7f7f7f; 86 | font-size: 20px; 87 | margin: 100px auto; 88 | width: 1em; 89 | height: 1em; 90 | border-radius: 50%; 91 | position: relative; 92 | text-indent: -9999em; 93 | -webkit-animation: load4 1.3s infinite linear; 94 | animation: load4 1.3s infinite linear; 95 | -webkit-transform: translateZ(0); 96 | -ms-transform: translateZ(0); 97 | transform: translateZ(0); 98 | } 99 | 100 | @-webkit-keyframes load4 { 101 | 0%, 100% { 102 | box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; 103 | } 104 | 105 | 12.5% { 106 | box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; 107 | } 108 | 109 | 25% { 110 | box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; 111 | } 112 | 113 | 37.5% { 114 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; 115 | } 116 | 117 | 50% { 118 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; 119 | } 120 | 121 | 62.5% { 122 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; 123 | } 124 | 125 | 75% { 126 | box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; 127 | } 128 | 129 | 87.5% { 130 | box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; 131 | } 132 | } 133 | 134 | @keyframes load4 { 135 | 0%, 100% { 136 | box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; 137 | } 138 | 139 | 12.5% { 140 | box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; 141 | } 142 | 143 | 25% { 144 | box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; 145 | } 146 | 147 | 37.5% { 148 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; 149 | } 150 | 151 | 50% { 152 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; 153 | } 154 | 155 | 62.5% { 156 | box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; 157 | } 158 | 159 | 75% { 160 | box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; 161 | } 162 | 163 | 87.5% { 164 | box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; 165 | } 166 | } 167 | label { 168 | display: block; 169 | margin-block-start: 1em; 170 | margin-block-end: 1em; 171 | margin-inline-start: 0px; 172 | margin-inline-end: 0px; 173 | } 174 | .clickableLabel 175 | { 176 | cursor:pointer; 177 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Controllers/ErrorController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Controllers 6 | { 7 | using System.Web.Mvc; 8 | using Microsoft.Teams.Apps.ListSearch.Resources; 9 | 10 | /// 11 | /// Error Controller 12 | /// 13 | public class ErrorController : Controller 14 | { 15 | /// 16 | /// Error view 17 | /// 18 | /// The error to show 19 | /// True if this should be partial view 20 | /// Task that resolves to representing Error view. 21 | public ActionResult Index(string code = null, bool isPartialView = false) 22 | { 23 | switch (code) 24 | { 25 | case "Unauthorized": 26 | this.ViewBag.Title = Strings.ErrorUnauthorizedTitle; 27 | this.ViewBag.Message = Strings.ErrorUnauthorizedMessage; 28 | break; 29 | 30 | case "SessionExpired": 31 | this.ViewBag.Title = Strings.ErrorSessionExpiredTitle; 32 | this.ViewBag.Message = Strings.ErrorSessionExpiredMessage; 33 | break; 34 | 35 | default: 36 | this.ViewBag.Title = Strings.ErrorGenericTitle; 37 | this.ViewBag.Message = Strings.ErrorGenericMessage; 38 | break; 39 | } 40 | 41 | return isPartialView ? (ActionResult)this.PartialView("ErrorPartial") : this.View(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Controllers/RefreshController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Controllers 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using System.Web.Http; 11 | using Microsoft.Teams.Apps.Common.Extensions; 12 | using Microsoft.Teams.Apps.Common.Logging; 13 | using Microsoft.Teams.Apps.ListSearch.Common.Helpers; 14 | using Microsoft.Teams.Apps.ListSearch.Common.Models; 15 | 16 | /// 17 | /// Controller to refresh the KB. 18 | /// 19 | public class RefreshController : ApiController 20 | { 21 | private readonly KBInfoHelper kbInfoHelper; 22 | private readonly KnowledgeBaseRefreshHelper refreshHelper; 23 | private readonly ILogProvider logProvider; 24 | 25 | /// 26 | /// Initializes a new instance of the class. 27 | /// 28 | /// KB helper to use 29 | /// KB refresh helper to use 30 | /// Log provider to be used 31 | public RefreshController(KBInfoHelper kbInfoHelper, KnowledgeBaseRefreshHelper refreshHelper, ILogProvider logProvider) 32 | { 33 | this.kbInfoHelper = kbInfoHelper; 34 | this.refreshHelper = refreshHelper; 35 | this.logProvider = logProvider; 36 | } 37 | 38 | /// 39 | /// Refreshes all KBs due for a refresh 40 | /// 41 | /// to refresh KBs. 42 | [HttpPost] 43 | public async Task RefreshAllKBs() 44 | { 45 | if (!this.ValidateAuthorizationHeader()) 46 | { 47 | return this.Unauthorized(); 48 | } 49 | 50 | this.logProvider.LogInfo("Refreshing all knowledge bases"); 51 | 52 | List kbList = await this.kbInfoHelper.GetAllKBs( 53 | fields: new string[] 54 | { 55 | nameof(KBInfo.LastRefreshDateTime), 56 | nameof(KBInfo.RefreshFrequencyInHours), 57 | nameof(KBInfo.SharePointListId), 58 | nameof(KBInfo.QuestionField), 59 | nameof(KBInfo.AnswerFields), 60 | nameof(KBInfo.SharePointSiteId), 61 | nameof(KBInfo.LastRefreshAttemptDateTime), 62 | nameof(KBInfo.LastRefreshAttemptError), 63 | }); 64 | this.logProvider.LogInfo($"Found {kbList.Count} knowledge bases"); 65 | 66 | int refreshTasksCount = 0; 67 | foreach (var kb in kbList) 68 | { 69 | DateTime lastRefreshed = kb.LastRefreshDateTime; 70 | int frequencyInHours = kb.RefreshFrequencyInHours; 71 | if (lastRefreshed == DateTime.MinValue || frequencyInHours == 0) 72 | { 73 | continue; 74 | } 75 | 76 | if (lastRefreshed.AddHours(frequencyInHours) < DateTime.UtcNow) 77 | { 78 | Guid correlationId = Guid.NewGuid(); 79 | this.logProvider.LogInfo($"Queueing refresh task for knowledge base {kb.KBId} (correlation id {correlationId})", correlationId: correlationId); 80 | System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(ct => this.RefreshKnowledgeBaseAsync(kb, correlationId, ct)); 81 | refreshTasksCount++; 82 | } 83 | else 84 | { 85 | this.logProvider.LogInfo($"Skipping refresh for {kb.KBId}, refreshed less than {kb.RefreshFrequencyInHours} hours ago"); 86 | } 87 | } 88 | 89 | return this.StatusCode(refreshTasksCount > 0 ? System.Net.HttpStatusCode.Accepted : System.Net.HttpStatusCode.OK); 90 | } 91 | 92 | private async Task RefreshKnowledgeBaseAsync(KBInfo kb, Guid correlationId, System.Threading.CancellationToken cancelToken) 93 | { 94 | Exception refreshError = null; 95 | 96 | try 97 | { 98 | await this.refreshHelper.RefreshKnowledgeBaseAsync(kb, correlationId); 99 | } 100 | catch (Exception ex) 101 | { 102 | refreshError = ex; 103 | this.logProvider.LogWarning($"Failed to refresh KB {kb.KBId}: {ex.Message}", exception: ex, correlationId: correlationId); 104 | } 105 | 106 | // Log success/failure of the knowledge base refresh 107 | var properties = new Dictionary 108 | { 109 | { "KnowledgeBaseId", kb.KBId }, 110 | { "KnowledgeBaseName", kb.KBName }, 111 | { "Success", (refreshError != null).ToString() }, 112 | }; 113 | if (refreshError != null) 114 | { 115 | properties["LastRefreshDateTime"] = kb.LastRefreshDateTime.ToString("u"); 116 | properties["ErrorMessage"] = refreshError.Message; 117 | } 118 | 119 | this.logProvider.LogEvent("KnowledgeBaseRefresh", properties, correlationId: correlationId); 120 | } 121 | 122 | // Validate the value of the Authorize header 123 | private bool ValidateAuthorizationHeader() 124 | { 125 | var authorization = this.Request.Headers.Authorization; 126 | return (authorization?.Scheme?.ToLowerInvariant() == "bearer") && 127 | (authorization.Parameter == System.Configuration.ConfigurationManager.AppSettings["RefreshEndpointKey"]); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Filters/JwtExceptionFilter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Filters 6 | { 7 | using System.Linq; 8 | using System.Web.Mvc; 9 | using Microsoft.IdentityModel.Tokens; 10 | using Microsoft.Teams.Apps.Common.Extensions; 11 | using Microsoft.Teams.Apps.Common.Logging; 12 | 13 | /// 14 | /// Custom exception handler for JWT validation errors 15 | /// 16 | public class JwtExceptionFilter : FilterAttribute, IExceptionFilter 17 | { 18 | private const string ErrorController = "Error"; 19 | private const string LifetimeValidationFailedExceptionCode = "IDX10223"; 20 | private static readonly string[] ExpectedJWTExceptionSources = new string[] { "System.IdentityModel.Tokens.Jwt", "Microsoft.IdentityModel.Tokens" }; 21 | 22 | /// 23 | /// Gets or sets a value indicating whether the filter should redirect to a partial view on error 24 | /// 25 | public bool IsPartialView 26 | { 27 | get; set; 28 | } 29 | 30 | /// 31 | public void OnException(ExceptionContext filterContext) 32 | { 33 | var logProvider = DependencyResolver.Current.GetService(); 34 | 35 | string errorCode = string.Empty; 36 | 37 | var ex = filterContext.Exception; 38 | if (ex.GetType() == typeof(SecurityTokenException) || ExpectedJWTExceptionSources.Contains(ex.Source)) 39 | { 40 | if (ex.Message.Contains(LifetimeValidationFailedExceptionCode)) 41 | { 42 | logProvider.LogInfo("Access denied: Expired JWT"); 43 | errorCode = "SessionExpired"; 44 | } 45 | else 46 | { 47 | logProvider.LogInfo("Access denied: Invalid JWT"); 48 | errorCode = "Unauthorized"; 49 | } 50 | } 51 | else 52 | { 53 | logProvider.LogError("Error while processing request", exception: ex); 54 | } 55 | 56 | filterContext.Result = new RedirectResult($"/{ErrorController}?code={errorCode}&isPartialView={this.IsPartialView}"); 57 | filterContext.ExceptionHandled = true; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Teams.Apps.ListSearch.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Global.asax.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.Teams.Apps.ListSearch 3 | { 4 | using System.Web.Http; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | 9 | /// 10 | /// Web API Application 11 | /// 12 | public class WebApiApplication : System.Web.HttpApplication 13 | { 14 | /// 15 | /// Application start method for the application. 16 | /// 17 | protected void Application_Start() 18 | { 19 | AreaRegistration.RegisterAllAreas(); 20 | GlobalConfiguration.Configure(WebApiConfig.Register); 21 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 22 | RouteConfig.RegisterRoutes(RouteTable.Routes); 23 | BundleConfig.RegisterBundles(BundleTable.Bundles); 24 | AutofacConfig.RegisterDependencies(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/AuthenticationFailureResult.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | using System.Net; 8 | using System.Net.Http; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using System.Web.Http; 12 | 13 | /// 14 | /// Authentication Failure Result 15 | /// 16 | public class AuthenticationFailureResult : IHttpActionResult 17 | { 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// Reason phrase for failure 22 | /// Request 23 | public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request) 24 | { 25 | this.ReasonPhrase = reasonPhrase; 26 | this.Request = request; 27 | } 28 | 29 | /// 30 | /// Gets Reason Phrase 31 | /// 32 | public string ReasonPhrase { get; private set; } 33 | 34 | /// 35 | /// Gets Request 36 | /// 37 | public HttpRequestMessage Request { get; private set; } 38 | 39 | /// 40 | /// Executes Task 41 | /// 42 | /// cancellation token 43 | /// that resolves to 44 | public Task ExecuteAsync(CancellationToken cancellationToken) 45 | { 46 | return Task.FromResult(this.Execute()); 47 | } 48 | 49 | /// 50 | /// Execute 51 | /// 52 | /// containing request message and reason phrase. 53 | private HttpResponseMessage Execute() 54 | { 55 | HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized) 56 | { 57 | RequestMessage = this.Request, 58 | ReasonPhrase = this.ReasonPhrase, 59 | }; 60 | return response; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/ComposeExtensionResultType.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | /// 8 | /// Messaging Extension result types 9 | /// 10 | public static class ComposeExtensionResultType 11 | { 12 | /// 13 | /// Continue task 14 | /// 15 | public const string TaskContinue = "continue"; 16 | 17 | /// 18 | /// Result 19 | /// 20 | public const string Result = "result"; 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/ConfigEntity.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | using Microsoft.WindowsAzure.Storage.Table; 8 | 9 | /// 10 | /// Structure of Config Entity in table storage. 11 | /// 12 | public class ConfigEntity : TableEntity 13 | { 14 | /// 15 | /// Gets or sets property that represents Data field of the Table Entity. 16 | /// 17 | public string Data { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/GetListContentsResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | 10 | /// 11 | /// Get List Contents Response 12 | /// 13 | public class GetListContentsResponse 14 | { 15 | /// 16 | /// Gets or sets odata context 17 | /// 18 | [JsonProperty("@odata.context")] 19 | public string ODataContext { get; set; } 20 | 21 | /// 22 | /// Gets or sets odata next link 23 | /// 24 | [JsonProperty("@odata.nextLink")] 25 | public string ODataNextLink { get; set; } 26 | 27 | /// 28 | /// Gets or sets value 29 | /// 30 | [JsonProperty("value")] 31 | public object Value { get; set; } 32 | } 33 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/JWTExceptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | /// 8 | /// JWT Exception 9 | /// 10 | public static class JWTExceptions 11 | { 12 | /// 13 | /// Exception code for lifetime validation failure of jwt 14 | /// 15 | public static readonly string LifetimeValidationFailedExceptionCode = "IDX10223"; 16 | 17 | /// 18 | /// Expected sources for JWT Exceptions 19 | /// 20 | public static readonly string[] ExpectedJWTExceptionSources = new string[] { "System.IdentityModel.Tokens.Jwt", "Microsoft.IdentityModel.Tokens" }; 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/KBInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | using System; 8 | using Microsoft.WindowsAzure.Storage.Table; 9 | using Newtonsoft.Json; 10 | 11 | /// 12 | /// Represents KB Info in storage. 13 | /// 14 | public class KBInfo : TableEntity 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public KBInfo() 20 | { 21 | } 22 | 23 | /// 24 | /// Gets the kb id 25 | /// 26 | public string KBId 27 | { 28 | get 29 | { 30 | return this.RowKey; 31 | } 32 | } 33 | 34 | /// 35 | /// Gets or sets name of the kb 36 | /// 37 | [JsonProperty("KBName")] 38 | public string KBName { get; set; } 39 | 40 | /// 41 | /// Gets or sets sharepoint url of the list 42 | /// 43 | [JsonProperty("SharePointUrl")] 44 | public string SharePointUrl { get; set; } 45 | 46 | /// 47 | /// Gets or sets sharepoint site id. 48 | /// 49 | [JsonProperty("SharePointSiteId")] 50 | public string SharePointSiteId { get; set; } 51 | 52 | /// 53 | /// Gets or sets question field 54 | /// 55 | [JsonProperty("QuestionField")] 56 | public string QuestionField { get; set; } 57 | 58 | /// 59 | /// Gets or sets answer field 60 | /// 61 | [JsonProperty("AnswerField")] 62 | public string AnswerFields { get; set; } 63 | 64 | /// 65 | /// Gets or sets last refresh date time 66 | /// 67 | [JsonProperty("LastRefreshDateTime")] 68 | public DateTime LastRefreshDateTime { get; set; } 69 | 70 | /// 71 | /// Gets or sets refresh frequency in hours 72 | /// 73 | [JsonProperty("RefreshFrequencyInHours")] 74 | public int RefreshFrequencyInHours { get; set; } 75 | 76 | /// 77 | /// Gets or sets SharePoint list id 78 | /// 79 | [JsonProperty("SharePointListId")] 80 | public string SharePointListId { get; set; } 81 | } 82 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/RefreshTokenResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Refresh Token Response. 11 | /// 12 | public class RefreshTokenResponse 13 | { 14 | /// 15 | /// Gets or sets Token Type. 16 | /// 17 | [JsonProperty("token_type")] 18 | public string TokenType { get; set; } 19 | 20 | /// 21 | /// Gets or sets scope. 22 | /// 23 | [JsonProperty("scope")] 24 | public string Scope { get; set; } 25 | 26 | /// 27 | /// Gets or sets Expires In. 28 | /// 29 | [JsonProperty("expires_in")] 30 | public string ExpiresIn { get; set; } 31 | 32 | /// 33 | /// Gets or sets Ext Expires In. 34 | /// 35 | [JsonProperty("ext_expires_in")] 36 | public string ExtExpiresIn { get; set; } 37 | 38 | /// 39 | /// Gets or sets Access Token. 40 | /// 41 | [JsonProperty("access_token")] 42 | public string AccessToken { get; set; } 43 | 44 | /// 45 | /// Gets or sets Refresh Token. 46 | /// 47 | [JsonProperty("refresh_token")] 48 | public string RefreshToken { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/StorageInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | /// 8 | /// References to storage tables. 9 | /// 10 | public class StorageInfo 11 | { 12 | /// 13 | /// Config Table 14 | /// 15 | public static readonly string ConfigTableName = "Config"; 16 | 17 | /// 18 | /// KB Info Table 19 | /// 20 | public static readonly string KBInfoTableName = "KBInfo"; 21 | 22 | /// 23 | /// Token Table 24 | /// 25 | public static readonly string TokenTableName = "Token"; 26 | 27 | /// 28 | /// Blob Container 29 | /// 30 | public static readonly string BlobContainerName = "list-search-blob"; 31 | } 32 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/TaskContinueResult.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Task Continue Result 11 | /// 12 | public class TaskContinueResult 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// Task info for task continue result. 18 | public TaskContinueResult(TaskInfo taskInfo) 19 | { 20 | this.TaskInfo = taskInfo; 21 | this.Type = ComposeExtensionResultType.TaskContinue; 22 | } 23 | 24 | /// 25 | /// Gets or sets 26 | /// 27 | [JsonProperty("value")] 28 | public TaskInfo TaskInfo { get; set; } 29 | 30 | /// 31 | /// Gets or sets Type of task 32 | /// 33 | [JsonProperty("type")] 34 | public string Type { get; set; } 35 | } 36 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/TaskInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | using Microsoft.Bot.Connector; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Represents Task Info for task module 12 | /// 13 | public class TaskInfo 14 | { 15 | /// 16 | /// Gets or sets the url 17 | /// 18 | [JsonProperty("url")] 19 | public string Url { get; set; } 20 | 21 | /// 22 | /// Gets or sets the card 23 | /// 24 | [JsonProperty("card")] 25 | public Attachment Card { get; set; } 26 | 27 | /// 28 | /// Gets or sets the title 29 | /// 30 | [JsonProperty("title")] 31 | public string Title { get; set; } 32 | 33 | /// 34 | /// Gets or sets the height 35 | /// 36 | [JsonProperty("height")] 37 | public object Height { get; set; } 38 | 39 | /// 40 | /// Gets or sets the width 41 | /// 42 | [JsonProperty("width")] 43 | public object Width { get; set; } 44 | 45 | /// 46 | /// Gets or sets the fallback url 47 | /// 48 | [JsonProperty("fallbackUrl")] 49 | public string FallbackUrl { get; set; } 50 | 51 | /// 52 | /// Gets or sets the completion bot id 53 | /// 54 | [JsonProperty("completionBotId")] 55 | public string CompletionBotId { get; set; } 56 | 57 | /// 58 | /// Get Json Serialized object. 59 | /// 60 | /// Serialized Json string 61 | public string ToJson() 62 | { 63 | return JsonConvert.SerializeObject(this); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/TaskSubmitResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.ListSearch.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Represents Task Envelope for task module 11 | /// 12 | public class TaskSubmitResponse 13 | { 14 | /// 15 | /// Gets or sets Task 16 | /// 17 | [JsonProperty("task")] 18 | public TaskContinueResult Task { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/TokenEntity.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | using Microsoft.WindowsAzure.Storage.Table; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Represents Token entity in storage. 12 | /// 13 | public class TokenEntity : TableEntity // TODO: remove this and change to key vault. 14 | { 15 | /// 16 | /// Gets or sets access token 17 | /// 18 | [JsonProperty("AccessToken")] 19 | public string AccessToken { get; set; } 20 | 21 | /// 22 | /// Gets or sets refresh token 23 | /// 24 | [JsonProperty("RefreshToken")] 25 | public string RefreshToken { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Models/TokenTypes.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ListSearch.Models 6 | { 7 | /// 8 | /// Token Types 9 | /// 10 | public class TokenTypes 11 | { 12 | /// 13 | /// Token of type Graph. 14 | /// 15 | public static readonly string GraphTokenType = "Graph"; 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.ListSearch")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.ListSearch")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("25420fd9-d399-4f1c-be15-938a37bbe9d2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Resources/Strings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Microsoft.Teams.Apps.ListSearch.Resources { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Strings { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Strings() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Teams.Apps.ListSearch.Resources.Strings", typeof(Strings).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Sorry, something unexpected happened. Please try again.. 65 | /// 66 | internal static string ErrorGenericMessage { 67 | get { 68 | return ResourceManager.GetString("ErrorGenericMessage", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Error. 74 | /// 75 | internal static string ErrorGenericTitle { 76 | get { 77 | return ResourceManager.GetString("ErrorGenericTitle", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Your search session has expired. Please relaunch the app and try your search again.. 83 | /// 84 | internal static string ErrorSessionExpiredMessage { 85 | get { 86 | return ResourceManager.GetString("ErrorSessionExpiredMessage", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Session expired. 92 | /// 93 | internal static string ErrorSessionExpiredTitle { 94 | get { 95 | return ResourceManager.GetString("ErrorSessionExpiredTitle", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to You do not have access to view this page.. 101 | /// 102 | internal static string ErrorUnauthorizedMessage { 103 | get { 104 | return ResourceManager.GetString("ErrorUnauthorizedMessage", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to Unauthorized. 110 | /// 111 | internal static string ErrorUnauthorizedTitle { 112 | get { 113 | return ResourceManager.GetString("ErrorUnauthorizedTitle", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to Find information and share with your team. 119 | /// 120 | internal static string MessagingExtensionTitle { 121 | get { 122 | return ResourceManager.GetString("MessagingExtensionTitle", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to Open. 128 | /// 129 | internal static string ResultCardButtonTitle { 130 | get { 131 | return ResourceManager.GetString("ResultCardButtonTitle", resourceCulture); 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | preprocessor,pre-processor 5 | shortlived,short-lived 6 | 7 | 8 | 9 | 10 | 11 | 12 | \.g\.cs$ 13 | \.generated\.cs$ 14 | \.g\.i\.cs$ 15 | TemporaryGeneratedFile_.*\.cs$ 16 | AssemblyInfo.*\.cs$ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | False 27 | 28 | 29 | 30 | 31 | 32 | as 33 | do 34 | id 35 | if 36 | in 37 | ip 38 | is 39 | mx 40 | my 41 | no 42 | on 43 | to 44 | ui 45 | vs 46 | x 47 | y 48 | z 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | False 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Error/ErrorPartial.cshtml: -------------------------------------------------------------------------------- 1 | 
2 | @ViewBag.Message 3 |
-------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Error/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = null; 3 | } 4 | 5 | 6 | 7 | 8 | @ViewBag.Title 9 | @Styles.Render("~/Content/css") 10 | 11 | 12 |
13 | @ViewBag.Message 14 |
15 | 16 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Search/ResultCard.cshtml: -------------------------------------------------------------------------------- 1 | @model Lib.Models.SelectedSearchResult 2 | @{ 3 | Layout = null; 4 | } 5 | @Styles.Render("~/Content/css") 6 | @{ 7 | Layout = null; 8 | } 9 | 10 | 11 | 12 | 13 | 14 | 15 | ResultCard 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 |
24 | 25 |
26 | 27 | @if (Model != null) 28 | { 29 |
30 |

@Model.KBName

31 |
32 |
33 | @Model.Question 34 | @foreach (Lib.Models.DeserializedAnswer answer in Model.Answers) 35 | { 36 | 37 | } 38 |
39 | } 40 |
41 |
42 | @{ 43 | var JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings() 44 | { 45 | ContractResolver = new ListSearch.Helpers.CamelCaseExceptDictionaryKeysResolver(), 46 | Formatting = Newtonsoft.Json.Formatting.Indented, 47 | }; 48 | 49 | string json = Newtonsoft.Json.JsonConvert.SerializeObject(Model, JsonSerializerSettings); 50 | } 51 | 52 | 53 |
54 |
55 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Search/ResultCardPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model Microsoft.Teams.Apps.ListSearch.Common.Models.SelectedSearchResult 2 | 3 |
4 |
5 |
@Model.Question
6 | @{ 7 | var JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings() 8 | { 9 | ContractResolver = new Microsoft.Teams.Apps.ListSearch.Common.Helpers.CamelCaseExceptDictionaryKeysResolver(), 10 | Formatting = Newtonsoft.Json.Formatting.None, 11 | }; 12 | var json = Newtonsoft.Json.JsonConvert.SerializeObject(Model, JsonSerializerSettings); 13 | } 14 | 15 | @foreach (Microsoft.Teams.Apps.ListSearch.Common.Models.DeserializedAnswer answer in Model.Answers) 16 | { 17 | 18 | 19 | 20 | 21 | } 22 |
@answer.Question@answer.Answer
23 |
24 |
25 | 26 | 27 | 30 |
31 |
32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Search/SearchList.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = null; 3 | } 4 | @Styles.Render("~/Content/css") 5 | 6 | 7 | 8 | 9 | 10 | SearchList 11 | 12 | 13 | 14 |
15 | 16 |
17 | 20 |
21 | @Html.Partial("SearchResults") 22 |
23 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Search/SearchResults.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | 3 | @if (Model != null && Model.Count != 0) 4 | { 5 |
    6 | @foreach (Microsoft.Teams.Apps.ListSearch.Common.Models.SelectedSearchResult item in Model) 7 | { 8 | var answers = Newtonsoft.Json.JsonConvert.SerializeObject(item.Answers); 9 |
  • 10 |
    @item.Question
    11 |
    @(string.Join(" | ", item.Answers.Select(a => a.Answer)))
    12 |
  • 13 | } 14 |
15 | } 16 | else 17 | { 18 |
19 | Sorry, no results found. Try another search term. 20 |
21 | } 22 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "provider": "cdnjs", 7 | "library": "jquery@3.4.1", 8 | "destination": "lib/jquery/", 9 | "files": [ 10 | "jquery.min.js", 11 | "jquery.js" 12 | ] 13 | }, 14 | { 15 | "provider": "cdnjs", 16 | "library": "modernizr@2.8.3", 17 | "destination": "lib/modernizr/", 18 | "files": [ 19 | "modernizr.min.js", 20 | "modernizr.js" 21 | ] 22 | }, 23 | { 24 | "provider": "unpkg", 25 | "library": "bootstrap@3.4.1", 26 | "destination": "lib/bootstrap/", 27 | "files": [ 28 | "dist/css/bootstrap-theme.css", 29 | "dist/css/bootstrap-theme.css.map", 30 | "dist/css/bootstrap-theme.min.css", 31 | "dist/css/bootstrap-theme.min.css.map", 32 | "dist/css/bootstrap.css", 33 | "dist/css/bootstrap.css.map", 34 | "dist/css/bootstrap.min.css", 35 | "dist/css/bootstrap.min.css.map", 36 | "dist/fonts/glyphicons-halflings-regular.eot", 37 | "dist/fonts/glyphicons-halflings-regular.svg", 38 | "dist/fonts/glyphicons-halflings-regular.ttf", 39 | "dist/fonts/glyphicons-halflings-regular.woff", 40 | "dist/fonts/glyphicons-halflings-regular.woff2", 41 | "dist/js/bootstrap.js", 42 | "dist/js/bootstrap.min.js" 43 | ] 44 | }, 45 | { 46 | "provider": "unpkg", 47 | "library": "@microsoft/teams-js@1.4.1", 48 | "destination": "lib/microsoft-teams/", 49 | "files": [ 50 | "dist/MicrosoftTeams.min.js", 51 | "dist/MicrosoftTeams.js", 52 | "dist/MicrosoftTeams.js.map" 53 | ] 54 | }, 55 | { 56 | "provider": "unpkg", 57 | "library": "msteams-ui-styles-core@0.8.1", 58 | "destination": "lib/msteams-ui-styles-core/", 59 | "files": [ 60 | "css/msteams-10.css", 61 | "css/msteams-14.css", 62 | "css/msteams-16.css" 63 | ] 64 | }, 65 | { 66 | "provider": "unpkg", 67 | "library": "msteams-ui-icons-core@0.4.1", 68 | "destination": "lib/msteams-ui-icons-core/", 69 | "files": [ 70 | "fonts/TeamsAssets-Light.eot", 71 | "fonts/TeamsAssets-Light.svg", 72 | "fonts/TeamsAssets-Light.ttf", 73 | "fonts/TeamsAssets-Light.woff", 74 | "fonts/TeamsAssets-Light.woff2", 75 | "fonts/TeamsAssets-Regular.eot", 76 | "fonts/TeamsAssets-Regular.svg", 77 | "fonts/TeamsAssets-Regular.ttf", 78 | "fonts/TeamsAssets-Regular.woff", 79 | "fonts/TeamsAssets-Regular.woff2" 80 | ] 81 | } 82 | ] 83 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.ListSearch/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Source/Resources.txt: -------------------------------------------------------------------------------- 1 | App Service (Contains config app) 2 | 3 | Storage Account 4 | 5 | Application Insights instance 6 | 7 | Logic app (for refresh) 8 | 9 | API connection (to sharepoint) for Logic App 10 | 11 | QnA Maker Services: 12 | Cognitive service 13 | App Service plan 14 | App Service (Contains ME Web app and bot) 15 | Search Service -------------------------------------------------------------------------------- /Source/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | 8 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 9 | "settings": { 10 | "documentationRules": { 11 | "companyName": "Microsoft" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.515" 4 | } 5 | } --------------------------------------------------------------------------------