From 004b24186bd2f0bb5a1ca2ea53648f529c88940c Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Wed, 8 Feb 2023 18:53:28 +0000 Subject: [PATCH 1/4] Add new command --- src/cli/onefuzz/api.py | 9 +++++++++ src/pytypes/onefuzztypes/requests.py | 1 + 2 files changed, 10 insertions(+) diff --git a/src/cli/onefuzz/api.py b/src/cli/onefuzz/api.py index d241284c8a..a276069cbd 100644 --- a/src/cli/onefuzz/api.py +++ b/src/cli/onefuzz/api.py @@ -859,6 +859,15 @@ def list( data=requests.NotificationSearch(container=container), ) + def get(self, notification_id: UUID_EXPANSION) -> List[models.Notification]: + """Get a notification""" + self.logger.debug("getting notification") + return self._req_model_list( + "GET", + models.Notification, + data=requests.NotificationSearch(notification_id=notification_id), + ) + class Tasks(Endpoint): """Interact with tasks""" diff --git a/src/pytypes/onefuzztypes/requests.py b/src/pytypes/onefuzztypes/requests.py index b2ceba3a44..f11f2255b7 100644 --- a/src/pytypes/onefuzztypes/requests.py +++ b/src/pytypes/onefuzztypes/requests.py @@ -52,6 +52,7 @@ class NotificationCreate(BaseRequest, NotificationConfig): class NotificationSearch(BaseRequest): container: Optional[List[Container]] + notification_id: Optional[UUID] class NotificationGet(BaseRequest): From be77d1962e4d0c27c3cadaa9119142414b83f732 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Tue, 14 Feb 2023 18:50:59 +0000 Subject: [PATCH 2/4] Enforce scriban at notification creation time --- .../Functions/Migrations/JinjaToScriban.cs | 155 +------- .../ApiService/Functions/ValidateScriban.cs | 126 +------ .../onefuzzlib/NotificationOperations.cs | 5 + .../notifications/JinjaTemplateAdapter.cs | 346 ++++++++++++++++++ .../IntegrationTests/Fakes/TestContext.cs | 5 +- .../Fakes/TestFeatureManagerSnapshot.cs | 30 ++ 6 files changed, 389 insertions(+), 278 deletions(-) create mode 100644 src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs diff --git a/src/ApiService/ApiService/Functions/Migrations/JinjaToScriban.cs b/src/ApiService/ApiService/Functions/Migrations/JinjaToScriban.cs index 1f6ac83fed..238976cab5 100644 --- a/src/ApiService/ApiService/Functions/Migrations/JinjaToScriban.cs +++ b/src/ApiService/ApiService/Functions/Migrations/JinjaToScriban.cs @@ -39,11 +39,11 @@ private async Async.Task Post(HttpRequestData req) { _log.Info($"Finding notifications to migrate"); var notifications = _context.NotificationOperations.SearchAll() - .Select(notification => { + .SelectAwait(async notification => { var (didModify, config) = notification.Config switch { TeamsTemplate => (false, notification.Config), - AdoTemplate adoTemplate => ConvertToScriban(adoTemplate), - GithubIssuesTemplate githubIssuesTemplate => ConvertToScriban(githubIssuesTemplate), + AdoTemplate adoTemplate => await JinjaTemplateAdapter.ConvertToScriban(adoTemplate), + GithubIssuesTemplate githubIssuesTemplate => await JinjaTemplateAdapter.ConvertToScriban(githubIssuesTemplate), _ => throw new NotImplementedException("Unexpected notification configuration type") }; @@ -82,153 +82,4 @@ await notifications.Select(notification => notification.NotificationId).ToListAs return await RequestHandling.Ok(req, new JinjaToScribanMigrationResponse(updatedNotificationsIds, failedNotificationIds)); } - - private static (bool didModify, AdoTemplate template) ConvertToScriban(AdoTemplate template) { - var didModify = false; - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Project)) { - didModify = true; - template = template with { - Project = JinjaTemplateAdapter.AdaptForScriban(template.Project) - }; - } - - foreach (var item in template.AdoFields) { - if (JinjaTemplateAdapter.IsJinjaTemplate(item.Value)) { - template.AdoFields[item.Key] = JinjaTemplateAdapter.AdaptForScriban(item.Value); - didModify = true; - } - } - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Type)) { - didModify = true; - template = template with { - Type = JinjaTemplateAdapter.AdaptForScriban(template.Type) - }; - } - - if (template.Comment != null && JinjaTemplateAdapter.IsJinjaTemplate(template.Comment)) { - didModify = true; - template = template with { - Comment = JinjaTemplateAdapter.AdaptForScriban(template.Comment) - }; - } - - foreach (var item in template.OnDuplicate.AdoFields) { - if (JinjaTemplateAdapter.IsJinjaTemplate(item.Value)) { - template.OnDuplicate.AdoFields[item.Key] = JinjaTemplateAdapter.AdaptForScriban(item.Value); - didModify = true; - } - } - - if (template.OnDuplicate.Comment != null && JinjaTemplateAdapter.IsJinjaTemplate(template.OnDuplicate.Comment)) { - didModify = true; - template = template with { - OnDuplicate = template.OnDuplicate with { - Comment = JinjaTemplateAdapter.AdaptForScriban(template.OnDuplicate.Comment) - } - }; - } - - return (didModify, template); - } - - private static (bool didModify, GithubIssuesTemplate template) ConvertToScriban(GithubIssuesTemplate template) { - var didModify = false; - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.UniqueSearch.str)) { - didModify = true; - template = template with { - UniqueSearch = template.UniqueSearch with { - str = JinjaTemplateAdapter.AdaptForScriban(template.UniqueSearch.str) - } - }; - } - - if (!string.IsNullOrEmpty(template.UniqueSearch.Author) && JinjaTemplateAdapter.IsJinjaTemplate(template.UniqueSearch.Author)) { - didModify = true; - template = template with { - UniqueSearch = template.UniqueSearch with { - Author = JinjaTemplateAdapter.AdaptForScriban(template.UniqueSearch.Author) - } - }; - } - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Title)) { - didModify = true; - template = template with { - Title = JinjaTemplateAdapter.AdaptForScriban(template.Title) - }; - } - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Body)) { - didModify = true; - template = template with { - Body = JinjaTemplateAdapter.AdaptForScriban(template.Body) - }; - } - - if (!string.IsNullOrEmpty(template.OnDuplicate.Comment) && JinjaTemplateAdapter.IsJinjaTemplate(template.OnDuplicate.Comment)) { - didModify = true; - template = template with { - OnDuplicate = template.OnDuplicate with { - Comment = JinjaTemplateAdapter.AdaptForScriban(template.OnDuplicate.Comment) - } - }; - } - - if (template.OnDuplicate.Labels.Any()) { - template = template with { - OnDuplicate = template.OnDuplicate with { - Labels = template.OnDuplicate.Labels.Select(label => { - if (JinjaTemplateAdapter.IsJinjaTemplate(label)) { - didModify = true; - return JinjaTemplateAdapter.AdaptForScriban(label); - } - return label; - }).ToList() - } - }; - } - - if (template.Assignees.Any()) { - template = template with { - Assignees = template.Assignees.Select(assignee => { - if (JinjaTemplateAdapter.IsJinjaTemplate(assignee)) { - didModify = true; - return JinjaTemplateAdapter.AdaptForScriban(assignee); - } - return assignee; - }).ToList() - }; - } - - if (template.Labels.Any()) { - template = template with { - Labels = template.Labels.Select(label => { - if (JinjaTemplateAdapter.IsJinjaTemplate(label)) { - didModify = true; - return JinjaTemplateAdapter.AdaptForScriban(label); - } - return label; - }).ToList() - }; - } - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Organization)) { - didModify = true; - template = template with { - Organization = JinjaTemplateAdapter.AdaptForScriban(template.Organization) - }; - } - - if (JinjaTemplateAdapter.IsJinjaTemplate(template.Repository)) { - didModify = true; - template = template with { - Repository = JinjaTemplateAdapter.AdaptForScriban(template.Repository) - }; - } - - return (didModify, template); - } } diff --git a/src/ApiService/ApiService/Functions/ValidateScriban.cs b/src/ApiService/ApiService/Functions/ValidateScriban.cs index d32f70b523..4e8b003354 100644 --- a/src/ApiService/ApiService/Functions/ValidateScriban.cs +++ b/src/ApiService/ApiService/Functions/ValidateScriban.cs @@ -17,19 +17,9 @@ private async Async.Task Post(HttpRequestData req) { return await _context.RequestHandling.NotOk(req, request.ErrorV, "ValidateTemplate"); } - var instanceUrl = _context.ServiceConfiguration.OneFuzzInstance!; - try { - var (renderer, templateRenderContext) = await GenerateTemplateRenderContext(request.OkV.Context); - - var renderedTemaplate = await renderer.Render(request.OkV.Template, new Uri(instanceUrl), strictRendering: true); + return await RequestHandling.Ok(req, await JinjaTemplateAdapter.ValidateScribanTemplate(_context, _log, request.OkV.Context, request.OkV.Template)); - var response = new TemplateValidationResponse( - renderedTemaplate, - templateRenderContext - ); - - return await RequestHandling.Ok(req, response); } catch (Exception e) { return await new RequestHandling(_log).NotOk( req, @@ -47,118 +37,4 @@ public Async.Task Run([HttpTrigger(AuthorizationLevel.Anonymou _ => throw new InvalidOperationException("Unsupported HTTP method"), }; } - - private async Async.Task<(NotificationsBase.Renderer, TemplateRenderContext)> GenerateTemplateRenderContext(TemplateRenderContext? templateRenderContext) { - if (templateRenderContext != null) { - _log.Info($"Using the request's TemplateRenderContext"); - } else { - _log.Info($"Generating TemplateRenderContext"); - } - - var targetUrl = templateRenderContext?.TargetUrl ?? new Uri("https://example.com/targetUrl"); - var inputUrl = templateRenderContext?.InputUrl ?? new Uri("https://example.com/inputUrl"); - var reportUrl = templateRenderContext?.ReportUrl ?? new Uri("https://example.com/reportUrl"); - var executable = "target.exe"; - var crashType = "some crash type"; - var crashSite = "some crash site"; - var callStack = new List() - { - "stack frame 0", - "stack frame 1" - }; - var callStackSha = "call stack sha"; - var inputSha = "input sha"; - var taskId = Guid.NewGuid(); - var jobId = Guid.NewGuid(); - var taskState = TaskState.Running; - var jobState = JobState.Enabled; - var os = Os.Linux; - var taskType = TaskType.LibfuzzerFuzz; - var duration = 100; - var project = "some project"; - var jobName = "job name"; - var buildName = "build name"; - var reportContainer = templateRenderContext?.ReportContainer ?? Container.Parse("example-container-name"); - var reportFileName = templateRenderContext?.ReportFilename ?? "example file name"; - var reproCmd = templateRenderContext?.ReproCmd ?? "onefuzz command to create a repro"; - var report = templateRenderContext?.Report ?? new Report( - inputUrl.ToString(), - null, - executable, - crashType, - crashSite, - callStack, - callStackSha, - inputSha, - null, - taskId, - jobId, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ); - - var task = new Task( - jobId, - taskId, - taskState, - os, - templateRenderContext?.Task ?? new TaskConfig( - jobId, - null, - new TaskDetails( - taskType, - duration - ) - ) - ); - - var job = new Job( - jobId, - jobState, - templateRenderContext?.Job ?? new JobConfig( - project, - jobName, - buildName, - duration, - null - ) - ); - - var renderer = await NotificationsBase.Renderer.ConstructRenderer( - _context, - reportContainer, - reportFileName, - report, - task, - job, - targetUrl, - inputUrl, - reportUrl, - scribanOnlyOverride: true - ); - - templateRenderContext ??= new TemplateRenderContext( - report, - task.Config, - job.Config, - reportUrl, - inputUrl, - targetUrl, - reportContainer, - reportFileName, - reproCmd - ); - - return (renderer, templateRenderContext); - } } diff --git a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs index 14ef941e4a..fa79284c99 100644 --- a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs @@ -91,6 +91,11 @@ public async Async.Task> Create(Container container, return OneFuzzResult.Error(ErrorCode.INVALID_REQUEST, "invalid container"); } + if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableScribanOnly) && + !await JinjaTemplateAdapter.IsValidScribanNotificationTemplate(_context, _logTracer, config)) { + return OneFuzzResult.Error(ErrorCode.INVALID_REQUEST, "The notification config is not a valid scriban template"); + } + if (replaceExisting) { var existing = this.SearchByRowKeys(new[] { container.String }); await foreach (var existingEntry in existing) { diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs b/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs index 48ef27a446..a0701845ee 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs @@ -11,4 +11,350 @@ public static string AdaptForScriban(string jinjaTemplate) { .Replace("{%", "{{") .Replace("%}", "}}"); } + + public static async Async.Task IsValidScribanNotificationTemplate(IOnefuzzContext context, ILogTracer log, NotificationTemplate template) { + try { + var (didModify, _) = template switch { + TeamsTemplate => (false, template), + AdoTemplate adoTemplate => await ConvertToScriban(adoTemplate, attemptRender: true, context, log), + GithubIssuesTemplate githubTemplate => await ConvertToScriban(githubTemplate, attemptRender: true, context, log), + _ => throw new ArgumentOutOfRangeException(nameof(template), "Unexpected notification template type") + }; + + if (!didModify) { + return true; + } + return false; + } catch (Exception e) { + log.Exception(e); + return false; + } + } + + public static async Async.Task ValidateScribanTemplate(IOnefuzzContext context, ILogTracer log, TemplateRenderContext? renderContext, string template) { + var instanceUrl = context.ServiceConfiguration.OneFuzzInstance!; + + var (renderer, templateRenderContext) = await GenerateTemplateRenderContext(context, log, renderContext); + + var renderedTemaplate = await renderer.Render(template, new Uri(instanceUrl), strictRendering: true); + + return new TemplateValidationResponse( + renderedTemaplate, + templateRenderContext + ); + } + + private static async Async.Task<(NotificationsBase.Renderer, TemplateRenderContext)> GenerateTemplateRenderContext(IOnefuzzContext context, ILogTracer log, TemplateRenderContext? templateRenderContext) { + if (templateRenderContext != null) { + log.Info($"Using custom TemplateRenderContext"); + } else { + log.Info($"Generating TemplateRenderContext"); + } + + var targetUrl = templateRenderContext?.TargetUrl ?? new Uri("https://example.com/targetUrl"); + var inputUrl = templateRenderContext?.InputUrl ?? new Uri("https://example.com/inputUrl"); + var reportUrl = templateRenderContext?.ReportUrl ?? new Uri("https://example.com/reportUrl"); + var executable = "target.exe"; + var crashType = "some crash type"; + var crashSite = "some crash site"; + var callStack = new List() + { + "stack frame 0", + "stack frame 1" + }; + var callStackSha = "call stack sha"; + var inputSha = "input sha"; + var taskId = Guid.NewGuid(); + var jobId = Guid.NewGuid(); + var taskState = TaskState.Running; + var jobState = JobState.Enabled; + var os = Os.Linux; + var taskType = TaskType.LibfuzzerFuzz; + var duration = 100; + var project = "some project"; + var jobName = "job name"; + var buildName = "build name"; + var reportContainer = templateRenderContext?.ReportContainer ?? Container.Parse("example-container-name"); + var reportFileName = templateRenderContext?.ReportFilename ?? "example file name"; + var reproCmd = templateRenderContext?.ReproCmd ?? "onefuzz command to create a repro"; + var report = templateRenderContext?.Report ?? new Report( + inputUrl.ToString(), + null, + executable, + crashType, + crashSite, + callStack, + callStackSha, + inputSha, + null, + taskId, + jobId, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ); + + var task = new Task( + jobId, + taskId, + taskState, + os, + templateRenderContext?.Task ?? new TaskConfig( + jobId, + null, + new TaskDetails( + taskType, + duration + ) + ) + ); + + var job = new Job( + jobId, + jobState, + templateRenderContext?.Job ?? new JobConfig( + project, + jobName, + buildName, + duration, + null + ) + ); + + var renderer = await NotificationsBase.Renderer.ConstructRenderer( + context, + reportContainer, + reportFileName, + report, + task, + job, + targetUrl, + inputUrl, + reportUrl, + scribanOnlyOverride: true + ); + + templateRenderContext ??= new TemplateRenderContext( + report, + task.Config, + job.Config, + reportUrl, + inputUrl, + targetUrl, + reportContainer, + reportFileName, + reproCmd + ); + + return (renderer, templateRenderContext); + } + + public async static Async.Task<(bool didModify, AdoTemplate template)> ConvertToScriban(AdoTemplate template, bool attemptRender = false, IOnefuzzContext? context = null, ILogTracer? log = null) { + if (attemptRender) { + context = context.EnsureNotNull("Required to render"); + log = log.EnsureNotNull("Required to render"); + } + + var didModify = false; + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Project)) { + didModify = true; + template = template with { + Project = JinjaTemplateAdapter.AdaptForScriban(template.Project) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Project).IgnoreResult(); + } + + foreach (var item in template.AdoFields) { + if (JinjaTemplateAdapter.IsJinjaTemplate(item.Value)) { + template.AdoFields[item.Key] = JinjaTemplateAdapter.AdaptForScriban(item.Value); + didModify = true; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, item.Value).IgnoreResult(); + } + } + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Type)) { + didModify = true; + template = template with { + Type = JinjaTemplateAdapter.AdaptForScriban(template.Type) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Type).IgnoreResult(); + } + + if (template.Comment != null) { + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Comment)) { + didModify = true; + template = template with { + Comment = JinjaTemplateAdapter.AdaptForScriban(template.Comment) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Comment).IgnoreResult(); + } + } + + foreach (var item in template.OnDuplicate.AdoFields) { + if (JinjaTemplateAdapter.IsJinjaTemplate(item.Value)) { + template.OnDuplicate.AdoFields[item.Key] = JinjaTemplateAdapter.AdaptForScriban(item.Value); + didModify = true; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, item.Value).IgnoreResult(); + } + } + + if (template.OnDuplicate.Comment != null) { + if (JinjaTemplateAdapter.IsJinjaTemplate(template.OnDuplicate.Comment)) { + didModify = true; + template = template with { + OnDuplicate = template.OnDuplicate with { + Comment = JinjaTemplateAdapter.AdaptForScriban(template.OnDuplicate.Comment) + } + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.OnDuplicate.Comment).IgnoreResult(); + } + } + + return (didModify, template); + } + + public async static Async.Task<(bool didModify, GithubIssuesTemplate template)> ConvertToScriban(GithubIssuesTemplate template, bool attemptRender = false, IOnefuzzContext? context = null, ILogTracer? log = null) { + if (attemptRender) { + context = context.EnsureNotNull("Required to render"); + log = log.EnsureNotNull("Required to render"); + } + + var didModify = false; + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.UniqueSearch.str)) { + didModify = true; + template = template with { + UniqueSearch = template.UniqueSearch with { + str = JinjaTemplateAdapter.AdaptForScriban(template.UniqueSearch.str) + } + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.UniqueSearch.str).IgnoreResult(); + } + + + if (!string.IsNullOrEmpty(template.UniqueSearch.Author)) { + if (JinjaTemplateAdapter.IsJinjaTemplate(template.UniqueSearch.Author)) { + didModify = true; + template = template with { + UniqueSearch = template.UniqueSearch with { + Author = JinjaTemplateAdapter.AdaptForScriban(template.UniqueSearch.Author) + } + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.UniqueSearch.Author).IgnoreResult(); + } + } + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Title)) { + didModify = true; + template = template with { + Title = JinjaTemplateAdapter.AdaptForScriban(template.Title) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Title).IgnoreResult(); + } + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Body)) { + didModify = true; + template = template with { + Body = JinjaTemplateAdapter.AdaptForScriban(template.Body) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Body).IgnoreResult(); + } + + if (!string.IsNullOrEmpty(template.OnDuplicate.Comment)) { + if (JinjaTemplateAdapter.IsJinjaTemplate(template.OnDuplicate.Comment)) { + didModify = true; + template = template with { + OnDuplicate = template.OnDuplicate with { + Comment = JinjaTemplateAdapter.AdaptForScriban(template.OnDuplicate.Comment) + } + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.OnDuplicate.Comment).IgnoreResult(); + } + } + + if (template.OnDuplicate.Labels.Any()) { + template = template with { + OnDuplicate = template.OnDuplicate with { + Labels = template.OnDuplicate.Labels.ToAsyncEnumerable().SelectAwait(async label => { + if (JinjaTemplateAdapter.IsJinjaTemplate(label)) { + didModify = true; + return JinjaTemplateAdapter.AdaptForScriban(label); + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, label).IgnoreResult(); + } + return label; + }).ToEnumerable().ToList() + } + }; + } + + if (template.Assignees.Any()) { + template = template with { + Assignees = template.Assignees.ToAsyncEnumerable().SelectAwait(async assignee => { + if (JinjaTemplateAdapter.IsJinjaTemplate(assignee)) { + didModify = true; + return JinjaTemplateAdapter.AdaptForScriban(assignee); + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, assignee).IgnoreResult(); + } + return assignee; + }).ToEnumerable().ToList() + }; + } + + if (template.Labels.Any()) { + template = template with { + Labels = template.Labels.ToAsyncEnumerable().SelectAwait(async label => { + if (JinjaTemplateAdapter.IsJinjaTemplate(label)) { + didModify = true; + return JinjaTemplateAdapter.AdaptForScriban(label); + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, label).IgnoreResult(); + } + return label; + }).ToEnumerable().ToList() + }; + } + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Organization)) { + didModify = true; + template = template with { + Organization = JinjaTemplateAdapter.AdaptForScriban(template.Organization) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Organization).IgnoreResult(); + } + + if (JinjaTemplateAdapter.IsJinjaTemplate(template.Repository)) { + didModify = true; + template = template with { + Repository = JinjaTemplateAdapter.AdaptForScriban(template.Repository) + }; + } else if (attemptRender) { + await ValidateScribanTemplate(context!, log!, null, template.Repository).IgnoreResult(); + } + + return (didModify, template); + } } diff --git a/src/ApiService/IntegrationTests/Fakes/TestContext.cs b/src/ApiService/IntegrationTests/Fakes/TestContext.cs index 0afec423c2..e69ae1955a 100644 --- a/src/ApiService/IntegrationTests/Fakes/TestContext.cs +++ b/src/ApiService/IntegrationTests/Fakes/TestContext.cs @@ -41,6 +41,7 @@ public TestContext(ILogTracer logTracer, IStorage storage, ICreds creds, string UserCredentials = new UserCredentials(logTracer, ConfigOperations); NotificationOperations = new NotificationOperations(logTracer, this); SecretsOperations = new TestSecretsOperations(Creds, ServiceConfiguration); + FeatureManagerSnapshot = new TestFeatureManagerSnapshot(); } public TestEvents Events { get; set; } = new(); @@ -92,6 +93,9 @@ public Async.Task InsertAll(params EntityBase[] objs) public ISecretsOperations SecretsOperations { get; } + public IFeatureManagerSnapshot FeatureManagerSnapshot { get; } + + // -- Remainder not implemented -- public IConfig Config => throw new System.NotImplementedException(); @@ -129,7 +133,6 @@ public Async.Task InsertAll(params EntityBase[] objs) public ITeams Teams => throw new NotImplementedException(); public IGithubIssues GithubIssues => throw new NotImplementedException(); public IAdo Ado => throw new NotImplementedException(); - public IFeatureManagerSnapshot FeatureManagerSnapshot => throw new NotImplementedException(); public IConfigurationRefresher ConfigurationRefresher => throw new NotImplementedException(); } diff --git a/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs b/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs new file mode 100644 index 0000000000..e832f567fa --- /dev/null +++ b/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs @@ -0,0 +1,30 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.FeatureManagement; + +namespace IntegrationTests.Fakes; + +public class TestFeatureManagerSnapshot : IFeatureManagerSnapshot { + + private static ConcurrentDictionary FeatureFlags = new(); + public IAsyncEnumerable GetFeatureNamesAsync() { + throw new System.NotImplementedException(); + } + + public Task IsEnabledAsync(string feature) { + return Task.FromResult(FeatureFlags.ContainsKey(feature) && FeatureFlags.TryGetValue(feature, out var enabled) && enabled); + } + + public Task IsEnabledAsync(string feature, TContext context) { + throw new System.NotImplementedException(); + } + + public static void AddFeatureFlag(string featureName, bool enabled = false) { + var _ = FeatureFlags.TryAdd(featureName, enabled); + } + + public static void SetFeatureFlag(string featureName, bool enabled) { + var _ = FeatureFlags.TryUpdate(featureName, enabled, !enabled); + } +} From 3dc05426a7161aac7deb3c3ab90212d6b5b96b22 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Tue, 14 Feb 2023 20:42:59 +0000 Subject: [PATCH 3/4] fmt --- .../IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs b/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs index e832f567fa..7ca3584ae1 100644 --- a/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs +++ b/src/ApiService/IntegrationTests/Fakes/TestFeatureManagerSnapshot.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.FeatureManagement; From beb7fd3bad708eb914b7777d0355741acefbd54f Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Wed, 15 Feb 2023 13:56:41 +0000 Subject: [PATCH 4/4] missed when merging --- .../ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs b/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs index a0701845ee..d735b40a7e 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/JinjaTemplateAdapter.cs @@ -135,6 +135,7 @@ public static async Async.Task ValidateScribanTempla reportContainer, reportFileName, report, + log, task, job, targetUrl,