Пример #1
0
        public static async Task RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context,
            ILogger log)
        {
            var groupEntity = context.GetInput <EmployeeResourceGroupEntity>();
            var teamId      = ParseTeamIdExtension.GetTeamIdFromDeepLink(groupEntity.GroupLink);

            try
            {
                if (!context.IsReplaying)
                {
                    log.LogInformation("About to sync pair up members.");
                }

                // sync pair up recipients to Team user pair up mapping repository table.
                await context.CallActivityWithRetryAsync(
                    FunctionNames.SyncPairUpMembersActivity,
                    FunctionSettings.DefaultRetryOptions,
                    groupEntity);

                if (!context.IsReplaying)
                {
                    log.LogInformation("About to get all active pair up users.");
                }

                // get active pair-up mappings to be sent to service bus.
                var batchesToSend = await context.CallActivityWithRetryAsync <List <TeamUserMapping> >(
                    FunctionNames.GetActivePairUpUsersActivity,
                    FunctionSettings.DefaultRetryOptions,
                    groupEntity);

                if (batchesToSend != null && batchesToSend.Any())
                {
                    log.LogInformation($"About to process {batchesToSend.Count()} users for team {groupEntity.TeamId}.");

                    if (!context.IsReplaying)
                    {
                        log.LogInformation("About to send pair up batches to queue.");
                    }

                    // Send pair up user batches to queue.
                    await context.CallActivityWithRetryAsync(
                        FunctionNames.SendPairUpMatchesActivity,
                        FunctionSettings.DefaultRetryOptions,
                        (teamId, batchesToSend));
                }

                log.LogInformation($"SyncRecipientsAndSendBatchesToQueueOrchestrator successfully completed for team: {groupEntity.GroupId}!");
            }
            catch (Exception ex)
            {
                var errorMessage = $"SyncRecipientsAndSendBatchesToQueueOrchestrator failed for team: {groupEntity.TeamId}. Exception Message: {ex.Message}";
                log.LogError(ex, errorMessage);

                throw;
            }
        }
        /// <summary>
        /// Fills the AuthenticationOptions's properties with the correct values from the configuration.
        /// </summary>
        /// <param name="authenticationOptions">The AuthenticationOptions whose properties will be filled.</param>
        /// <param name="configuration">The configuration.</param>
        private static void FillAuthenticationOptionsProperties(AuthenticationOptions authenticationOptions, IConfiguration configuration)
        {
            // NOTE: This AzureAd:Instance configuration setting does not need to be
            // overridden by any deployment specific value. It can stay the default value
            // that is set in the project's configuration.
            authenticationOptions.AzureAdInstance         = configuration.GetValue <string>("AzureAd:Instance");
            authenticationOptions.AzureAdTenantId         = configuration.GetValue <string>("AzureAd:TenantId");
            authenticationOptions.AzureAdClientId         = configuration.GetValue <string>("AzureAd:ClientId");
            authenticationOptions.AzureAdApplicationIdUri = configuration.GetValue <string>("AzureAd:ApplicationIdUri");

            // NOTE: This AzureAd:ValidIssuers configuration setting does not need to be
            // overridden by any deployment specific value. It can stay the default value
            // that is set in the project's configuration.
            authenticationOptions.AzureAdValidIssuers = configuration.GetValue <string>("AzureAd:ValidIssuers");
            authenticationOptions.AdminTeamId         = ParseTeamIdExtension.GetTeamIdFromDeepLink(configuration.GetValue <string>("AdminTeamLink"));
        }
        public async Task <ActionResult <ResourceGroupResponse> > GetEmployeeResourceGroupByTeamId(string teamId, [FromQuery] string groupId)
        {
            try
            {
                var groupEntity = await this.employeeResourceGroupRepository.GetResourceGroupByTeamIdAsync(teamId);

                if (groupEntity == null)
                {
                    this.logger.LogInformation($"No record found for provided team Id: {teamId} and group Id: {groupId}.");
                    return(this.NotFound($"No record found for provided team Id : {teamId} and group Id: {groupId}."));
                }

                if (groupId != ParseTeamIdExtension.GetGroupIdFromDeepLink(groupEntity.GroupLink))
                {
                    this.logger.LogInformation($"Group Id {groupId} and team Id {teamId} must belongs to same team.");
                    return(this.BadRequest($"Group Id {groupId} and team Id {teamId} must belongs to same team."));
                }

                var resourceGroupResponse = new ResourceGroupResponse
                {
                    GroupType                = groupEntity.GroupType,
                    GroupId                  = groupEntity.GroupId,
                    GroupName                = groupEntity.GroupName,
                    GroupDescription         = groupEntity.GroupDescription,
                    GroupLink                = groupEntity.GroupLink,
                    ImageLink                = groupEntity.ImageLink,
                    Tags                     = JsonConvert.DeserializeObject <List <string> >(groupEntity.Tags),
                    Location                 = groupEntity.Location,
                    IncludeInSearchResults   = groupEntity.IncludeInSearchResults,
                    MatchingFrequency        = groupEntity.MatchingFrequency,
                    IsProfileMatchingEnabled = groupEntity.IsProfileMatchingEnabled,
                };

                return(this.Ok(resourceGroupResponse));
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, $"Error while fetching employee resource group for team id: {teamId} and group Id: {groupId}.");
                throw;
            }
        }
        public async Task <ActionResult <EmployeeResourceGroupEntity> > UpdateEmployeeResourceGroupAsync(string id, [FromBody] ResourceGroupRequest employeeResourceGroupEntity, [FromQuery] string groupId)
        {
            try
            {
                if (employeeResourceGroupEntity == null)
                {
                    this.logger.LogError("Employee resource group entity is null.");
                    return(this.BadRequest(this.localizer.GetString("ResourceGroupNullOrEmptyErrorMessage")));
                }

                var updateEntity = await this.employeeResourceGroupRepository.GetAsync(Constants.ResourceGroupTablePartitionKey, id);

                if (updateEntity == null)
                {
                    this.logger.LogError("The employee resource group entity that user is trying to update does not exist.");
                    return(this.NotFound(this.localizer.GetString("ResourceGroupNotExistsErrorMessage")));
                }
                else
                {
                    var userId = this.HttpContext.User.FindFirstValue(Constants.ClaimTypeUserId);

                    // Validating if resource group type is 'Teams', user must be member of that group and they should belongs to same tenant.
                    if (employeeResourceGroupEntity.GroupType == (int)ResourceGroupType.Teams)
                    {
                        string tenantId = ParseTeamIdExtension.GetTenantIdFromDeepLink(updateEntity.GroupLink);

                        if (!this.options.Value.AllowedTenants.Contains(tenantId))
                        {
                            this.logger.LogError($"Tenant is not valid: {tenantId}");
                            return(this.BadRequest(this.localizer.GetString("InvalidTenantErrorMessage")));
                        }

                        string teamId             = ParseTeamIdExtension.GetTeamIdFromDeepLink(updateEntity.GroupLink);
                        var    groupMembersDetail = await this.groupMembersService.GetGroupMembersAsync(groupId);

                        var groupMemberAadIds = groupMembersDetail.Select(row => row.Id).ToList();

                        if (!groupMemberAadIds.Contains(userId))
                        {
                            this.logger.LogError($"User {userId} is not a member of the team {teamId}");
                            return(this.Forbid(this.localizer.GetString("InvalidGroupMemberErrorMessage")));
                        }

                        updateEntity.TeamId = teamId;
                    }
                    else
                    {
                        updateEntity.TeamId = string.Empty;
                    }

                    updateEntity.GroupType                = employeeResourceGroupEntity.GroupType;
                    updateEntity.GroupName                = employeeResourceGroupEntity.GroupName;
                    updateEntity.GroupDescription         = employeeResourceGroupEntity.GroupDescription;
                    updateEntity.GroupLink                = employeeResourceGroupEntity.GroupLink;
                    updateEntity.ImageLink                = employeeResourceGroupEntity.ImageLink;
                    updateEntity.Tags                     = employeeResourceGroupEntity.Tags;
                    updateEntity.Location                 = employeeResourceGroupEntity.Location;
                    updateEntity.IncludeInSearchResults   = employeeResourceGroupEntity.IncludeInSearchResults;
                    updateEntity.IsProfileMatchingEnabled = employeeResourceGroupEntity.IsProfileMatchingEnabled;
                    updateEntity.MatchingFrequency        = employeeResourceGroupEntity.MatchingFrequency;
                    updateEntity.UpdatedByObjectId        = userId;
                    updateEntity.UpdatedOn                = DateTime.UtcNow;

                    await this.employeeResourceGroupRepository.InsertOrMergeAsync(updateEntity);
                }

                return(this.Ok(updateEntity));
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error while updating employee resource group entity.");
                throw;
            }
        }
        public async Task <ActionResult <EmployeeResourceGroupEntity> > CreateEmployeeResourceGroupAsync([FromBody] EmployeeResourceGroupEntity employeeResourceGroupEntity)
        {
            try
            {
                if (employeeResourceGroupEntity == null)
                {
                    this.logger.LogWarning("Employee resource group entity is null.");
                    return(this.BadRequest(this.localizer.GetString("ResourceGroupNullOrEmptyErrorMessage")));
                }

                // Storage call to get employee resource group entity if present already.
                var groupEntity = await this.employeeResourceGroupRepository.GetFilterDataByGroupLinkOrGroupNameAsync(
                    employeeResourceGroupEntity.GroupType,
                    employeeResourceGroupEntity.GroupLink,
                    employeeResourceGroupEntity.GroupName);

                if (groupEntity != null)
                {
                    this.logger.LogInformation($"Resource group entity already present with same group name {groupEntity.GroupName} or link :{groupEntity.GroupLink}.");
                    return(this.BadRequest(this.localizer.GetString("GroupAlreadyExistsErrorMessage")));
                }

                this.logger.LogInformation("Initiated call to store employee resource group entity.");
                var userId = this.HttpContext.User.FindFirstValue(Constants.ClaimTypeUserId);

                // Validating if resource group type is 'Teams', user must be member of that group and they should belongs to same tenant.
                if (employeeResourceGroupEntity.GroupType == (int)ResourceGroupType.Teams)
                {
                    string tenantId = ParseTeamIdExtension.GetTenantIdFromDeepLink(employeeResourceGroupEntity.GroupLink);

                    if (!this.options.Value.AllowedTenants.Contains(tenantId))
                    {
                        this.logger.LogError($"Tenant is not valid: {tenantId}");
                        return(this.BadRequest(this.localizer.GetString("InvalidTenantErrorMessage")));
                    }

                    string teamId             = ParseTeamIdExtension.GetTeamIdFromDeepLink(employeeResourceGroupEntity.GroupLink);
                    string groupId            = ParseTeamIdExtension.GetGroupIdFromDeepLink(employeeResourceGroupEntity.GroupLink);
                    var    groupMembersDetail = await this.groupMembersService.GetGroupMembersAsync(groupId);

                    var groupMemberAadIds = groupMembersDetail.Select(row => row.Id).ToList();

                    if (!groupMemberAadIds.Contains(userId))
                    {
                        this.logger.LogError($"User {userId} is not a member of the team {teamId}");
                        return(this.Forbid(this.localizer.GetString("InvalidGroupMemberErrorMessage")));
                    }

                    employeeResourceGroupEntity.TeamId = teamId;
                    employeeResourceGroupEntity.IsProfileMatchingEnabled = true;
                }
                else
                {
                    employeeResourceGroupEntity.IsProfileMatchingEnabled = false;
                }

                employeeResourceGroupEntity.Group             = Constants.ResourceGroupTablePartitionKey;
                employeeResourceGroupEntity.GroupId           = this.tableRowKeyGenerator.CreateNewKeyOrderingOldestToMostRecent();
                employeeResourceGroupEntity.ApprovalStatus    = (int)ApprovalStatus.PendingForApproval;
                employeeResourceGroupEntity.CreatedOn         = DateTime.UtcNow;
                employeeResourceGroupEntity.CreatedByObjectId = userId;
                employeeResourceGroupEntity.MatchingFrequency = (int)MatchingFrequency.Monthly;
                await this.employeeResourceGroupRepository.CreateOrUpdateAsync(employeeResourceGroupEntity);

                this.logger.LogInformation("Resource group - HTTP post call succeeded");

                return(this.Created(this.Request.GetDisplayUrl(), employeeResourceGroupEntity));
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error while creating new employee resource group entity.");
                throw;
            }
        }
        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name="services">IServiceCollection instance.</param>
        public void ConfigureServices(IServiceCollection services)
        {
            // Add all options set from configuration values.
            services.AddOptions <AuthenticationOptions>()
            .Configure <IConfiguration>((authenticationOptions, configuration) =>
            {
                Startup.FillAuthenticationOptionsProperties(authenticationOptions, configuration);
            });

            services.AddOptions <BotOptions>()
            .Configure <IConfiguration>((botOptions, configuration) =>
            {
                botOptions.MicrosoftAppId       = configuration.GetValue <string>("MicrosoftAppId");
                botOptions.MicrosoftAppPassword = configuration.GetValue <string>("MicrosoftAppPassword");
                botOptions.AppBaseUri           = configuration.GetValue <string>("AppBaseUri");
                botOptions.AdminTeamId          = ParseTeamIdExtension.GetTeamIdFromDeepLink(configuration.GetValue <string>("AdminTeamLink"));
                botOptions.ManifestId           = configuration.GetValue <string>("ManifestId");
            });

            services.AddOptions <BotFilterMiddlewareOptions>()
            .Configure <IConfiguration>((botFilterMiddlewareOptions, configuration) =>
            {
                botFilterMiddlewareOptions.DisableTenantFilter =
                    configuration.GetValue <bool>("DisableTenantFilter", false);
                botFilterMiddlewareOptions.AllowedTenants =
                    configuration.GetValue <string>("AllowedTenants")?.ToString().Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
                    ?.Select(p => p.Trim()).ToArray();
            });

            services.AddOptions <RepositoryOptions>()
            .Configure <IConfiguration>((repositoryOptions, configuration) =>
            {
                repositoryOptions.StorageAccountConnectionString =
                    configuration.GetValue <string>("StorageAccountConnectionString");

                // Setting this to true because the main application should ensure that all
                // tables exist.
                repositoryOptions.EnsureTableExists = true;
            });

            services.AddOptions <MessageQueueOptions>()
            .Configure <IConfiguration>((messageQueueOptions, configuration) =>
            {
                messageQueueOptions.ServiceBusConnection =
                    configuration.GetValue <string>("ServiceBusConnection");
            });

            services.AddOptions <DataQueueMessageOptions>()
            .Configure <IConfiguration>((dataQueueMessageOptions, configuration) =>
            {
                dataQueueMessageOptions.ForceCompleteMessageDelayInSeconds =
                    configuration.GetValue <double>("ForceCompleteMessageDelayInSeconds", 86400);
            });
            services.AddOptions <UserAppOptions>()
            .Configure <IConfiguration>((options, configuration) =>
            {
                options.ProactivelyInstallUserApp =
                    configuration.GetValue <bool>("ProactivelyInstallUserApp", true);

                options.UserAppExternalId =
                    configuration.GetValue <string>("UserAppExternalId", "148a66bb-e83d-425a-927d-09f4299a9274");
            });

            services.AddOptions <QnAMakerSettings>()
            .Configure <IConfiguration>((options, configuration) =>
            {
                options.ScoreThreshold =
                    configuration.GetValue <double>("ScoreThreshold", 0.5);
            });

            services.AddOptions();

            // Add localization services.
            services.AddLocalizationSettings(this.Configuration);

            // Add authentication services.
            AuthenticationOptions authenticationOptionsParameter = new AuthenticationOptions();

            Startup.FillAuthenticationOptionsProperties(authenticationOptionsParameter, this.Configuration);
            services.AddAuthentication(this.Configuration, authenticationOptionsParameter);
            services.AddControllersWithViews();

            // Setup SPA static files.
            // In production, the React files will be served from this directory.
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });

            // Add blob client.
            services.AddSingleton(sp => new BlobContainerClient(
                                      sp.GetService <IConfiguration>().GetValue <string>("StorageAccountConnectionString"),
                                      Common.Constants.BlobContainerName));

            // The bot needs an HttpClient to download and upload files.
            services.AddHttpClient();

            services.AddSingleton <BotFrameworkHttpAdapter>();

            // Add bot services.
            services.AddSingleton <ICredentialProvider, ConfigurationCredentialProvider>();
            services.AddTransient <DIConnectBotFilterMiddleware>();
            services.AddSingleton <DIConnectBotAdapter>();
            services.AddTransient <AdminTeamNotifier>();
            services.AddTransient <TeamsDataCapture>();
            services.AddTransient <TeamsFileUpload>();
            services.AddTransient <KnowledgeBaseResponse>();
            services.AddTransient <NotificationCardHelper>();
            services.AddTransient <IBot, DIConnectBot>();

            // Add repositories.
            services.AddSingleton <TeamDataRepository>();
            services.AddSingleton <EmployeeResourceGroupRepository>();
            services.AddSingleton <UserDataRepository>();
            services.AddSingleton <SentNotificationDataRepository>();
            services.AddSingleton <NotificationDataRepository>();
            services.AddSingleton <ExportDataRepository>();
            services.AddSingleton <AppConfigRepository>();
            services.AddSingleton <FeedbackDataRepository>();
            services.AddSingleton <TeamUserPairUpMappingRepository>();

            // Add service bus message queues.
            services.AddSingleton <PrepareToSendQueue>();
            services.AddSingleton <DataQueue>();
            services.AddSingleton <ExportQueue>();

            // Add draft notification preview services.
            services.AddTransient <DraftNotificationPreviewService>();

            // Add Microsoft graph services.
            services.AddScoped <IAuthenticationProvider, GraphTokenProvider>();
            services.AddScoped <IGraphServiceClient, GraphServiceClient>();
            services.AddScoped <Beta.IGraphServiceClient, Beta.GraphServiceClient>();
            services.AddScoped <IGraphServiceFactory, GraphServiceFactory>();
            services.AddScoped <IGroupMembersService>(sp => sp.GetRequiredService <IGraphServiceFactory>().GetGroupMembersService());
            services.AddScoped <IGroupsService>(sp => sp.GetRequiredService <IGraphServiceFactory>().GetGroupsService());
            services.AddScoped <IAppCatalogService>(sp => sp.GetRequiredService <IGraphServiceFactory>().GetAppCatalogService());
            IQnAMakerClient qnaMakerClient = new QnAMakerClient(new ApiKeyServiceClientCredentials(this.Configuration["QnAMakerSubscriptionKey"]))
            {
                Endpoint = this.Configuration["QnAMakerApiEndpointUrl"]
            };
            string endpointKey = Task.Run(() => qnaMakerClient.EndpointKeys.GetKeysAsync()).Result.PrimaryEndpointKey;

            services.AddSingleton <IQnAService>((provider) => new QnAService(
                                                    provider.GetRequiredService <AppConfigRepository>(),
                                                    provider.GetRequiredService <IOptionsMonitor <QnAMakerSettings> >(),
                                                    new QnAMakerRuntimeClient(new EndpointKeyServiceClientCredentials(endpointKey))
            {
                RuntimeEndpoint = this.Configuration["QnAMakerHostUrl"]
            }));
            services.AddScoped <IGroupMembersService>(sp => sp.GetRequiredService <IGraphServiceFactory>().GetGroupMembersService());

            // Add Application Insights telemetry.
            services.AddApplicationInsightsTelemetry();

            // Add miscellaneous dependencies.
            services.AddTransient <TableRowKeyGenerator>();
            services.AddTransient <AdaptiveCardCreator>();
            services.AddSingleton <IAppSettingsService, AppSettingsService>();
            services.AddSingleton <ITeamMembersService, TeamMembersService>();
            services.AddSingleton <IMemberValidationHelper, MemberValidationHelper>();

            // Add helper class.
            services.AddSingleton <UserTeamMappingsHelper>();
            services.AddSingleton <CardHelper>();

            services.Configure <MvcOptions>(options =>
            {
                options.EnableEndpointRouting = false;
            });
        }