/// <summary> /// Initializes a new instance of the <see cref="SarifWorkItemFiler"> class.</see> /// </summary> /// <param name="filingUri"> /// The uri to the remote filing host. /// </param> /// <param name="filingContext"> /// A starting context object that configures the work item filing operation. In the /// current implementation, this context is copied for each SARIF file (if any) split /// from the input log and then further elaborated upon. /// </param> public SarifWorkItemFiler(Uri filingUri = null, SarifWorkItemContext filingContext = null) { this.FilingContext = filingContext ?? new SarifWorkItemContext { HostUri = filingUri }; filingUri = filingUri ?? this.FilingContext.HostUri; if (filingUri == null) { throw new ArgumentNullException(nameof(filingUri)); } ; if (filingUri != this.FilingContext.HostUri) { // Inconsistent URIs were provided in 'filingContext' and 'filingUri'; arguments. throw new InvalidOperationException(WorkItemsResources.InconsistentHostUrisProvided); } this.FilingClient = FilingClientFactory.Create(this.FilingContext.HostUri); this.Logger = ServiceProviderFactory.ServiceProvider.GetService <ILogger>(); Assembly.GetExecutingAssembly().LogIdentity(); this.FiledWorkItems = new List <WorkItemModel>(); }
public static string CreateWorkItemDescription(this SarifLog log, SarifWorkItemContext context, IList <Uri> locationUris) { int totalResults = log.GetAggregateFilableResultsCount(); List <string> toolNames = log.GetToolNames(); string phrasedToolNames = toolNames.ToAndPhrase(); string multipleToolsFooter = toolNames.Count > 1 ? WorkItemsResources.MultipleToolsFooter : string.Empty; Uri runRepositoryUri = log?.Runs.FirstOrDefault()?.VersionControlProvenance?.FirstOrDefault().RepositoryUri; Uri detectionLocationUri = !string.IsNullOrEmpty(runRepositoryUri?.OriginalString) ? runRepositoryUri : locationUris?[0]; string detectionLocation = (detectionLocationUri?.IsAbsoluteUri == true && detectionLocationUri?.Scheme == "https") ? context.CreateLinkText(detectionLocationUri.OriginalString, detectionLocationUri?.OriginalString) : detectionLocationUri?.OriginalString; if (locationUris?.Count > 1) { int additionalLocations = locationUris.Count - 1; detectionLocation = $"{detectionLocation} (+{additionalLocations} locations)"; } // This work item contains {0} {1} issue(s) detected in {2}{3}. Click the 'Scans' tab to review results. string description = string.Format( WorkItemsResources.WorkItemBodyTemplateText, // This work item contains totalResults, // {0} phrasedToolNames, // {1} issue(s) detected in detectionLocation, // {2} multipleToolsFooter); // {3}. Click the 'Scans' tab to review results.\r\n return(description); }
public void WorkItemFilingContext_IncludeCustomBugFooter() { var context = new SarifWorkItemContext(); string customBugFooter = "This text is important to all my bugs."; context.AzureDevOpsDescriptionFooter = customBugFooter; var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context); workItemModel.BodyOrDescription.Should().Contain(customBugFooter); }
public void WorkItemFiler_PerResultSplitStrategyPartitionsProperly() { SarifLog sarifLog = TestData.CreateSimpleLog(); SarifWorkItemContext context = CreateAzureDevOpsTestContext(); context.SplittingStrategy = SplittingStrategy.PerResult; int numberOfResults = sarifLog.Runs.Sum(run => run.Results.Count); context.SetProperty(ExpectedWorkItemsCount, numberOfResults); TestWorkItemFiler(sarifLog, context, true); }
public void SarifWorkItemModel_PopulatesDescription() { var context = new SarifWorkItemContext(); SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); var workItemModel = new SarifWorkItemModel(sarifLog, context); workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty(); workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName)); workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString); workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.GeneralFooterText); workItemModel.BodyOrDescription.Should().NotContain(WorkItemsResources.AdoViewingOptions); }
public void SarifWorkItemModel_PopulatesAdoDescription() { // Context object specifies ADO as a filing client provider by default var context = new SarifWorkItemContext(); SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); var workItemModel = new SarifWorkItemModel(sarifLog, context); workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty(); workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName)); workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString); workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.AzureDevOpsDefaultDescriptionFooter); workItemModel.BodyOrDescription.Should().NotContain(WorkItemsResources.GitHubDefaultDescriptionFooter); }
public void SarifWorkItemModel_IncorporatesMultipleToolNamesIntoAdoDescription() { var context = new SarifWorkItemContext(); context.CurrentProvider = FilingClient.SourceControlProvider.AzureDevOps; SarifLog sarifLog = TestData.CreateTwoRunThreeResultLog(); var workItemModel = new SarifWorkItemModel(sarifLog, context); workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty(); workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName)); workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.SecondTestToolName)); workItemModel.BodyOrDescription.Should().Contain(TestData.FileLocations.Location1); }
public void WorkItemFilingContext_NullSarifLogRaisesArgumentNullException() { var areaPathTransformer = new AreaPathFromUri(); var context = new SarifWorkItemContext(); context.AddWorkItemModelTransformer(areaPathTransformer); context.Transformers[0].GetType().Should().Be(areaPathTransformer.GetType()); var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context); context.Transformers[0].Transform(workItemModel); workItemModel.Area.Should().BeNull(); }
public void SarifWorkItemModel_PopulatesAdoDescription() { var context = new SarifWorkItemContext(); context.CurrentProvider = FilingClient.SourceControlProvider.AzureDevOps; SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); var workItemModel = new SarifWorkItemModel(sarifLog, context); workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty(); workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName)); workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString); workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.ViewScansTabResults); }
public void WorkItemFiler_PerRunSplitStrategyPartitionsProperlyGithub() { SarifWorkItemContext context = GitHubTestContext; SarifLog sarifLog = TestData.CreateSimpleLog(); // Our default splitting strategy is PerRun, that is, one // work item (and corresponding attachment) should be filed // for each run in the log file. int numberOfRuns = sarifLog.Runs.Count; context.SetProperty(ExpectedWorkItemsCount, numberOfRuns); TestWorkItemFiler(sarifLog, context, false); }
public void WorkItemFilingContext_FetchUriSuccessfully() { var areaPathTransformer = new AreaPathFromUri(); var context = new SarifWorkItemContext(); context.AddWorkItemModelTransformer(areaPathTransformer); context.Transformers[0].GetType().Should().Be(areaPathTransformer.GetType()); SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); var workItemModel = new SarifWorkItemModel(sarifLog, context); context.Transformers[0].Transform(workItemModel); workItemModel.Area.Should().Be(TestData.FileLocations.Location1); }
public void SarifWorkItemExtensions_GetAggregateResultCount_ComputeResultCountsFromLogs() { var context = new SarifWorkItemContext(); SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); int resultCount = sarifLog.GetAggregateFilableResultsCount(false); resultCount.Should().Be(1); sarifLog = TestData.CreateTwoRunThreeResultLog(); resultCount = sarifLog.GetAggregateFilableResultsCount(false); resultCount.Should().Be(3); sarifLog = TestData.CreateEmptyRun(); resultCount = sarifLog.GetAggregateFilableResultsCount(false); resultCount.Should().Be(0); }
private SarifWorkItemContext RoundTripThroughXml(SarifWorkItemContext sarifWorkItemContext) { string temp = Path.GetTempFileName(); try { sarifWorkItemContext.SaveToXml(temp); sarifWorkItemContext = new SarifWorkItemContext(); sarifWorkItemContext.LoadFromXml(temp); } finally { if (File.Exists(temp)) { File.Delete(temp); } } return(sarifWorkItemContext); }
private static Mock <SarifWorkItemFiler> CreateMockSarifWorkItemFiler(SarifWorkItemContext context = null) { context = context ?? GitHubTestContext; var mockFiler = new Mock <SarifWorkItemFiler>(context.HostUri, context); mockFiler .Setup(x => x.FileWorkItems(It.IsAny <string>())) .CallBase(); mockFiler .Setup(x => x.FileWorkItems(It.IsAny <SarifLog>())) .CallBase(); mockFiler .Setup(x => x.FileWorkItems(It.IsAny <Uri>())) .CallBase(); return(mockFiler); }
public void SarifWorkItemExtensions_GetRunToolNames_FetchesAllRunToolNames() { var context = new SarifWorkItemContext(); SarifLog sarifLog = TestData.CreateOneIdThreeLocations(); List <string> toolNames = sarifLog.GetToolNames(); toolNames.Count.Should().Be(1); toolNames.Should().Contain(TestData.TestToolName); sarifLog = TestData.CreateTwoRunThreeResultLog(); toolNames = sarifLog.GetToolNames(); toolNames.Count.Should().Be(2); toolNames.Should().Contain(TestData.TestToolName); toolNames.Should().Contain(TestData.SecondTestToolName); sarifLog = TestData.CreateEmptyRun(); toolNames = sarifLog.GetToolNames(); toolNames.Count.Should().Be(0); }
public void WorkItemFilingContext_RoundTripsWorkItemModelTransformer() { var munger = new Munger(); var context = new SarifWorkItemContext(); context.AddWorkItemModelTransformer(munger); context.Transformers[0].GetType().Should().Be(munger.GetType()); context = RoundTripThroughXml(context); context.Transformers[0].GetType().Should().Be(munger.GetType()); string newAreaPath = Guid.NewGuid().ToString(); context.SetProperty(Munger.NewAreaPath, newAreaPath); var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context); context.Transformers[0].Transform(workItemModel); workItemModel.Area.Should().Be(newAreaPath); }
internal static void FileWorkItemsHelper(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient) { // The helper below will initialize the sarif work item model with a copy // of the root pipeline filing context. This context will then be initialized // based on the current sarif log file that we're processing. SarifWorkItemModel workItemModel = new SarifWorkItemModel(sarifLog, filingContext); try { // Populate the work item with the target organization/repository information. // In ADO, certain fields (such as the area path) will defaut to the // project name and so this information is used in at least that context. workItemModel.RepositoryOrProject = filingClient.ProjectOrRepository; workItemModel.OwnerOrAccount = filingClient.AccountOrOrganization; foreach (SarifWorkItemModelTransformer transformer in workItemModel.Context.Transformers) { transformer.Transform(workItemModel); } filingClient.FileWorkItems(new[] { workItemModel }).Wait(); // TODO: We need to process updated work item models to persist filing // details back to the input SARIF file, if that was specified. // This code should either return or persist the updated models // via a property, so that the file work items command can do // this work. // // https://github.com/microsoft/sarif-sdk/issues/1774 } catch (Exception ex) { Console.Error.WriteLine(ex); } }
public SarifWorkItemContext(SarifWorkItemContext initializer) : base(initializer) { }
private void TestWorkItemFiler(SarifLog sarifLog, SarifWorkItemContext context) { // ONE. Create test data that the low-level ADO client mocks // will flow back to the SARIF work item filer. var attachmentReference = new AttachmentReference() { Id = Guid.NewGuid(), Url = Guid.NewGuid().ToString() }; var workItem = new WorkItem { Id = DateTime.UtcNow.Millisecond, Links = new ReferenceLinks() }; // The fake URI to the filed item that we'll expect the filer to receive string bugUriText = "https://example.com/" + Guid.NewGuid().ToString(); string bugHtmlUriText = "https://example.com/" + Guid.NewGuid().ToString(); Uri bugUri = new Uri(bugUriText, UriKind.RelativeOrAbsolute); Uri bugHtmlUri = new Uri(bugHtmlUriText, UriKind.RelativeOrAbsolute); workItem.Url = bugUriText; workItem.Links.AddLink("html", bugHtmlUriText); // TWO. Define variables to capture whether we enter all expected ADO client methods. bool connectCalled = false; int createWorkItemCalled = 0, createAttachmentCalled = 0; // THREE. Create a default mock SARIF filer and client, configured by an AzureDevOps context // (which creates a default ADO filing client underneath). SarifWorkItemFiler filer = CreateMockSarifWorkItemFiler(context).Object; var workItemTrackingHttpClientMock = new Mock <IWorkItemTrackingHttpClient>(); workItemTrackingHttpClientMock .Setup(x => x.CreateAttachmentAsync(It.IsAny <MemoryStream>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <object>(), It.IsAny <CancellationToken>())) .ReturnsAsync(attachmentReference) // Return our test attachment, defined above. .Callback <MemoryStream, string, string, string, object, CancellationToken>( (stream, fileName, uploadType, areaPath, userState, cancellationToken) => { // Verify that the ADO client receives the request to create an attachment createAttachmentCalled++; }); workItemTrackingHttpClientMock .Setup(x => x.CreateWorkItemAsync(It.IsAny <JsonPatchDocument>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool?>(), It.IsAny <bool?>(), It.IsAny <bool?>(), It.IsAny <object>(), It.IsAny <CancellationToken>())) .ReturnsAsync(workItem) // Return our test work item, defined above .Callback <JsonPatchDocument, string, string, bool?, bool?, bool?, object, CancellationToken>( (document, project, type, validateOnly, bypassRules, suppressNotifications, userState, cancellationToken) => { // Verify that the ADO client receives the request to file the bug createWorkItemCalled++; }); // FOUR. Create a mock VssConnection instance, which handles the auth connection // and retrieval of the ADO filing client. We are required to put this // mock behind an interface due to an inability var vssConnectionMock = new Mock <IVssConnection>(); vssConnectionMock .Setup(x => x.ConnectAsync(It.IsAny <Uri>(), It.IsAny <string>())) .Returns(Task.CompletedTask) .Callback <Uri, string>( (uri, pat) => { // The following data values flow to us via test constants which are // captured in the default context object that initialized the filer. pat.Should().Be(TestData.NotActuallyASecret); // We configure the filer with a full ADO URI that contains the account and // project, e.g., https://dev.azure.com/myaccount/myproject. By the time the // ADO client receives it, however, the project has been stripped off // (because it is not required for making the connection). AzureDevOpsFilingUri.OriginalString.StartsWith(uri.ToString()).Should().BeTrue(); // Verify that we received the connection request. connectCalled = true; }); // Our GetClientAsync is overridden to provide our low-level ADO mock. vssConnectionMock .Setup(x => x.GetClientAsync()) .ReturnsAsync(workItemTrackingHttpClientMock.Object); // FIVE. We are required to inject the low level ADO connection wrapper. We are // required to do so due to the complexity of creating and initializing // required objects. MOQ cannot override constructors and (related) // in general cannot instantiate a type without a parameterless ctor. // Even when a concrete class can be instantiated, its various properties // might not be easily stubbed (as for a non-virtual property accessor). // For these cases, we need to insert an interface in our system, and // create wrappers around those concrete types, where the interface is // directly mapped or otherwise adapted to the concrete type's methods. // Moq can then simply manufacture a class that implements that interface // in order to control behaviors. // // Rather than introducing a type factory or more sophisticated pattern // of injection, we use the very simple expedient of declaring an internal // property to hold a mock instance. In production, if that property is null, // the system instantiates a wrapper around the standard types for use. var adoFilingClient = (AzureDevOpsFilingClient)filer.FilingClient; adoFilingClient._vssConection = vssConnectionMock.Object; string sarifLogText = JsonConvert.SerializeObject(sarifLog); SarifLog updatedSarifLog = filer.FileWorkItems(sarifLogText); // Did we see all the execution we expected? connectCalled.Should().BeTrue(); int expectedWorkItemsCount = context.GetProperty(ExpectedWorkItemsCount); createWorkItemCalled.Should().Be(expectedWorkItemsCount); createAttachmentCalled.Should().Be(expectedWorkItemsCount); // This property is a naive mechanism to ensure that the code // executed comprehensively (i.e., that execution was not limited // due to unhandled exceptions). This is required because we have // not really implemented a proper async API with appropriate handling // for exceptions and other negative conditions. I wouldn't expect this // little helper to survive but it closes the loop for the current // rudimentary in-flight implementation. filer.FilingResult.Should().Be(FilingResult.Succeeded); filer.FiledWorkItems.Count.Should().Be(expectedWorkItemsCount); foreach (WorkItemModel filedWorkItem in filer.FiledWorkItems) { // Finally, make sure that our test data flows back properly through the filer. filedWorkItem.Attachment.Should().NotBeNull(); JsonConvert.SerializeObject(filedWorkItem.Attachment.Text).Should().NotBeNull(); filedWorkItem.Uri.Should().Be(bugUri); filedWorkItem.HtmlUri.Should().Be(bugHtmlUri); } // Validate that we updated the SARIF log with work itme URIs. // updatedSarifLog.Should().NotBeEquivalentTo(sarifLog); foreach (Run run in updatedSarifLog.Runs) { foreach (Result result in run.Results) { result.WorkItemUris.Should().NotBeNull(); result.WorkItemUris.Count.Should().Be(1); result.WorkItemUris[0].Should().Be(bugHtmlUri); result.TryGetProperty(SarifWorkItemFiler.PROGRAMMABLE_URIS_PROPERTY_NAME, out List <Uri> programmableUris) .Should().BeTrue(); programmableUris.Should().NotBeNull(); programmableUris.Count.Should().Be(1); programmableUris[0].Should().Be(bugUri); } } }
private void FileWorkItemsHelper(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient) { string logGuid = sarifLog.GetProperty<Guid>("guid").ToString(); // The helper below will initialize the sarif work item model with a copy // of the root pipeline filing context. This context will then be initialized // based on the current sarif log file that we're processing. // First intializes the contexts provider to the value in the current filing client. filingContext.CurrentProvider = filingClient.Provider; var sarifWorkItemModel = new SarifWorkItemModel(sarifLog, filingContext); try { // Populate the work item with the target organization/repository information. // In ADO, certain fields (such as the area path) will defaut to the // project name and so this information is used in at least that context. sarifWorkItemModel.OwnerOrAccount = filingClient.AccountOrOrganization; sarifWorkItemModel.RepositoryOrProject = filingClient.ProjectOrRepository; foreach (SarifWorkItemModelTransformer transformer in sarifWorkItemModel.Context.Transformers) { SarifWorkItemModel updatedSarifWorkItemModel = transformer.Transform(sarifWorkItemModel); // If a transformer has set the model to null, that indicates // it should be pulled from the work flow (i.e., not filed). if (updatedSarifWorkItemModel == null) { Dictionary<string, object> customDimentions = new Dictionary<string, object>(); customDimentions.Add("TransformerType", transformer.GetType().FullName); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Canceled, customDimentions); return; } sarifWorkItemModel = updatedSarifWorkItemModel; } Task<IEnumerable<WorkItemModel>> task = filingClient.FileWorkItems(new[] { sarifWorkItemModel }); task.Wait(); this.FiledWorkItems.AddRange(task.Result); // IMPORTANT: as we update our partitioned logs, we are actually modifying the input log file // as well. That's because our partitioning is configured to reuse references to existing // run and result objects, even though they are partitioned into a separate log file. // This approach also us to update the original log file with the filed work item details // without requiring us to build a map of results between the original log and its // partioned log files. // UpdateLogWithWorkItemDetails(sarifLog, sarifWorkItemModel.HtmlUri, sarifWorkItemModel.Uri); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Succeeded); } catch (Exception ex) { this.Logger.LogError(ex, "An exception was raised filing log '{logGuid}'.", logGuid); Dictionary<string, object> customDimentions = new Dictionary<string, object>(); customDimentions.Add("ExceptionType", ex.GetType().FullName); customDimentions.Add("ExceptionMessage", ex.Message); customDimentions.Add("ExceptionStackTrace", ex.ToString()); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.ExceptionRaised, customDimentions); } }
private void TestWorkItemFiler(SarifLog sarifLog, SarifWorkItemContext context, bool adoClient) { // ONE. Create test data that the low-level ADO client mocks // will flow back to the SARIF work item filer. var attachmentReference = new AttachmentReference() { Id = Guid.NewGuid(), Url = Guid.NewGuid().ToString() }; var workItem = new WorkItem { Id = DateTime.UtcNow.Millisecond, Links = new ReferenceLinks() }; // The fake URI to the filed item that we'll expect the filer to receive string bugUriText = "https://example.com/" + Guid.NewGuid().ToString(); string bugHtmlUriText = "https://example.com/" + Guid.NewGuid().ToString(); Uri bugUri = new Uri(bugUriText, UriKind.RelativeOrAbsolute); Uri bugHtmlUri = new Uri(bugHtmlUriText, UriKind.RelativeOrAbsolute); workItem.Url = bugUriText; workItem.Links.AddLink("html", bugHtmlUriText); // TWO. Reset variables to capture whether we enter all expected client methods. ConnectCalled = false; CreateWorkItemCalled = CreateAttachmentCount = UpdateIssueCount = 0; // THREE. Create a default mock SARIF filer and client. SarifWorkItemFiler filer = CreateMockSarifWorkItemFiler(context).Object; // FOUR. Based on which client we are using (ADO or GitHub), create the correct context. // This implies created both the connection mocks and the mocks for filing, updating, and attaching work items. // We are required to put this mock behind an interface due to an inability to mock these types directly. FilingClient filingClient; if (adoClient == true) { filingClient = CreateAdoMocksAndFilingClient(attachmentReference, workItem, filer); } else { filingClient = CreateGitHubMocksAndFilingClient(bugUriText, bugHtmlUriText, filer); } string sarifLogText = JsonConvert.SerializeObject(sarifLog); SarifLog updatedSarifLog = filer.FileWorkItems(sarifLogText); // Did we see all the execution we expected? ConnectCalled.Should().BeTrue(); int expectedWorkItemsCount = context.GetProperty(ExpectedWorkItemsCount); CreateWorkItemCalled.Should().Be(expectedWorkItemsCount); CreateAttachmentCount.Should().Be(adoClient ? expectedWorkItemsCount : 0); // This property is a naive mechanism to ensure that the code // executed comprehensively (i.e., that execution was not limited // due to unhandled exceptions). This is required because we have // not really implemented a proper async API with appropriate handling // for exceptions and other negative conditions. I wouldn't expect this // little helper to survive but it closes the loop for the current // rudimentary in-flight implementation. filer.FilingResult.Should().Be(FilingResult.Succeeded); filer.FiledWorkItems.Count.Should().Be(expectedWorkItemsCount); foreach (WorkItemModel filedWorkItem in filer.FiledWorkItems) { // Finally, make sure that our test data flows back properly through the filer. filedWorkItem.Attachment.Should().NotBeNull(); JsonConvert.SerializeObject(filedWorkItem.Attachment.Text).Should().NotBeNull(); filedWorkItem.Uri.Should().Be(bugUri); filedWorkItem.HtmlUri.Should().Be(bugHtmlUri); } // Validate that we updated the SARIF log with work itme URIs. // updatedSarifLog.Should().NotBeEquivalentTo(sarifLog); foreach (Run run in updatedSarifLog.Runs) { foreach (Result result in run.Results) { result.WorkItemUris.Should().NotBeNull(); result.WorkItemUris.Count.Should().Be(1); result.WorkItemUris[0].Should().Be(bugHtmlUri); result.TryGetProperty(SarifWorkItemFiler.PROGRAMMABLE_URIS_PROPERTY_NAME, out List <Uri> programmableUris) .Should().BeTrue(); programmableUris.Should().NotBeNull(); programmableUris.Count.Should().Be(1); programmableUris[0].Should().Be(bugUri); } } }
/// <summary> /// Initializes a new instance of the <see cref="SarifWorkItemFiler"> class.</see> /// </summary> /// <param name="filingClient"> /// A client for communicating with a work item filing host (for example, GitHub or Azure DevOps). /// </param> /// <param name="filingContext"> /// A starting context object that configures the work item filing operation. In the /// current implementation, this context is copied for each SARIF file (if any) split /// from the input log and then further elaborated upon. /// </param> public SarifWorkItemFiler(SarifWorkItemContext filingContext) { this.FilingContext = filingContext ?? throw new ArgumentNullException(nameof(filingContext)); this.FilingClient = FilingClientFactory.Create(filingContext.ProjectUri); }
private static SarifWorkItemFiler CreateWorkItemFiler(SarifWorkItemContext context = null) => CreateMockSarifWorkItemFiler(context).Object;
public SarifWorkItemModel FileWorkItemInternal(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient) { using (Logger.BeginScopeContext(nameof(FileWorkItemInternal))) { string logGuid = sarifLog.GetProperty <Guid>("guid").ToString(); // The helper below will initialize the sarif work item model with a copy // of the root pipeline filing context. This context will then be initialized // based on the current sarif log file that we're processing. // First intializes the contexts provider to the value in the current filing client. filingContext.CurrentProvider = filingClient.Provider; var sarifWorkItemModel = new SarifWorkItemModel(sarifLog, filingContext); try { // Populate the work item with the target organization/repository information. // In ADO, certain fields (such as the area path) will defaut to the // project name and so this information is used in at least that context. sarifWorkItemModel.OwnerOrAccount = filingClient.AccountOrOrganization; sarifWorkItemModel.RepositoryOrProject = filingClient.ProjectOrRepository; if (filingContext.SyncWorkItemMetadata) { Task <WorkItemModel> getMetadataTask = filingClient.GetWorkItemMetadata(sarifWorkItemModel); getMetadataTask.Wait(); sarifWorkItemModel = (SarifWorkItemModel)getMetadataTask.Result; } using (Logger.BeginScopeContext("RunTransformers")) { foreach (SarifWorkItemModelTransformer transformer in sarifWorkItemModel.Context.Transformers) { SarifWorkItemModel updatedSarifWorkItemModel = transformer.Transform(sarifWorkItemModel); // If a transformer has set the model to null, that indicates // it should be pulled from the work flow (i.e., not filed). if (updatedSarifWorkItemModel == null) { Dictionary <string, object> customDimentions = new Dictionary <string, object>(); customDimentions.Add("TransformerType", transformer.GetType().FullName); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Canceled, customDimentions); return(null); } sarifWorkItemModel = updatedSarifWorkItemModel; } } Task <IEnumerable <WorkItemModel> > task = filingClient.FileWorkItems(new[] { sarifWorkItemModel }); task.Wait(); this.FiledWorkItems.AddRange(task.Result); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Succeeded); } catch (Exception ex) { this.Logger.LogError(ex, "An exception was raised filing log '{logGuid}'.", logGuid); Dictionary <string, object> customDimentions = new Dictionary <string, object>(); customDimentions.Add("ExceptionType", ex.GetType().FullName); customDimentions.Add("ExceptionMessage", ex.Message); customDimentions.Add("ExceptionStackTrace", ex.ToString()); LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.ExceptionRaised, customDimentions); } return(sarifWorkItemModel); } }