mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-10 01:01:34 +00:00
Validate scriban on new notifications (#2834)
* Add new command * Enforce scriban at notification creation time * fmt * missed when merging
This commit is contained in:
parent
21374b36e6
commit
e9f5a6a2e7
@ -39,11 +39,11 @@ public class JinjaToScriban {
|
|||||||
_log.Info($"Finding notifications to migrate");
|
_log.Info($"Finding notifications to migrate");
|
||||||
|
|
||||||
var notifications = _context.NotificationOperations.SearchAll()
|
var notifications = _context.NotificationOperations.SearchAll()
|
||||||
.Select(notification => {
|
.SelectAwait(async notification => {
|
||||||
var (didModify, config) = notification.Config switch {
|
var (didModify, config) = notification.Config switch {
|
||||||
TeamsTemplate => (false, notification.Config),
|
TeamsTemplate => (false, notification.Config),
|
||||||
AdoTemplate adoTemplate => ConvertToScriban(adoTemplate),
|
AdoTemplate adoTemplate => await JinjaTemplateAdapter.ConvertToScriban(adoTemplate),
|
||||||
GithubIssuesTemplate githubIssuesTemplate => ConvertToScriban(githubIssuesTemplate),
|
GithubIssuesTemplate githubIssuesTemplate => await JinjaTemplateAdapter.ConvertToScriban(githubIssuesTemplate),
|
||||||
_ => throw new NotImplementedException("Unexpected notification configuration type")
|
_ => throw new NotImplementedException("Unexpected notification configuration type")
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -82,153 +82,4 @@ public class JinjaToScriban {
|
|||||||
|
|
||||||
return await RequestHandling.Ok(req, new JinjaToScribanMigrationResponse(updatedNotificationsIds, failedNotificationIds));
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,19 +17,9 @@ public class ValidateScriban {
|
|||||||
return await _context.RequestHandling.NotOk(req, request.ErrorV, "ValidateTemplate");
|
return await _context.RequestHandling.NotOk(req, request.ErrorV, "ValidateTemplate");
|
||||||
}
|
}
|
||||||
|
|
||||||
var instanceUrl = _context.ServiceConfiguration.OneFuzzInstance!;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var (renderer, templateRenderContext) = await GenerateTemplateRenderContext(request.OkV.Context);
|
return await RequestHandling.Ok(req, await JinjaTemplateAdapter.ValidateScribanTemplate(_context, _log, request.OkV.Context, request.OkV.Template));
|
||||||
|
|
||||||
var renderedTemaplate = await renderer.Render(request.OkV.Template, new Uri(instanceUrl), strictRendering: true);
|
|
||||||
|
|
||||||
var response = new TemplateValidationResponse(
|
|
||||||
renderedTemaplate,
|
|
||||||
templateRenderContext
|
|
||||||
);
|
|
||||||
|
|
||||||
return await RequestHandling.Ok(req, response);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return await new RequestHandling(_log).NotOk(
|
return await new RequestHandling(_log).NotOk(
|
||||||
req,
|
req,
|
||||||
@ -47,119 +37,4 @@ public class ValidateScriban {
|
|||||||
_ => throw new InvalidOperationException("Unsupported HTTP method"),
|
_ => 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<string>()
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
_log,
|
|
||||||
task,
|
|
||||||
job,
|
|
||||||
targetUrl,
|
|
||||||
inputUrl,
|
|
||||||
reportUrl,
|
|
||||||
scribanOnlyOverride: true
|
|
||||||
);
|
|
||||||
|
|
||||||
templateRenderContext ??= new TemplateRenderContext(
|
|
||||||
report,
|
|
||||||
task.Config,
|
|
||||||
job.Config,
|
|
||||||
reportUrl,
|
|
||||||
inputUrl,
|
|
||||||
targetUrl,
|
|
||||||
reportContainer,
|
|
||||||
reportFileName,
|
|
||||||
reproCmd
|
|
||||||
);
|
|
||||||
|
|
||||||
return (renderer, templateRenderContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,11 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
|||||||
return OneFuzzResult<Notification>.Error(ErrorCode.INVALID_REQUEST, "invalid container");
|
return OneFuzzResult<Notification>.Error(ErrorCode.INVALID_REQUEST, "invalid container");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.EnableScribanOnly) &&
|
||||||
|
!await JinjaTemplateAdapter.IsValidScribanNotificationTemplate(_context, _logTracer, config)) {
|
||||||
|
return OneFuzzResult<Notification>.Error(ErrorCode.INVALID_REQUEST, "The notification config is not a valid scriban template");
|
||||||
|
}
|
||||||
|
|
||||||
if (replaceExisting) {
|
if (replaceExisting) {
|
||||||
var existing = this.SearchByRowKeys(new[] { container.String });
|
var existing = this.SearchByRowKeys(new[] { container.String });
|
||||||
await foreach (var existingEntry in existing) {
|
await foreach (var existingEntry in existing) {
|
||||||
|
@ -11,4 +11,351 @@ public class JinjaTemplateAdapter {
|
|||||||
.Replace("{%", "{{")
|
.Replace("{%", "{{")
|
||||||
.Replace("%}", "}}");
|
.Replace("%}", "}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Async.Task<bool> 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<TemplateValidationResponse> 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<string>()
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
log,
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public sealed class TestContext : IOnefuzzContext {
|
|||||||
UserCredentials = new UserCredentials(logTracer, ConfigOperations);
|
UserCredentials = new UserCredentials(logTracer, ConfigOperations);
|
||||||
NotificationOperations = new NotificationOperations(logTracer, this);
|
NotificationOperations = new NotificationOperations(logTracer, this);
|
||||||
SecretsOperations = new TestSecretsOperations(Creds, ServiceConfiguration);
|
SecretsOperations = new TestSecretsOperations(Creds, ServiceConfiguration);
|
||||||
|
FeatureManagerSnapshot = new TestFeatureManagerSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestEvents Events { get; set; } = new();
|
public TestEvents Events { get; set; } = new();
|
||||||
@ -92,6 +93,9 @@ public sealed class TestContext : IOnefuzzContext {
|
|||||||
|
|
||||||
public ISecretsOperations SecretsOperations { get; }
|
public ISecretsOperations SecretsOperations { get; }
|
||||||
|
|
||||||
|
public IFeatureManagerSnapshot FeatureManagerSnapshot { get; }
|
||||||
|
|
||||||
|
|
||||||
// -- Remainder not implemented --
|
// -- Remainder not implemented --
|
||||||
|
|
||||||
public IConfig Config => throw new System.NotImplementedException();
|
public IConfig Config => throw new System.NotImplementedException();
|
||||||
@ -129,7 +133,6 @@ public sealed class TestContext : IOnefuzzContext {
|
|||||||
public ITeams Teams => throw new NotImplementedException();
|
public ITeams Teams => throw new NotImplementedException();
|
||||||
public IGithubIssues GithubIssues => throw new NotImplementedException();
|
public IGithubIssues GithubIssues => throw new NotImplementedException();
|
||||||
public IAdo Ado => throw new NotImplementedException();
|
public IAdo Ado => throw new NotImplementedException();
|
||||||
public IFeatureManagerSnapshot FeatureManagerSnapshot => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public IConfigurationRefresher ConfigurationRefresher => throw new NotImplementedException();
|
public IConfigurationRefresher ConfigurationRefresher => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -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<string, bool> FeatureFlags = new();
|
||||||
|
public IAsyncEnumerable<string> GetFeatureNamesAsync() {
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> IsEnabledAsync(string feature) {
|
||||||
|
return Task.FromResult(FeatureFlags.ContainsKey(feature) && FeatureFlags.TryGetValue(feature, out var enabled) && enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> IsEnabledAsync<TContext>(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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user