public async Task <TResponse?> SendAsync <TResponse>(ReloadlyRequest <TResponse> request) where TResponse : class { var reqMessage = request.CreateHttpRequestMessage(); if (!_disableTelemetry) { var telemetry = TelemetryUtility.Create(_apiVersion).HttpHeaderValue(); reqMessage.Headers.TryAddWithoutValidation(TelemetryHeaderName, telemetry); } var httpClient = _httpClientFactory.CreateClient(); var resMessage = await httpClient.SendAsync(reqMessage); return(await ParseResponse(request, resMessage)); }
public SearchPageTelemetryEvent( Guid parentId, int pageIndex, int resultCount, TimeSpan duration, IEnumerable <TimeSpan> sourceTimings, TimeSpan aggregationTime, LoadingStatus loadingStatus) : base("SearchPage") { base["ParentId"] = parentId.ToString(); base["PageIndex"] = pageIndex; base["ResultCount"] = resultCount; base["Duration"] = duration.TotalSeconds; base["IndividualSourceDurations"] = TelemetryUtility.ToJsonArrayOfTimingsInSeconds(sourceTimings); base["ResultsAggregationDuration"] = aggregationTime.TotalSeconds; base["LoadingStatus"] = loadingStatus.ToString(); }
public static void VerifyTelemetryEventData(string operationId, ActionEventBase expected, TelemetryEvent actual) { Assert.Equal(operationId, actual["OperationId"].ToString()); Assert.Equal(expected.ProjectsCount, (int)actual["ProjectsCount"]); Assert.Equal(expected.PackagesCount, (int)actual["PackagesCount"]); Assert.Equal(expected.Status.ToString(), actual["Status"].ToString()); Assert.Equal(expected.StartTime, actual["StartTime"]); Assert.Equal(expected.EndTime, actual["EndTime"]); Assert.Equal(expected.Duration, (double)actual["Duration"]); TelemetryUtility.VerifyDateTimeFormat(actual["StartTime"] as string); TelemetryUtility.VerifyDateTimeFormat(actual["EndTime"] as string); for (var i = 0; i < expected.ProjectsCount; i++) { Assert.Equal(expected.ProjectIds[i], actual["ProjectId" + (i + 1)].ToString()); } }
protected override void ProcessRecordCore() { var startTime = DateTimeOffset.Now; // Set to log telemetry granular events for this install operation TelemetryService = new TelemetryServiceHelper(); // start timer for telemetry event TelemetryUtility.StartorResumeTimer(); using (var lck = _lockService.AcquireLock()) { Preprocess(); SubscribeToProgressEvents(); if (!_readFromPackagesConfig && !_readFromDirectPackagePath && _nugetVersion == null) { Task.Run(InstallPackageByIdAsync); } else { var identities = GetPackageIdentities(); Task.Run(() => InstallPackagesAsync(identities)); } WaitAndLogPackageActions(); UnsubscribeFromProgressEvents(); } // stop timer for telemetry event and create action telemetry event instance TelemetryUtility.StopTimer(); var actionTelemetryEvent = TelemetryUtility.GetActionTelemetryEvent( new[] { Project }, NuGetOperationType.Install, OperationSource.PMC, startTime, _status, _packageCount, TelemetryUtility.GetTimerElapsedTimeInSeconds()); // emit telemetry event along with granular level events ActionsTelemetryService.Instance.EmitActionEvent(actionTelemetryEvent, TelemetryService.TelemetryEvents); }
public static async Task <ProjectTelemetryEvent> GetProjectTelemetryEventAsync(NuGetProject nuGetProject) { if (nuGetProject == null) { throw new ArgumentNullException(nameof(nuGetProject)); } string projectUniqueName = string.Empty; ProjectTelemetryEvent returnValue = null; try { // Get the project details. projectUniqueName = nuGetProject.GetMetadata <string>(NuGetProjectMetadataKeys.UniqueName); string projectId = nuGetProject.GetMetadata <string>(NuGetProjectMetadataKeys.ProjectId); NuGetProjectType projectType = GetProjectType(nuGetProject); bool isUpgradable = await NuGetProjectUpgradeUtility.IsNuGetProjectUpgradeableAsync(nuGetProject); string fullPath = nuGetProject.GetMetadata <string>(NuGetProjectMetadataKeys.FullPath); returnValue = new ProjectTelemetryEvent( NuGetVersion.Value, projectId, projectType, isUpgradable, fullPath); } catch (Exception ex) { // ArgumentException means project metadata is empty // DTE exceptions could mean VS process has a severe failure string message = $"Failed to emit project information for project '{projectUniqueName}'. Exception:" + Environment.NewLine + ex.ToString(); ActivityLog.LogWarning(ExceptionHelper.LogEntrySource, message); Debug.Fail(message); await TelemetryUtility.PostFaultAsync(ex, nameof(VSTelemetryServiceUtility), nameof(GetProjectTelemetryEventAsync)); } return(returnValue); }
protected override void ProcessRecordCore() { var startTime = DateTimeOffset.Now; _packageCount = 1; // Enable granular level events for this uninstall operation TelemetryService = new TelemetryServiceHelper(); TelemetryUtility.StartorResumeTimer(); // Run Preprocess outside of JTF Preprocess(); NuGetUIThreadHelper.JoinableTaskFactory.Run(async() => { await _lockService.ExecuteNuGetOperationAsync(() => { SubscribeToProgressEvents(); Task.Run(UninstallPackageAsync); WaitAndLogPackageActions(); UnsubscribeFromProgressEvents(); return(Task.FromResult(true)); }, Token); }); TelemetryUtility.StopTimer(); var actionTelemetryEvent = TelemetryUtility.GetActionTelemetryEvent( new[] { Project }, NuGetOperationType.Uninstall, OperationSource.PMC, startTime, _status, _packageCount, TelemetryUtility.GetTimerElapsedTimeInSeconds()); // emit telemetry event with granular level events ActionsTelemetryService.Instance.EmitActionEvent(actionTelemetryEvent, TelemetryService.TelemetryEvents); }
private void ShowManageLibraryPackageForSolutionDialog(object sender, EventArgs e) { NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async delegate { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (ShouldMEFBeInitialized()) { await InitializeMEFAsync(); } var windowFrame = await FindExistingSolutionWindowFrameAsync(); if (windowFrame == null) { // Create the window frame windowFrame = await CreateDocWindowForSolutionAsync(); } if (windowFrame != null) { // process search string string parameterString = null; var args = e as OleMenuCmdEventArgs; if (args != null) { parameterString = args.InValue as string; } var searchText = GetSearchText(parameterString); Search(windowFrame, searchText); windowFrame.Show(); } }).FileAndForget( TelemetryUtility.CreateFileAndForgetEventName( nameof(NuGetPackage), nameof(ShowManageLibraryPackageForSolutionDialog))); }
private void StartAsyncDefaultProjectUpdate() { Assumes.Present(_solutionManager.Value); NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async() => { await TaskScheduler.Default; NuGetProject project = await _solutionManager.Value.GetDefaultNuGetProjectAsync(); var oldValue = DefaultProject; string newValue; if (oldValue == null && project == null) { return; } else if (project == null) { newValue = null; } else { newValue = await GetDisplayNameAsync(project); } bool isInvalidationRequired = oldValue != newValue; if (isInvalidationRequired) { DefaultProject = newValue; await CommandUiUtilities.InvalidateDefaultProjectAsync(); } }) .FileAndForget(TelemetryUtility.CreateFileAndForgetEventName(nameof(PowerShellHost), nameof(StartAsyncDefaultProjectUpdate))); }
public void CreateFileAndForgetEventName_WhenArgumentsAreValid_ReturnsString() { string actualResult = TelemetryUtility.CreateFileAndForgetEventName("a", "b"); Assert.Equal("VS/NuGet/fileandforget/a/b", actualResult); }
public void IsHttpV3_WhenSourceIsNull_Throws() { var exception = Assert.Throws <ArgumentNullException>(() => TelemetryUtility.IsHttpV3(source: null)); Assert.Equal("source", exception.ParamName); }
public void ToJsonArrayOfTimingsInSeconds_WithOneValue_ReturnsTimingsInSeconds() { TimeSpan[] values = new[] { new TimeSpan(hours: 0, minutes: 0, seconds: 5) }; TelemetryUtility.ToJsonArrayOfTimingsInSeconds(values).Should().Be("[5]"); }
private void EmitRestoreTelemetryEvent(IEnumerable <NuGetProject> projects, bool forceRestore, RestoreOperationSource source, DateTimeOffset startTime, double duration, PackageSourceTelemetry.Totals protocolDiagnosticTotals, IntervalTracker intervalTimingTracker) { var sortedProjects = projects.OrderBy( project => project.GetMetadata <string>(NuGetProjectMetadataKeys.UniqueName)); var projectIds = sortedProjects.Select( project => project.GetMetadata <string>(NuGetProjectMetadataKeys.ProjectId)).ToArray(); var projectDictionary = sortedProjects .GroupBy(x => x.ProjectStyle) .ToDictionary(x => x.Key, y => y.Count()); var packageSourceMapping = PackageSourceMapping.GetPackageSourceMapping(_settings); bool isPackageSourceMappingEnabled = packageSourceMapping?.IsEnabled ?? false; var packageSources = _sourceRepositoryProvider.PackageSourceProvider.LoadPackageSources().ToList(); int NumHTTPFeeds = 0; int NumLocalFeeds = 0; bool hasVSOfflineFeed = false; bool hasNuGetOrg = false; foreach (var packageSource in packageSources) { if (packageSource.IsEnabled) { if (packageSource.IsHttp) { NumHTTPFeeds++; hasNuGetOrg |= TelemetryUtility.IsNuGetOrg(packageSource.Source); } else { hasVSOfflineFeed |= TelemetryUtility.IsVsOfflineFeed(packageSource); NumLocalFeeds++; } } } var restoreTelemetryEvent = new RestoreTelemetryEvent( _nuGetProjectContext.OperationId.ToString(), projectIds, forceRestore, source, startTime, _status, packageCount: _packageCount, noOpProjectsCount: _noOpProjectsCount, upToDateProjectsCount: _upToDateProjectCount, unknownProjectsCount: projectDictionary.GetValueOrDefault(ProjectStyle.Unknown, 0), // appears in DependencyGraphRestoreUtility projectJsonProjectsCount: projectDictionary.GetValueOrDefault(ProjectStyle.ProjectJson, 0), packageReferenceProjectsCount: projectDictionary.GetValueOrDefault(ProjectStyle.PackageReference, 0), legacyPackageReferenceProjectsCount: sortedProjects.Where(x => x.ProjectStyle == ProjectStyle.PackageReference && x is LegacyPackageReferenceProject).Count(), cpsPackageReferenceProjectsCount: sortedProjects.Where(x => x.ProjectStyle == ProjectStyle.PackageReference && x is CpsPackageReferenceProject).Count(), dotnetCliToolProjectsCount: projectDictionary.GetValueOrDefault(ProjectStyle.DotnetCliTool, 0), // appears in DependencyGraphRestoreUtility packagesConfigProjectsCount: projectDictionary.GetValueOrDefault(ProjectStyle.PackagesConfig, 0), DateTimeOffset.Now, duration, _trackingData, intervalTimingTracker, isPackageSourceMappingEnabled, NumHTTPFeeds, NumLocalFeeds, hasNuGetOrg, hasVSOfflineFeed); TelemetryActivity.EmitTelemetryEvent(restoreTelemetryEvent); var sourceEvent = SourceTelemetry.GetRestoreSourceSummaryEvent(_nuGetProjectContext.OperationId, packageSources, protocolDiagnosticTotals); TelemetryActivity.EmitTelemetryEvent(sourceEvent); }
public void ToJsonArrayOfTimingsInSeconds_WithMultipleValues_AppendsValuesWithComma() { TimeSpan[] values = new[] { new TimeSpan(hours: 0, minutes: 0, seconds: 5), new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 0, milliseconds: 500) }; TelemetryUtility.ToJsonArrayOfTimingsInSeconds(values).Should().Be("[5,60.5]"); }
/// <summary> /// This is where the nominate calls for the IVs1 and IVS3 APIs combine. The reason for this method is to avoid duplication and potential issues /// The issue with this method is that it has some weird custom logging to ensure backward compatibility. It's on the implementer to ensure these calls are correct. /// <param name="projectUniqueName">projectUniqueName</param> /// <param name="projectRestoreInfo">projectRestoreInfo. Can be null</param> /// <param name="projectRestoreInfo2">proectRestoreInfo2. Can be null</param> /// <param name="token"></param> /// <remarks>Exactly one of projectRestoreInfos has to null.</remarks> /// <returns>The task that scheduled restore</returns> private Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, IVsProjectRestoreInfo2 projectRestoreInfo2, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null && projectRestoreInfo2 == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo != null && projectRestoreInfo2 != null) { throw new ArgumentException($"Internal error: Both {nameof(projectRestoreInfo)} and {nameof(projectRestoreInfo2)} cannot have values. Please file an issue at NuGet/Home if you see this exception."); } if (projectRestoreInfo != null) { if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } else { if (projectRestoreInfo2.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); DependencyGraphSpec dgSpec; IReadOnlyList <IAssetsLogMessage> nominationErrors = null; try { dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo, projectRestoreInfo2); } catch (Exception e) { var restoreLogMessage = RestoreLogMessage.CreateError(NuGetLogCode.NU1105, string.Format(Resources.NU1105, projectNames.ShortName, e.Message)); restoreLogMessage.LibraryId = projectUniqueName; nominationErrors = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(restoreLogMessage) }; var projectDirectory = Path.GetDirectoryName(projectUniqueName); string projectIntermediatePath = projectRestoreInfo == null ? projectRestoreInfo2.BaseIntermediatePath : projectRestoreInfo.BaseIntermediatePath; var dgSpecOutputPath = GetProjectOutputPath(projectDirectory, projectIntermediatePath); dgSpec = CreateMinimalDependencyGraphSpec(projectUniqueName, dgSpecOutputPath); } _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec, nominationErrors); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (OperationCanceledException) { throw; } catch (Exception e) { _logger.LogError(e.ToString()); TelemetryUtility.EmitException(nameof(VsSolutionRestoreService), nameof(NominateProjectAsync), e); return(Task.FromResult(false)); } }
/// <summary> /// Create a SourceSummaryEvent event with counts of local vs http and v2 vs v3 feeds. /// </summary> private static TelemetryEvent GetSourceSummaryEvent( string eventName, Guid parentId, IEnumerable <PackageSource> packageSources, PackageSourceTelemetry.Totals protocolDiagnosticTotals) { var local = 0; var httpV2 = 0; var httpV3 = 0; var nugetOrg = HttpStyle.NotPresent; var vsOfflinePackages = false; var dotnetCuratedFeed = false; if (packageSources != null) { foreach (var source in packageSources) { // Ignore disabled sources if (source.IsEnabled) { if (source.IsHttp) { if (TelemetryUtility.IsHttpV3(source)) { // Http V3 feed httpV3++; if (TelemetryUtility.IsNuGetOrg(source.Source)) { nugetOrg |= HttpStyle.YesV3; } } else { // Http V2 feed httpV2++; if (TelemetryUtility.IsNuGetOrg(source.Source)) { if (source.Source.IndexOf( "api/v2/curated-feeds/microsoftdotnet", StringComparison.OrdinalIgnoreCase) >= 0) { dotnetCuratedFeed = true; } else { nugetOrg |= HttpStyle.YesV2; } } } } else { // Local or UNC feed local++; if (TelemetryUtility.IsVsOfflineFeed(source)) { vsOfflinePackages = true; } } } } } return(new SourceSummaryTelemetryEvent( eventName, parentId, local, httpV2, httpV3, nugetOrg.ToString(), vsOfflinePackages, dotnetCuratedFeed, protocolDiagnosticTotals)); }
private void Run(Func <Task> action, [CallerMemberName] string methodName = null) { NuGetUIThreadHelper.JoinableTaskFactory .RunAsync(() => _semaphore.ExecuteAsync(action)) .FileAndForget(TelemetryUtility.CreateFileAndForgetEventName(nameof(OutputConsoleLogger), methodName)); }
public static async Task RunAsync([QueueTrigger("actions")] ProvisioningActionModel action, TextWriter log) { var startProvisioning = DateTime.Now; String provisioningEnvironment = ConfigurationManager.AppSettings["SPPA:ProvisioningEnvironment"]; log.WriteLine($"Processing queue trigger function for tenant {action.TenantId}"); log.WriteLine($"PnP Correlation ID: {action.CorrelationId.ToString()}"); // Instantiate and use the telemetry model TelemetryUtility telemetry = new TelemetryUtility((s) => { log.WriteLine(s); }); Dictionary <string, string> telemetryProperties = new Dictionary <string, string>(); // Configure telemetry properties // telemetryProperties.Add("UserPrincipalName", action.UserPrincipalName); telemetryProperties.Add("TenantId", action.TenantId); telemetryProperties.Add("PnPCorrelationId", action.CorrelationId.ToString()); telemetryProperties.Add("TargetSiteAlreadyExists", action.TargetSiteAlreadyExists.ToString()); telemetryProperties.Add("TargetSiteBaseTemplateId", action.TargetSiteBaseTemplateId); // Get a reference to the data context ProvisioningAppDBContext dbContext = new ProvisioningAppDBContext(); try { // Log telemetry event telemetry?.LogEvent("ProvisioningFunction.Start"); if (CheckIfActionIsAlreadyRunning(action, dbContext)) { throw new ConcurrentProvisioningException("The requested package is currently provisioning in the selected target tenant and cannot be applied in parallel. Please wait for the previous provisioning action to complete."); } var tokenId = $"{action.TenantId}-{action.UserPrincipalName.ToLower().GetHashCode()}-{action.ActionType.ToString().ToLower()}-{provisioningEnvironment}"; // Retrieve the SPO target tenant via Microsoft Graph var graphAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, "https://graph.microsoft.com/", ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"], ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"], ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]); log.WriteLine($"Retrieved target Microsoft Graph Access Token."); if (!String.IsNullOrEmpty(graphAccessToken)) { #region Get current context data (User, SPO Tenant, SPO Access Token) // Get the currently connected user name and email (UPN) var jwtAccessToken = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(graphAccessToken); String delegatedUPN = String.Empty; var upnClaim = jwtAccessToken.Claims.FirstOrDefault(c => c.Type == "upn"); if (upnClaim != null && !String.IsNullOrEmpty(upnClaim.Value)) { delegatedUPN = upnClaim.Value; } String delegatedUserName = String.Empty; var nameClaim = jwtAccessToken.Claims.FirstOrDefault(c => c.Type == "name"); if (nameClaim != null && !String.IsNullOrEmpty(nameClaim.Value)) { delegatedUserName = nameClaim.Value; } // Determine the URL of the root SPO site for the current tenant var rootSiteJson = HttpHelper.MakeGetRequestForString("https://graph.microsoft.com/v1.0/sites/root", graphAccessToken); SharePointSite rootSite = JsonConvert.DeserializeObject <SharePointSite>(rootSiteJson); String spoTenant = rootSite.WebUrl; log.WriteLine($"Target SharePoint Online Tenant: {spoTenant}"); // Configure telemetry properties telemetryProperties.Add("SPOTenant", spoTenant); // Retrieve the SPO Access Token var spoAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, rootSite.WebUrl, ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"], ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"], ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]); log.WriteLine($"Retrieved target SharePoint Online Access Token."); #endregion // Connect to SPO, create and provision site AuthenticationManager authManager = new AuthenticationManager(); using (ClientContext context = authManager.GetAzureADAccessTokenAuthenticatedContext(spoTenant, spoAccessToken)) { // Telemetry and startup var web = context.Web; context.ClientTag = $"SPDev:ProvisioningPortal-{provisioningEnvironment}"; context.Load(web, w => w.Title, w => w.Id); await context.ExecuteQueryAsync(); // Save the current SPO Correlation ID telemetryProperties.Add("SPOCorrelationId", context.TraceCorrelationId); log.WriteLine($"SharePoint Online Root Site Collection title: {web.Title}"); #region Store the main site URL in KeyVault // Store the main site URL in the vault var vault = ProvisioningAppManager.SecurityTokensServiceProvider; // Read any existing properties for the current tenantId var properties = await vault.GetAsync(tokenId); if (properties == null) { // If there are no properties, create a new dictionary properties = new Dictionary <String, String>(); } // Set/Update the RefreshToken value properties["SPORootSite"] = spoTenant; // Add or Update the Key Vault accordingly await vault.AddOrUpdateAsync(tokenId, properties); #endregion #region Provision the package var package = dbContext.Packages.FirstOrDefault(p => p.Id == new Guid(action.PackageId)); if (package != null) { // Update the Popularity of the package package.TimesApplied++; dbContext.SaveChanges(); #region Get the Provisioning Hierarchy file // Determine reference path variables var blobConnectionString = ConfigurationManager.AppSettings["BlobTemplatesProvider:ConnectionString"]; var blobContainerName = ConfigurationManager.AppSettings["BlobTemplatesProvider:ContainerName"]; var packageFileName = package.PackageUrl.Substring(package.PackageUrl.LastIndexOf('/') + 1); var packageFileUri = new Uri(package.PackageUrl); var packageFileRelativePath = packageFileUri.AbsolutePath.Substring(2 + blobContainerName.Length); var packageFileRelativeFolder = packageFileRelativePath.Substring(0, packageFileRelativePath.LastIndexOf('/')); // Configure telemetry properties telemetryProperties.Add("PackageFileName", packageFileName); telemetryProperties.Add("PackageFileUri", packageFileUri.ToString()); // Read the main provisioning file from the Blob Storage CloudStorageAccount csa; if (!CloudStorageAccount.TryParse(blobConnectionString, out csa)) { throw new ArgumentException("Cannot create cloud storage account from given connection string."); } CloudBlobClient blobClient = csa.CreateCloudBlobClient(); CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName); var blockBlob = blobContainer.GetBlockBlobReference(packageFileRelativePath); // Crate an in-memory copy of the source stream MemoryStream mem = new MemoryStream(); await blockBlob.DownloadToStreamAsync(mem); mem.Position = 0; // Prepare the output hierarchy ProvisioningHierarchy hierarchy = null; if (packageFileName.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase)) { // That's an XML Provisioning Template file XDocument xml = XDocument.Load(mem); mem.Position = 0; // Deserialize the stream into a provisioning hierarchy reading any // dependecy with the Azure Blob Storage connector var formatter = XMLPnPSchemaFormatter.GetSpecificFormatter(xml.Root.Name.NamespaceName); var templateLocalFolder = $"{blobContainerName}/{packageFileRelativeFolder}"; var provider = new XMLAzureStorageTemplateProvider( blobConnectionString, templateLocalFolder); formatter.Initialize(provider); // Get the full hierarchy hierarchy = ((IProvisioningHierarchyFormatter)formatter).ToProvisioningHierarchy(mem); hierarchy.Connector = provider.Connector; } else if (packageFileName.EndsWith(".pnp", StringComparison.InvariantCultureIgnoreCase)) { // That's a PnP Package file // Get a provider based on the in-memory .PNP Open XML file OpenXMLConnector openXmlConnector = new OpenXMLConnector(mem); XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( openXmlConnector); // Get the .xml provisioning template file name var xmlTemplateFileName = openXmlConnector.Info?.Properties?.TemplateFileName ?? packageFileName.Substring(packageFileName.LastIndexOf('/') + 1) .ToLower().Replace(".pnp", ".xml"); // Get the full hierarchy hierarchy = provider.GetHierarchy(xmlTemplateFileName); hierarchy.Connector = provider.Connector; } #endregion #region Apply the template // Prepare variable to collect provisioned sites var provisionedSites = new List <Tuple <String, String> >(); // If we have a hierarchy with at least one Sequence if (hierarchy != null) // && hierarchy.Sequences != null && hierarchy.Sequences.Count > 0) { Console.WriteLine($"Provisioning hierarchy \"{hierarchy.DisplayName}\""); var tenantUrl = UrlUtilities.GetTenantAdministrationUrl(context.Url); // Retrieve the SPO Access Token var spoAdminAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, tenantUrl, ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"], ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"], ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]); log.WriteLine($"Retrieved target SharePoint Online Admin Center Access Token."); using (var tenantContext = authManager.GetAzureADAccessTokenAuthenticatedContext(tenantUrl, spoAdminAccessToken)) { using (var pnpTenantContext = PnPClientContext.ConvertFrom(tenantContext)) { var tenant = new Microsoft.Online.SharePoint.TenantAdministration.Tenant(pnpTenantContext); // Prepare a dictionary to hold the access tokens var accessTokens = new Dictionary <String, String>(); // Prepare logging for hierarchy application var ptai = new ProvisioningTemplateApplyingInformation(); ptai.MessagesDelegate += delegate(string message, ProvisioningMessageType messageType) { log.WriteLine($"{messageType} - {message}"); }; ptai.ProgressDelegate += delegate(string message, int step, int total) { log.WriteLine($"{step:00}/{total:00} - {message}"); }; ptai.SiteProvisionedDelegate += delegate(string title, string url) { log.WriteLine($"Fully provisioned site '{title}' with URL: {url}"); var provisionedSite = new Tuple <string, string>(title, url); if (!provisionedSites.Contains(provisionedSite)) { provisionedSites.Add(provisionedSite); } }; //#if !DEBUG // // Set the default delay for sites creations to 5 mins // ptai.DelayAfterModernSiteCreation = 60 * 5; //#endif // Configure the OAuth Access Tokens for the client context accessTokens.Add(new Uri(tenantUrl).Authority, spoAdminAccessToken); accessTokens.Add(new Uri(spoTenant).Authority, spoAccessToken); // Configure the OAuth Access Tokens for the PnPClientContext, too pnpTenantContext.PropertyBag["AccessTokens"] = accessTokens; ptai.AccessTokens = accessTokens; #region Theme handling // Process the graphical Theme if (action.ApplyTheme) { // If we don't have any custom Theme if (!action.ApplyCustomTheme) { // Associate the selected already existing Theme to all the sites of the hierarchy foreach (var sc in hierarchy.Sequences[0].SiteCollections) { sc.Theme = action.SelectedTheme; foreach (var s in sc.Sites) { UpdateChildrenSitesTheme(s, action.SelectedTheme); } } } } #endregion // Configure provisioning parameters if (action.PackageProperties != null) { foreach (var key in action.PackageProperties.Keys) { if (hierarchy.Parameters.ContainsKey(key.ToString())) { hierarchy.Parameters[key.ToString()] = action.PackageProperties[key].ToString(); } else { hierarchy.Parameters.Add(key.ToString(), action.PackageProperties[key].ToString()); } // Configure telemetry properties telemetryProperties.Add($"PackageProperty.{key}", action.PackageProperties[key].ToString()); } } // Log telemetry event telemetry?.LogEvent("ProvisioningFunction.BeginProvisioning", telemetryProperties); // Define a PnPProvisioningContext scope to share the security context across calls using (var pnpProvisioningContext = new PnPProvisioningContext(async(r, s) => { if (accessTokens.ContainsKey(r)) { // In this scenario we just use the dictionary of access tokens // in fact the overall operation for sure will take less than 1 hour return(await Task.FromResult(accessTokens[r])); } else { // Try to get a fresh new Access Token var token = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync( tokenId, $"https://{r}", ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"], ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"], ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]); accessTokens.Add(r, token); return(token); } })) { // Configure the webhooks, if any if (action.Webhooks != null && action.Webhooks.Count > 0) { foreach (var t in hierarchy.Templates) { foreach (var wh in action.Webhooks) { AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ProvisioningTemplateStarted); AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningStarted); AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningCompleted); AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ProvisioningTemplateCompleted); AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ExceptionOccurred); } } foreach (var wh in action.Webhooks) { AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningStarted); AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningCompleted); AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningExceptionOccurred); } } // Apply the hierarchy log.WriteLine($"Hierarchy Provisioning Started: {DateTime.Now:hh.mm.ss}"); tenant.ApplyProvisionHierarchy(hierarchy, (hierarchy.Sequences != null && hierarchy.Sequences.Count > 0) ? hierarchy.Sequences[0].ID : null, ptai); log.WriteLine($"Hierarchy Provisioning Completed: {DateTime.Now:hh.mm.ss}"); } if (action.ApplyTheme && action.ApplyCustomTheme) { if (!String.IsNullOrEmpty(action.ThemePrimaryColor) && !String.IsNullOrEmpty(action.ThemeBodyTextColor) && !String.IsNullOrEmpty(action.ThemeBodyBackgroundColor)) { log.WriteLine($"Applying custom Theme to provisioned sites"); #region Palette generation for Theme var jsonPalette = ThemeUtility.GetThemeAsJSON( action.ThemePrimaryColor, action.ThemeBodyTextColor, action.ThemeBodyBackgroundColor); #endregion // Apply the custom theme to all of the provisioned sites foreach (var ps in provisionedSites) { using (var provisionedSiteContext = authManager.GetAzureADAccessTokenAuthenticatedContext(ps.Item2, spoAccessToken)) { if (provisionedSiteContext.Web.ApplyTheme(jsonPalette)) { log.WriteLine($"Custom Theme applied on site '{ps.Item1}' with URL: {ps.Item2}"); } else { log.WriteLine($"Failed to apply custom Theme on site '{ps.Item1}' with URL: {ps.Item2}"); } } } } } // Log telemetry event telemetry?.LogEvent("ProvisioningFunction.EndProvisioning", telemetryProperties); // Notify user about the provisioning outcome if (!String.IsNullOrEmpty(action.NotificationEmail)) { var appOnlyAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAppOnlyAccessTokenAsync( "https://graph.microsoft.com/", ConfigurationManager.AppSettings["OfficeDevPnP:TenantId"], ConfigurationManager.AppSettings["OfficeDevPnP:ClientId"], ConfigurationManager.AppSettings["OfficeDevPnP:ClientSecret"], ConfigurationManager.AppSettings["OfficeDevPnP:AppUrl"]); MailHandler.SendMailNotification( "ProvisioningCompleted", action.NotificationEmail, null, new { TemplateName = action.DisplayName, ProvisionedSites = provisionedSites, }, appOnlyAccessToken); } // Log reporting event (1 = Success) LogReporting(action, provisioningEnvironment, startProvisioning, package, 1); } } } else { throw new ApplicationException($"The requested package does not contain a valid PnP Hierarchy!"); } #endregion } else { throw new ApplicationException($"Cannot find the package with ID: {action.PackageId}"); } #endregion #region Process any children items // If there are children items if (action.ChildrenItems != null && action.ChildrenItems.Count > 0) { // Prepare any further child provisioning request action.PackageId = action.ChildrenItems[0].PackageId; action.PackageProperties = action.ChildrenItems[0].Parameters; action.ChildrenItems.RemoveAt(0); // Enqueue any further child provisioning request await ProvisioningAppManager.EnqueueProvisioningRequest(action); } #endregion log.WriteLine($"Function successfully executed!"); // Log telemetry event telemetry?.LogEvent("ProvisioningFunction.End", telemetryProperties); } } else { var noTokensErrorMessage = $"Cannot retrieve Refresh Token or Access Token for {action.CorrelationId} in tenant {action.TenantId}!"; log.WriteLine(noTokensErrorMessage); throw new ApplicationException(noTokensErrorMessage); } } catch (Exception ex) { // Skip logging exception for Recycled Site if (ex is RecycledSiteException) { // rather log an event telemetry?.LogEvent("ProvisioningFunction.RecycledSite", telemetryProperties); // Log reporting event (3 = RecycledSite) LogReporting(action, provisioningEnvironment, startProvisioning, null, 3, ex.ToDetailedString()); } // Skip logging exception for Concurrent Provisioning else if (ex is ConcurrentProvisioningException) { // rather log an event telemetry?.LogEvent("ProvisioningFunction.ConcurrentProvisioning", telemetryProperties); // Log reporting event (4 = ConcurrentProvisioningException) LogReporting(action, provisioningEnvironment, startProvisioning, null, 4, ex.ToDetailedString()); } else { // Log telemetry event telemetry?.LogException(ex, "ProvisioningFunction.RunAsync", telemetryProperties); // Log reporting event (2 = Failed) LogReporting(action, provisioningEnvironment, startProvisioning, null, 2, ex.ToDetailedString()); } if (!String.IsNullOrEmpty(action.NotificationEmail)) { var appOnlyAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAppOnlyAccessTokenAsync( "https://graph.microsoft.com/", ConfigurationManager.AppSettings["OfficeDevPnP:TenantId"], ConfigurationManager.AppSettings["OfficeDevPnP:ClientId"], ConfigurationManager.AppSettings["OfficeDevPnP:ClientSecret"], ConfigurationManager.AppSettings["OfficeDevPnP:AppUrl"]); // Notify user about the provisioning outcome MailHandler.SendMailNotification( "ProvisioningFailed", action.NotificationEmail, null, new { TemplateName = action.DisplayName, ExceptionDetails = SimplifyException(ex), PnPCorrelationId = action.CorrelationId.ToString(), }, appOnlyAccessToken); } ProcessWebhooksExceptionNotification(action, ex); // Track the failure in the local action log MarkCurrentActionItemAsFailed(action, dbContext); throw ex; } finally { // Try to cleanup the pending action item, if any CleanupCurrentActionItem(action, dbContext); telemetry?.Flush(); } }
public void ToJsonArrayOfTimingsInSeconds_WithNullArgument_ReturnsEmptyString() { TelemetryUtility.ToJsonArrayOfTimingsInSeconds(null).Should().Be(string.Empty); }
public void CreateFileAndForgetEventName_WhenMemberNameIsNullOrEmpty_Throws(string memberName) { ArgumentException exception = Assert.Throws <ArgumentException>(() => TelemetryUtility.CreateFileAndForgetEventName("typeName", memberName)); Assert.Equal("memberName", exception.ParamName); }
/// <summary> /// Install package by Id /// </summary> /// <param name="project"></param> /// <param name="packageId"></param> /// <param name="resolutionContext"></param> /// <param name="projectContext"></param> /// <param name="isPreview"></param> /// <param name="isForce"></param> /// <param name="uninstallContext"></param> /// <returns></returns> protected async Task InstallPackageByIdAsync(NuGetProject project, string packageId, ResolutionContext resolutionContext, INuGetProjectContext projectContext, bool isPreview) { try { var actions = await PackageManager.PreviewInstallPackageAsync(project, packageId, resolutionContext, projectContext, PrimarySourceRepositories, null, CancellationToken.None); if (!actions.Any()) { // nuget operation status is set to NoOp to log under telemetry event when // there is no preview action. _status = NuGetOperationStatus.NoOp; } else { // update packages count to be logged under telemetry event _packageCount = actions.Select( action => action.PackageIdentity.Id).Distinct().Count(); } // stop telemetry event timer to avoid UI interaction TelemetryUtility.StopTimer(); if (!ShouldContinueDueToDotnetDeprecation(actions, isPreview)) { // resume telemetry event timer after ui confirmation TelemetryUtility.StartorResumeTimer(); return; } // resume telemetry event timer after ui confirmation TelemetryUtility.StartorResumeTimer(); if (isPreview) { PreviewNuGetPackageActions(actions); } else { var identity = actions.Select(v => v.PackageIdentity).Where(p => p.Id.Equals(packageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); NuGetPackageManager.SetDirectInstall(identity, projectContext); await PackageManager.ExecuteNuGetProjectActionsAsync(project, actions, this, resolutionContext.SourceCacheContext, CancellationToken.None); NuGetPackageManager.ClearDirectInstall(projectContext); // Refresh Manager UI if needed RefreshUI(actions); } } catch (InvalidOperationException ex) { if (ex.InnerException is PackageAlreadyInstalledException) { // Set nuget operation status to NoOp for telemetry event when package // is already installed. _status = NuGetOperationStatus.NoOp; Log(MessageLevel.Info, ex.Message); } else { throw ex; } } }
private async Task RestoreAsync(bool forceRestore, RestoreOperationSource restoreSource, CancellationToken token) { var startTime = DateTimeOffset.Now; _status = NuGetOperationStatus.NoOp; // start timer for telemetry event TelemetryUtility.StartorResumeTimer(); var projects = Enumerable.Empty <NuGetProject>(); _packageRestoreManager.PackageRestoredEvent += PackageRestoreManager_PackageRestored; _packageRestoreManager.PackageRestoreFailedEvent += PackageRestoreManager_PackageRestoreFailedEvent; try { var solutionDirectory = _solutionManager.SolutionDirectory; var isSolutionAvailable = _solutionManager.IsSolutionAvailable; if (solutionDirectory == null) { await _logger.DoAsync((l, _) => { _status = NuGetOperationStatus.Failed; l.ShowError(Resources.SolutionIsNotSaved); l.WriteLine(VerbosityLevel.Minimal, Resources.SolutionIsNotSaved); }); return; } // Check if solution has deferred projects var deferredProjectsData = new DeferredProjectRestoreData(new Dictionary <PackageReference, List <string> >(), new List <PackageSpec>()); if (await _solutionManager.SolutionHasDeferredProjectsAsync()) { var deferredProjectsPath = await _solutionManager.GetDeferredProjectsFilePathAsync(); deferredProjectsData = await DeferredProjectRestoreUtility.GetDeferredProjectsData(_deferredWorkspaceService, deferredProjectsPath, token); } // Get the projects from the SolutionManager // Note that projects that are not supported by NuGet, will not show up in this list projects = _solutionManager.GetNuGetProjects(); // Check if there are any projects that are not INuGetIntegratedProject, that is, // projects with packages.config. OR // any of the deferred project is type of packages.config, If so, perform package restore on them if (projects.Any(project => !(project is INuGetIntegratedProject)) || deferredProjectsData.PackageReferenceDict.Count > 0) { await RestorePackagesOrCheckForMissingPackagesAsync( solutionDirectory, isSolutionAvailable, deferredProjectsData.PackageReferenceDict, token); } var dependencyGraphProjects = projects .OfType <IDependencyGraphProject>() .ToList(); await RestorePackageSpecProjectsAsync( dependencyGraphProjects, forceRestore, isSolutionAvailable, deferredProjectsData.PackageSpecs, token); // TODO: To limit risk, we only publish the event when there is a cross-platform PackageReference // project in the solution. Extending this behavior to all solutions is tracked here: // https://github.com/NuGet/Home/issues/4478 #if !VS14 if (projects.OfType <CpsPackageReferenceProject>().Any() && !string.IsNullOrEmpty(_dependencyGraphProjectCacheHash)) { // A no-op restore is considered successful. A cancellation is considered unsuccessful. var args = new SolutionRestoredEventArgs( isSuccess: _status == NuGetOperationStatus.Succeeded || _status == NuGetOperationStatus.NoOp, solutionSpecHash: _dependencyGraphProjectCacheHash); _restoreEventsPublisher.OnSolutionRestoreCompleted(args); } #endif } finally { _packageRestoreManager.PackageRestoredEvent -= PackageRestoreManager_PackageRestored; _packageRestoreManager.PackageRestoreFailedEvent -= PackageRestoreManager_PackageRestoreFailedEvent; TelemetryUtility.StopTimer(); var duration = TelemetryUtility.GetTimerElapsedTime(); await _logger.WriteSummaryAsync(_status, duration); // Emit telemetry event for restore operation EmitRestoreTelemetryEvent( projects, restoreSource, startTime, _status, _packageCount, duration.TotalSeconds); } }
private async Task RestoreAsync(bool forceRestore, RestoreOperationSource restoreSource, CancellationToken token) { var startTime = DateTimeOffset.Now; _status = NuGetOperationStatus.NoOp; // start timer for telemetry event TelemetryUtility.StartorResumeTimer(); var projects = Enumerable.Empty <NuGetProject>(); _packageRestoreManager.PackageRestoredEvent += PackageRestoreManager_PackageRestored; _packageRestoreManager.PackageRestoreFailedEvent += PackageRestoreManager_PackageRestoreFailedEvent; try { var solutionDirectory = _solutionManager.SolutionDirectory; var isSolutionAvailable = await _solutionManager.IsSolutionAvailableAsync(); if (solutionDirectory == null) { await _logger.DoAsync((l, _) => { _status = NuGetOperationStatus.Failed; l.ShowError(Resources.SolutionIsNotSaved); l.WriteLine(VerbosityLevel.Minimal, Resources.SolutionIsNotSaved); }); return; } // Get the projects from the SolutionManager // Note that projects that are not supported by NuGet, will not show up in this list projects = await _solutionManager.GetNuGetProjectsAsync(); // Check if there are any projects that are not INuGetIntegratedProject, that is, // projects with packages.config. OR // any of the deferred project is type of packages.config, If so, perform package restore on them if (projects.Any(project => !(project is INuGetIntegratedProject))) { await RestorePackagesOrCheckForMissingPackagesAsync( solutionDirectory, isSolutionAvailable, restoreSource, token); } var dependencyGraphProjects = projects .OfType <IDependencyGraphProject>() .ToList(); await RestorePackageSpecProjectsAsync( dependencyGraphProjects, forceRestore, isSolutionAvailable, restoreSource, token); #if !VS14 // TODO: To limit risk, we only publish the event when there is a cross-platform PackageReference // project in the solution. Extending this behavior to all solutions is tracked here: // NuGet/Home#4478 if (projects.OfType <NetCorePackageReferenceProject>().Any()) { _restoreEventsPublisher.OnSolutionRestoreCompleted( new SolutionRestoredEventArgs(_status, solutionDirectory)); } #endif } finally { _packageRestoreManager.PackageRestoredEvent -= PackageRestoreManager_PackageRestored; _packageRestoreManager.PackageRestoreFailedEvent -= PackageRestoreManager_PackageRestoreFailedEvent; TelemetryUtility.StopTimer(); var duration = TelemetryUtility.GetTimerElapsedTime(); // Do not log any restore message if user disabled restore. if (_packageRestoreConsent.IsGranted) { await _logger.WriteSummaryAsync(_status, duration); } else { _logger.LogDebug(Resources.PackageRefNotRestoredBecauseOfNoConsent); } // Emit telemetry event for restore operation EmitRestoreTelemetryEvent( projects, restoreSource, startTime, duration.TotalSeconds); } }
public void ToJsonArrayOfTimingsInSeconds_WithEmptyArray_ReturnsEmptyString() { TelemetryUtility.ToJsonArrayOfTimingsInSeconds(Enumerable.Empty <TimeSpan>()).Should().Be(string.Empty); }