mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-10 09:11:36 +00:00
Refactor notification support (#2363)
* Add teams notifications * . * Fix compilation isues * Checkpoint * Added Ado * Fix some TODOs * Teams messages work! 🎉 * fmt * Bug fix container url generator * Some small ado changes * 🧹 * PR comments * Fix packages * Get more detailed restore information to debug errors * Maybe fixes this issue? * Undo CI change
This commit is contained in:
parent
f375ee719e
commit
ca7b6be43b
@ -1,16 +1,16 @@
|
||||
# Azure Devops Work Item creation
|
||||
|
||||
Automatic creation of ADO Work Items from OneFuzz allows for the user to
|
||||
customize any field using [jinja2](https://jinja.palletsprojects.com/)
|
||||
customize any field using [scriban](https://github.com/scriban/scriban)
|
||||
templates.
|
||||
|
||||
There are multiple Python objects provided via the template engine that
|
||||
There are multiple objects provided via the template engine that
|
||||
can be used such that any arbitrary component can be used to flesh out
|
||||
the configuration:
|
||||
|
||||
* task (See [TaskConfig](../../src/pytypes/onefuzztypes/models.py))
|
||||
* report (See [Report](../../src/pytypes/onefuzztypes/models.py))
|
||||
* job (See [JobConfig](../../src/pytypes/onefuzztypes/models.py))
|
||||
* task (See [TaskConfig](../../src/ApiService/ApiService/OneFuzzTypes/Model.cs))
|
||||
* report (See [Report](../../src/ApiService/ApiService/OneFuzzTypes/Model.cs))
|
||||
* job (See [JobConfig](../../src/ApiService/ApiService/OneFuzzTypes/Model.cs))
|
||||
|
||||
Using these objects allows dynamic configuration. As an example, the `project`
|
||||
could be specified directly, or dynamically pulled from a template:
|
||||
@ -49,7 +49,7 @@ clickable, make it a link.
|
||||
"Microsoft.VSTS.Scheduling.StoryPoints": "1",
|
||||
"System.IterationPath": "Iteration\\Path\\Here",
|
||||
"System.Title": "{{ report.crash_site }} - {{ report.executable }}",
|
||||
"Microsoft.VSTS.TCM.ReproSteps": "This is my call stack: <ul> {% for item in report.call_stack %} <li> {{ item }} </li> {% endfor %} </ul>"
|
||||
"Microsoft.VSTS.TCM.ReproSteps": "This is my call stack: <ul> {{ for item in report.call_stack }} <li> {{ item }} </li> {{ endfor }} </ul>"
|
||||
},
|
||||
"comment": "This is my comment. {{ report.input_sha256 }} {{ input_url }} <br> <pre>{{ repro_cmd }}</pre>",
|
||||
"unique_fields": ["System.Title", "System.AreaPath"],
|
||||
@ -140,7 +140,7 @@ onefuzz notifications create_ado oft-my-demo-job-reports \
|
||||
Microsoft.VSTS.Scheduling.StoryPoints=1 \
|
||||
"System.IterationPath=Iteration\\Path\\Here" \
|
||||
"System.Title={{ report.crash_site }} - {{ report.executable }}" \
|
||||
"Microsoft.VSTS.TCM.ReproSteps=This is my call stack: <ul> {% for item in report.call_stack %} <li> {{ item }} </li> {% endfor %} </ul>" \
|
||||
"Microsoft.VSTS.TCM.ReproSteps=This is my call stack: <ul> {{ for item in report.call_stack }} <li> {{ item }} </li> {{ end }} </ul>" \
|
||||
--comment "This is my comment. {{ report.input_sha256 }} {{ input_url }}" \
|
||||
--on_dup_comment "Another <a href='{{ input_url }}'>POC</a> was found in <a href='{{ target_url }}'>target</a>" \
|
||||
--on_dup_set_state Resolved=Active \
|
||||
|
@ -42,6 +42,8 @@
|
||||
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.SignalRService" Version="1.7.0" />
|
||||
<PackageReference Include="TaskTupleAwaiter" Version="2.0.0" />
|
||||
<PackageReference Include="Scriban" Version="5.5.0" />
|
||||
<PackageReference Include="Octokit" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.209.0-preview" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="host.json">
|
||||
|
@ -22,9 +22,9 @@ public class QueueFileChanges {
|
||||
_notificationOperations = notificationOperations;
|
||||
}
|
||||
|
||||
//[Function("QueueFileChanges")]
|
||||
[Function("QueueFileChanges")]
|
||||
public async Async.Task Run(
|
||||
[QueueTrigger("file-changes-refactored", Connection = "AzureWebJobsStorage")] string msg,
|
||||
[QueueTrigger("file-changes", Connection = "AzureWebJobsStorage")] string msg,
|
||||
int dequeueCount) {
|
||||
var fileChangeEvent = JsonSerializer.Deserialize<JsonDocument>(msg, EntityConverter.GetJsonSerializerOptions());
|
||||
var lastTry = dequeueCount == MAX_DEQUEUE_COUNT;
|
||||
@ -44,10 +44,10 @@ public class QueueFileChanges {
|
||||
return;
|
||||
}
|
||||
|
||||
await file_added(_log, fileChangeEvent, lastTry);
|
||||
await FileAdded(_log, fileChangeEvent, lastTry);
|
||||
}
|
||||
|
||||
private async Async.Task file_added(ILogTracer log, JsonDocument fileChangeEvent, bool failTaskOnTransientError) {
|
||||
private async Async.Task FileAdded(ILogTracer log, JsonDocument fileChangeEvent, bool failTaskOnTransientError) {
|
||||
var data = fileChangeEvent.RootElement.GetProperty("data");
|
||||
var url = data.GetProperty("url").GetString()!;
|
||||
var parts = url.Split("/").Skip(3).ToList();
|
||||
|
@ -84,7 +84,11 @@ public record ProxyHeartbeat
|
||||
Guid ProxyId,
|
||||
List<Forward> Forwards,
|
||||
DateTimeOffset TimeStamp
|
||||
);
|
||||
) {
|
||||
public override string ToString() {
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
};
|
||||
|
||||
public record Node
|
||||
(
|
||||
@ -412,7 +416,7 @@ public record Notification(
|
||||
|
||||
public record BlobRef(
|
||||
string Account,
|
||||
Container container,
|
||||
Container Container,
|
||||
string Name
|
||||
);
|
||||
|
||||
@ -470,21 +474,43 @@ public class NotificationTemplateConverter : JsonConverter<NotificationTemplate>
|
||||
public override NotificationTemplate? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
||||
using var templateJson = JsonDocument.ParseValue(ref reader);
|
||||
try {
|
||||
return templateJson.Deserialize<AdoTemplate>(options);
|
||||
} catch (JsonException) {
|
||||
return ValidateDeserialization(templateJson.Deserialize<AdoTemplate>(options));
|
||||
} catch (Exception ex) when (
|
||||
ex is JsonException
|
||||
|| ex is ArgumentNullException
|
||||
|| ex is ArgumentOutOfRangeException
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
return templateJson.Deserialize<TeamsTemplate>(options);
|
||||
} catch (JsonException) {
|
||||
return ValidateDeserialization(templateJson.Deserialize<TeamsTemplate>(options));
|
||||
} catch (Exception ex) when (
|
||||
ex is JsonException
|
||||
|| ex is ArgumentNullException
|
||||
|| ex is ArgumentOutOfRangeException
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
return templateJson.Deserialize<GithubIssuesTemplate>(options);
|
||||
} catch (JsonException) {
|
||||
return ValidateDeserialization(templateJson.Deserialize<GithubIssuesTemplate>(options));
|
||||
} catch (Exception ex) when (
|
||||
ex is JsonException
|
||||
|| ex is ArgumentNullException
|
||||
|| ex is ArgumentOutOfRangeException
|
||||
) {
|
||||
|
||||
}
|
||||
throw new JsonException("Unsupported notification template");
|
||||
|
||||
var expectedTemplateTypes = new List<Type> {
|
||||
typeof(AdoTemplate),
|
||||
typeof(TeamsTemplate),
|
||||
typeof(GithubIssuesTemplate)
|
||||
}
|
||||
.Select(type => type.ToString());
|
||||
|
||||
throw new JsonException($"Unsupported notification template. Could not deserialize {templateJson} into one of the following template types: {string.Join(", ", expectedTemplateTypes)}");
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, NotificationTemplate value, JsonSerializerOptions options) {
|
||||
@ -499,14 +525,38 @@ public class NotificationTemplateConverter : JsonConverter<NotificationTemplate>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static T ValidateDeserialization<T>(T? obj) {
|
||||
if (obj == null) {
|
||||
throw new ArgumentNullException($"Failed to deserialize type: {typeof(T)}. It was null.");
|
||||
}
|
||||
var nonNullableParameters = obj.GetType().GetConstructors().First().GetParameters()
|
||||
.Where(parameter => !parameter.HasDefaultValue)
|
||||
.Select(parameter => parameter.Name)
|
||||
.Where(pName => pName != null)
|
||||
.ToHashSet();
|
||||
|
||||
var nullProperties = obj.GetType().GetProperties()
|
||||
.Where(property => property.GetValue(obj) == null)
|
||||
.Select(property => property.Name)
|
||||
.ToHashSet<Endpoint>();
|
||||
|
||||
var nullNonNullableProperties = nonNullableParameters.Intersect(nullProperties);
|
||||
|
||||
if (nullNonNullableProperties.Any()) {
|
||||
throw new ArgumentOutOfRangeException($"Failed to deserialize type: {obj.GetType()}. The following non nullable properties are missing values: {string.Join(", ", nullNonNullableProperties)}");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public record ADODuplicateTemplate(
|
||||
List<string> Increment,
|
||||
string? Comment,
|
||||
Dictionary<string, string> SetState,
|
||||
Dictionary<string, string> AdoFields
|
||||
Dictionary<string, string> AdoFields,
|
||||
string? Comment = null
|
||||
);
|
||||
|
||||
public record AdoTemplate(
|
||||
@ -515,27 +565,26 @@ public record AdoTemplate(
|
||||
string Project,
|
||||
string Type,
|
||||
List<string> UniqueFields,
|
||||
string? Comment,
|
||||
Dictionary<string, string> AdoFields,
|
||||
ADODuplicateTemplate OnDuplicate
|
||||
ADODuplicateTemplate OnDuplicate,
|
||||
string? Comment = null
|
||||
) : NotificationTemplate;
|
||||
|
||||
public record TeamsTemplate(SecretData<string> Url) : NotificationTemplate;
|
||||
|
||||
|
||||
public record GithubAuth(string User, string PersonalAccessToken);
|
||||
|
||||
public record GithubIssueSearch(
|
||||
string? Author,
|
||||
GithubIssueState? State,
|
||||
List<GithubIssueSearchMatch> FieldMatch,
|
||||
[property: JsonPropertyName("string")] String str
|
||||
[property: JsonPropertyName("string")] String str,
|
||||
string? Author = null,
|
||||
GithubIssueState? State = null
|
||||
);
|
||||
|
||||
public record GithubIssueDuplicate(
|
||||
string? Comment,
|
||||
List<string> Labels,
|
||||
bool Reopen
|
||||
bool Reopen,
|
||||
string? Comment = null
|
||||
);
|
||||
|
||||
|
||||
|
@ -102,6 +102,9 @@ public class Program {
|
||||
.AddScoped<INodeMessageOperations, NodeMessageOperations>()
|
||||
.AddScoped<IRequestHandling, RequestHandling>()
|
||||
.AddScoped<IImageOperations, ImageOperations>()
|
||||
.AddScoped<ITeams, Teams>()
|
||||
.AddScoped<IGithubIssues, GithubIssues>()
|
||||
.AddScoped<IAdo, Ado>()
|
||||
.AddScoped<IOnefuzzContext, OnefuzzContext>()
|
||||
.AddScoped<IEndpointAuthorization, EndpointAuthorization>()
|
||||
.AddScoped<INodeMessageOperations, NodeMessageOperations>()
|
||||
|
@ -29,6 +29,8 @@ public interface IContainers {
|
||||
|
||||
public Async.Task<Uri> AddContainerSasUrl(Uri uri, TimeSpan? duration = null);
|
||||
public Async.Task<Dictionary<Container, IDictionary<string, string>>> GetContainers(StorageType corpus);
|
||||
|
||||
public string AuthDownloadUrl(Container container, string filename);
|
||||
}
|
||||
|
||||
public class Containers : IContainers {
|
||||
@ -215,4 +217,14 @@ public class Containers : IContainers {
|
||||
|
||||
return new(data.SelectMany(x => x));
|
||||
}
|
||||
|
||||
public string AuthDownloadUrl(Container container, string filename) {
|
||||
var instance = _config.OneFuzzInstance;
|
||||
|
||||
var queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
|
||||
queryString.Add("container", container.String);
|
||||
queryString.Add("filename", filename);
|
||||
|
||||
return $"{instance}/api/download?{queryString}";
|
||||
}
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ public class Extensions : IExtensions {
|
||||
BlobSasPermissions.Read
|
||||
),
|
||||
await _context.Containers.GetFileSasUrl(
|
||||
report?.InputBlob?.container!,
|
||||
report?.InputBlob?.Container!,
|
||||
report?.InputBlob?.Name!,
|
||||
StorageType.Corpus,
|
||||
BlobSasPermissions.Read
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using ApiService.OneFuzzLib.Orm;
|
||||
using Azure.Data.Tables;
|
||||
using Azure.Storage.Sas;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
@ -36,7 +35,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
done.Add(notification.Config);
|
||||
|
||||
if (notification.Config is TeamsTemplate teamsTemplate) {
|
||||
NotifyTeams(teamsTemplate, container, filename, reportOrRegression!);
|
||||
await _context.Teams.NotifyTeams(teamsTemplate, container, filename, reportOrRegression!);
|
||||
}
|
||||
|
||||
if (reportOrRegression == null) {
|
||||
@ -44,11 +43,11 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
}
|
||||
|
||||
if (notification.Config is AdoTemplate adoTemplate) {
|
||||
NotifyAdo(adoTemplate, container, filename, reportOrRegression, failTaskOnTransientError);
|
||||
await _context.Ado.NotifyAdo(adoTemplate, container, filename, reportOrRegression, failTaskOnTransientError);
|
||||
}
|
||||
|
||||
if (notification.Config is GithubIssuesTemplate githubIssuesTemplate) {
|
||||
GithubIssue(githubIssuesTemplate, container, filename, reportOrRegression);
|
||||
await _context.GithubIssues.GithubIssue(githubIssuesTemplate, container, filename, reportOrRegression);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +77,7 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Notification> GetNotifications(Container container) {
|
||||
return QueryAsync(filter: TableClient.CreateQueryFilter($"container eq {container.String}"));
|
||||
return SearchByRowKeys(new[] { container.String });
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<(Task, IEnumerable<Container>)> GetQueueTasks() {
|
||||
@ -140,16 +139,4 @@ public class NotificationOperations : Orm<Notification>, INotificationOperations
|
||||
_logTracer.Error($"unable to find crash_report or no repro entry for report: {JsonSerializer.Serialize(report)}");
|
||||
return null;
|
||||
}
|
||||
|
||||
private void GithubIssue(GithubIssuesTemplate config, Container container, string filename, IReport report) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void NotifyAdo(AdoTemplate config, Container container, string filename, IReport report, bool failTaskOnTransientError) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void NotifyTeams(TeamsTemplate config, Container container, string filename, IReport report) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,9 @@ public interface IOnefuzzContext {
|
||||
ISubnet Subnet { get; }
|
||||
IImageOperations ImageOperations { get; }
|
||||
EntityConverter EntityConverter { get; }
|
||||
ITeams Teams { get; }
|
||||
IGithubIssues GithubIssues { get; }
|
||||
IAdo Ado { get; }
|
||||
}
|
||||
|
||||
public class OnefuzzContext : IOnefuzzContext {
|
||||
@ -89,4 +92,7 @@ public class OnefuzzContext : IOnefuzzContext {
|
||||
public ISubnet Subnet => _serviceProvider.GetRequiredService<ISubnet>();
|
||||
public IImageOperations ImageOperations => _serviceProvider.GetRequiredService<IImageOperations>();
|
||||
public EntityConverter EntityConverter => _serviceProvider.GetRequiredService<EntityConverter>();
|
||||
public ITeams Teams => _serviceProvider.GetRequiredService<ITeams>();
|
||||
public IGithubIssues GithubIssues => _serviceProvider.GetRequiredService<IGithubIssues>();
|
||||
public IAdo Ado => _serviceProvider.GetRequiredService<IAdo>();
|
||||
}
|
||||
|
@ -59,18 +59,6 @@ public class Reports : IReports {
|
||||
}
|
||||
return regressionReport;
|
||||
}
|
||||
|
||||
private IReport? ParseReportOrRegression(IEnumerable<byte> content, string? filePath, bool expectReports = false) {
|
||||
try {
|
||||
var str = System.Text.Encoding.UTF8.GetString(content.ToArray());
|
||||
return ParseReportOrRegression(str, filePath, expectReports);
|
||||
} catch (Exception e) {
|
||||
if (expectReports) {
|
||||
_log.Error($"unable to parse report ({filePath}): unicode decode of report failed - {e.Message} {e.StackTrace}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IReport { };
|
||||
|
@ -248,7 +248,7 @@ public class Scheduler : IScheduler {
|
||||
});
|
||||
}
|
||||
|
||||
static Container GetSetupContainer(TaskConfig config) {
|
||||
public static Container GetSetupContainer(TaskConfig config) {
|
||||
|
||||
foreach (var container in config.Containers ?? throw new Exception("Missing containers")) {
|
||||
if (container.Type == ContainerType.Setup) {
|
||||
|
283
src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs
Normal file
283
src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs
Normal file
@ -0,0 +1,283 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
|
||||
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
|
||||
using Microsoft.VisualStudio.Services.Common;
|
||||
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public interface IAdo {
|
||||
public Async.Task NotifyAdo(AdoTemplate config, Container container, string filename, IReport reportable, bool failTaskOnTransientError);
|
||||
|
||||
}
|
||||
|
||||
public class Ado : NotificationsBase, IAdo {
|
||||
public Ado(ILogTracer logTracer, IOnefuzzContext context) : base(logTracer, context) {
|
||||
}
|
||||
|
||||
public async Async.Task NotifyAdo(AdoTemplate config, Container container, string filename, IReport reportable, bool failTaskOnTransientError) {
|
||||
if (reportable is RegressionReport) {
|
||||
_logTracer.Info($"ado integration does not support regression report. container:{container} filename:{filename}");
|
||||
return;
|
||||
}
|
||||
|
||||
var report = (Report)reportable;
|
||||
|
||||
var notificationInfo = @$"job_id:{report.JobId} task_id:{report.TaskId}
|
||||
container:{container} filename:{filename}";
|
||||
|
||||
_logTracer.Info($"notify ado: {notificationInfo}");
|
||||
|
||||
try {
|
||||
var ado = await AdoConnector.AdoConnectorCreator(_context, container, filename, config, report, _logTracer);
|
||||
await ado.Process(notificationInfo);
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
TODO: Catch these
|
||||
AzureDevOpsAuthenticationError,
|
||||
AzureDevOpsClientError,
|
||||
AzureDevOpsServiceError,
|
||||
AzureDevOpsClientRequestError,
|
||||
ValueError,
|
||||
*/
|
||||
if (!failTaskOnTransientError && IsTransient(e)) {
|
||||
_logTracer.Error($"transient ADO notification failure {notificationInfo}");
|
||||
throw;
|
||||
} else {
|
||||
await FailTask(report, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTransient(Exception e) {
|
||||
var errorCodes = new List<string>()
|
||||
{
|
||||
//TF401349: An unexpected error has occurred, please verify your request and try again.
|
||||
"TF401349",
|
||||
//TF26071: This work item has been changed by someone else since you opened it. You will need to refresh it and discard your changes.
|
||||
"TF26071",
|
||||
};
|
||||
|
||||
var errorStr = e.ToString();
|
||||
return errorCodes.Any(code => errorStr.Contains(code));
|
||||
}
|
||||
|
||||
class AdoConnector {
|
||||
private readonly AdoTemplate _config;
|
||||
private readonly Renderer _renderer;
|
||||
private readonly string _project;
|
||||
private readonly WorkItemTrackingHttpClient _client;
|
||||
private readonly Uri _instanceUrl;
|
||||
private readonly ILogTracer _logTracer;
|
||||
public static async Async.Task<AdoConnector> AdoConnectorCreator(IOnefuzzContext context, Container container, string filename, AdoTemplate config, Report report, ILogTracer logTracer, Renderer? renderer = null) {
|
||||
renderer ??= await Renderer.ConstructRenderer(context, container, filename, report);
|
||||
var instanceUrl = context.Creds.GetInstanceUrl();
|
||||
var project = await renderer.Render(config.Project, instanceUrl);
|
||||
|
||||
var authToken = await context.SecretsOperations.GetSecretStringValue(config.AuthToken);
|
||||
var client = GetAdoClient(config.BaseUrl, authToken!);
|
||||
return new AdoConnector(container, filename, config, report, renderer, project!, client, instanceUrl, logTracer);
|
||||
}
|
||||
|
||||
private static WorkItemTrackingHttpClient GetAdoClient(Uri baseUrl, string token) {
|
||||
return new WorkItemTrackingHttpClient(baseUrl, new VssBasicCredential("PAT", token));
|
||||
}
|
||||
public AdoConnector(Container container, string filename, AdoTemplate config, Report report, Renderer renderer, string project, WorkItemTrackingHttpClient client, Uri instanceUrl, ILogTracer logTracer) {
|
||||
_config = config;
|
||||
_renderer = renderer;
|
||||
_project = project;
|
||||
_client = client;
|
||||
_instanceUrl = instanceUrl;
|
||||
_logTracer = logTracer;
|
||||
}
|
||||
|
||||
public async Async.Task<string> Render(string template) {
|
||||
return await _renderer.Render(template, _instanceUrl);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<WorkItem> ExistingWorkItems() {
|
||||
var filters = new Dictionary<string, string>();
|
||||
foreach (var key in _config.UniqueFields) {
|
||||
var filter = string.Empty;
|
||||
if (string.Equals("System.TeamProject", key)) {
|
||||
filter = await Render(_config.Project);
|
||||
} else {
|
||||
filter = await Render(_config.AdoFields[key]);
|
||||
}
|
||||
filters.Add(key.ToLowerInvariant(), filter);
|
||||
}
|
||||
|
||||
var validFields = await GetValidFields(filters["system.teamproject"]);
|
||||
|
||||
var postQueryFilter = new Dictionary<string, string>();
|
||||
/*
|
||||
# WIQL (Work Item Query Language) is an SQL like query language that
|
||||
# doesn't support query params, safe quoting, or any other SQL-injection
|
||||
# protection mechanisms.
|
||||
#
|
||||
# As such, build the WIQL with a those fields we can pre-determine are
|
||||
# "safe" and otherwise use post-query filtering.
|
||||
*/
|
||||
|
||||
var parts = new List<string>();
|
||||
foreach (var key in filters.Keys) {
|
||||
//# Only add pre-system approved fields to the query
|
||||
if (!validFields.Contains(key)) {
|
||||
postQueryFilter.Add(key, filters[key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
# WIQL supports wrapping values in ' or " and escaping ' by doubling it
|
||||
#
|
||||
# For this System.Title: hi'there
|
||||
# use this query fragment: [System.Title] = 'hi''there'
|
||||
#
|
||||
# For this System.Title: hi"there
|
||||
# use this query fragment: [System.Title] = 'hi"there'
|
||||
#
|
||||
# For this System.Title: hi'"there
|
||||
# use this query fragment: [System.Title] = 'hi''"there'
|
||||
*/
|
||||
var single = "'";
|
||||
parts.Add($"[{key}] = '{filters[key].Replace(single, single + single)}'");
|
||||
}
|
||||
|
||||
var query = "select [System.Id] from WorkItems";
|
||||
if (parts != null && parts.Any()) {
|
||||
query += " where " + string.Join(" AND ", parts);
|
||||
}
|
||||
|
||||
var wiql = new Wiql() {
|
||||
Query = query
|
||||
};
|
||||
|
||||
foreach (var workItemReference in (await _client.QueryByWiqlAsync(wiql)).WorkItems) {
|
||||
var item = await _client.GetWorkItemAsync(_project, workItemReference.Id, expand: WorkItemExpand.Fields);
|
||||
|
||||
var loweredFields = item.Fields.ToDictionary(kvp => kvp.Key.ToLowerInvariant(), kvp => JsonSerializer.Serialize(kvp.Value));
|
||||
if (postQueryFilter.Any() && !postQueryFilter.All(kvp => {
|
||||
var lowerKey = kvp.Key.ToLowerInvariant();
|
||||
return loweredFields.ContainsKey(lowerKey) && loweredFields[lowerKey] == postQueryFilter[kvp.Key];
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
public async Async.Task UpdateExisting(WorkItem item, string notificationInfo) {
|
||||
if (_config.OnDuplicate.Comment != null) {
|
||||
var comment = await Render(_config.OnDuplicate.Comment);
|
||||
await _client.AddCommentAsync(
|
||||
new CommentCreate() {
|
||||
Text = comment
|
||||
},
|
||||
_project,
|
||||
(int)(item.Id!)
|
||||
);
|
||||
}
|
||||
|
||||
var document = new JsonPatchDocument();
|
||||
foreach (var field in _config.OnDuplicate.Increment) {
|
||||
var value = item.Fields.ContainsKey(field) ? int.Parse(JsonSerializer.Serialize(item.Fields[field])) : 0;
|
||||
value++;
|
||||
document.Add(new JsonPatchOperation() {
|
||||
Operation = VisualStudio.Services.WebApi.Patch.Operation.Replace,
|
||||
Path = $"/fields/{field}",
|
||||
Value = value.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var field in _config.OnDuplicate.AdoFields) {
|
||||
var fieldValue = await Render(_config.OnDuplicate.AdoFields[field.Key]);
|
||||
document.Add(new JsonPatchOperation() {
|
||||
Operation = VisualStudio.Services.WebApi.Patch.Operation.Replace,
|
||||
Path = $"/fields/{field}",
|
||||
Value = fieldValue
|
||||
});
|
||||
}
|
||||
|
||||
var systemState = JsonSerializer.Serialize(item.Fields["System.State"]);
|
||||
if (_config.OnDuplicate.SetState.ContainsKey(systemState)) {
|
||||
document.Add(new JsonPatchOperation() {
|
||||
Operation = VisualStudio.Services.WebApi.Patch.Operation.Replace,
|
||||
Path = "/fields/System.State",
|
||||
Value = _config.OnDuplicate.SetState[systemState]
|
||||
});
|
||||
}
|
||||
|
||||
if (document.Any()) {
|
||||
await _client.UpdateWorkItemAsync(document, _project, (int)(item.Id!));
|
||||
_logTracer.Info($"notify ado: updated work item {item.Id} - {notificationInfo}");
|
||||
} else {
|
||||
_logTracer.Info($"notify ado: no update for work item {item.Id} - {notificationInfo}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Async.Task<List<string>> GetValidFields(string? project) {
|
||||
return (await _client.GetFieldsAsync(project, expand: GetFieldsExpand.ExtensionFields))
|
||||
.Select(field => field.ReferenceName.ToLowerInvariant())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Async.Task<WorkItem> CreateNew() {
|
||||
var (taskType, document) = await RenderNew();
|
||||
var entry = await _client.CreateWorkItemAsync(document, _project, taskType);
|
||||
|
||||
if (_config.Comment != null) {
|
||||
var comment = await Render(_config.Comment);
|
||||
await _client.AddCommentAsync(
|
||||
new CommentCreate() {
|
||||
Text = comment,
|
||||
},
|
||||
_project,
|
||||
(int)(entry.Id!)
|
||||
);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private async Async.Task<(string, JsonPatchDocument)> RenderNew() {
|
||||
var taskType = await Render(_config.Type);
|
||||
var document = new JsonPatchDocument();
|
||||
if (!_config.AdoFields.ContainsKey("System.Tags")) {
|
||||
document.Add(new JsonPatchOperation() {
|
||||
Operation = VisualStudio.Services.WebApi.Patch.Operation.Add,
|
||||
Path = "/fields/System.Tags",
|
||||
Value = "Onefuzz"
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var field in _config.AdoFields.Keys) {
|
||||
var value = await Render(_config.AdoFields[field]);
|
||||
|
||||
if (string.Equals(field, "System.Tags")) {
|
||||
value += ";Onefuzz";
|
||||
}
|
||||
|
||||
document.Add(new JsonPatchOperation() {
|
||||
Operation = VisualStudio.Services.WebApi.Patch.Operation.Add,
|
||||
Path = $"/fields/{field}",
|
||||
Value = value
|
||||
});
|
||||
}
|
||||
|
||||
return (taskType, document);
|
||||
}
|
||||
|
||||
public async Async.Task Process(string notificationInfo) {
|
||||
var seen = false;
|
||||
await foreach (var workItem in ExistingWorkItems()) {
|
||||
await UpdateExisting(workItem, notificationInfo);
|
||||
seen = true;
|
||||
}
|
||||
|
||||
if (!seen) {
|
||||
var entry = await CreateNew();
|
||||
_logTracer.Info($"notify ado: created new work item {entry.Id} - {notificationInfo}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
using Octokit;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public interface IGithubIssues {
|
||||
Async.Task GithubIssue(GithubIssuesTemplate config, Container container, string filename, IReport? reportable);
|
||||
}
|
||||
|
||||
public class GithubIssues : NotificationsBase, IGithubIssues {
|
||||
|
||||
public GithubIssues(ILogTracer logTracer, IOnefuzzContext context)
|
||||
: base(logTracer, context) { }
|
||||
|
||||
public async Async.Task GithubIssue(GithubIssuesTemplate config, Container container, string filename, IReport? reportable) {
|
||||
if (reportable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reportable is RegressionReport) {
|
||||
_logTracer.Info($"github issue integration does not support regression reports. container:{container} filename:{filename}");
|
||||
return;
|
||||
}
|
||||
|
||||
var report = (Report)reportable;
|
||||
|
||||
try {
|
||||
await Process(config, container, filename, report);
|
||||
} catch (ApiException e) {
|
||||
await FailTask(report, e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Async.Task Process(GithubIssuesTemplate config, Container container, string filename, Report report) {
|
||||
var renderer = await Renderer.ConstructRenderer(_context, container, filename, report);
|
||||
var handler = await GithubConnnector.GithubConnnectorCreator(config, container, filename, renderer, _context.Creds.GetInstanceUrl(), _context, _logTracer);
|
||||
await handler.Process();
|
||||
}
|
||||
class GithubConnnector {
|
||||
private readonly GitHubClient _gh;
|
||||
private readonly GithubIssuesTemplate _config;
|
||||
private readonly Renderer _renderer;
|
||||
private readonly Uri _instanceUrl;
|
||||
private readonly ILogTracer _logTracer;
|
||||
|
||||
public static async Async.Task<GithubConnnector> GithubConnnectorCreator(GithubIssuesTemplate config, Container container, string filename, Renderer renderer, Uri instanceUrl, IOnefuzzContext context, ILogTracer logTracer) {
|
||||
var auth = config.Auth.Secret switch {
|
||||
SecretAddress<GithubAuth> sa => await context.SecretsOperations.GetSecretObj<GithubAuth>(sa.Url),
|
||||
SecretValue<GithubAuth> sv => sv.Value,
|
||||
_ => throw new ArgumentException($"Unexpected secret type {config.Auth.Secret.GetType()}")
|
||||
};
|
||||
return new GithubConnnector(config, container, filename, renderer, instanceUrl, auth!, logTracer);
|
||||
}
|
||||
|
||||
public GithubConnnector(GithubIssuesTemplate config, Container container, string filename, Renderer renderer, Uri instanceUrl, GithubAuth auth, ILogTracer logTracer) {
|
||||
_config = config;
|
||||
_gh = new GitHubClient(new ProductHeaderValue("microsoft/OneFuzz")) {
|
||||
Credentials = new Credentials(auth.User, auth.PersonalAccessToken)
|
||||
};
|
||||
_renderer = renderer;
|
||||
_instanceUrl = instanceUrl;
|
||||
_logTracer = logTracer;
|
||||
}
|
||||
|
||||
public async Async.Task Process() {
|
||||
var issues = await Existing();
|
||||
if (issues.Any()) {
|
||||
await Update(issues.First());
|
||||
} else {
|
||||
await Create();
|
||||
}
|
||||
}
|
||||
|
||||
private async Async.Task<string> Render(string field) {
|
||||
return await _renderer.Render(field, _instanceUrl);
|
||||
}
|
||||
|
||||
private async Async.Task<List<Issue>> Existing() {
|
||||
var query = new List<string>() {
|
||||
await Render(_config.UniqueSearch.str),
|
||||
$"repo:{_config.Organization}/{_config.Repository}"
|
||||
};
|
||||
|
||||
if (_config.UniqueSearch.Author != null) {
|
||||
query.Add($"author:{await Render(_config.UniqueSearch.Author)}");
|
||||
}
|
||||
|
||||
if (_config.UniqueSearch.State != null) {
|
||||
query.Add($"state:{_config.UniqueSearch.State}");
|
||||
}
|
||||
|
||||
var title = await Render(_config.Title);
|
||||
var body = await Render(_config.Body);
|
||||
var issues = new List<Issue>();
|
||||
var t = await _gh.Search.SearchIssues(new SearchIssuesRequest(string.Join(' ', query)));
|
||||
foreach (var issue in t.Items) {
|
||||
var skip = false;
|
||||
foreach (var field in _config.UniqueSearch.FieldMatch) {
|
||||
if (field == GithubIssueSearchMatch.Title && issue.Title != title) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (field == GithubIssueSearchMatch.Body && issue.Body != body) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
issues.Add(issue);
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
private async Async.Task Update(Issue issue) {
|
||||
_logTracer.Info($"updating issue: {issue}");
|
||||
if (_config.OnDuplicate.Comment != null) {
|
||||
await _gh.Issue.Comment.Create(issue.Repository.Id, issue.Number, await Render(_config.OnDuplicate.Comment));
|
||||
}
|
||||
if (_config.OnDuplicate.Labels.Any()) {
|
||||
var labels = await _config.OnDuplicate.Labels.ToAsyncEnumerable()
|
||||
.SelectAwait(async label => await Render(label))
|
||||
.ToArrayAsync();
|
||||
|
||||
await _gh.Issue.Labels.ReplaceAllForIssue(issue.Repository.Id, issue.Number, labels);
|
||||
}
|
||||
if (_config.OnDuplicate.Reopen && issue.State != ItemState.Open) {
|
||||
await _gh.Issue.Update(issue.Repository.Id, issue.Number, new IssueUpdate() {
|
||||
State = ItemState.Open
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Async.Task Create() {
|
||||
_logTracer.Info($"creating issue");
|
||||
var assignees = await _config.Assignees.ToAsyncEnumerable()
|
||||
.SelectAwait(async assignee => await Render(assignee))
|
||||
.ToListAsync();
|
||||
|
||||
var labels = await _config.Labels.ToAsyncEnumerable()
|
||||
.SelectAwait(async label => await Render(label))
|
||||
.ToHashSetAsync();
|
||||
|
||||
labels.Add("OneFuzz");
|
||||
|
||||
var newIssue = new NewIssue(await Render(_config.Title)) {
|
||||
Body = await Render(_config.Body),
|
||||
};
|
||||
|
||||
labels.ToList().ForEach(label => newIssue.Labels.Add(label));
|
||||
assignees.ForEach(assignee => newIssue.Assignees.Add(assignee));
|
||||
|
||||
await _gh.Issue.Create(
|
||||
await Render(_config.Organization),
|
||||
await Render(_config.Repository),
|
||||
newIssue
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
using Scriban;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public abstract class NotificationsBase {
|
||||
|
||||
#pragma warning disable CA1051 // permit visible instance fields
|
||||
protected readonly ILogTracer _logTracer;
|
||||
protected readonly IOnefuzzContext _context;
|
||||
#pragma warning restore CA1051
|
||||
public NotificationsBase(ILogTracer logTracer, IOnefuzzContext context) {
|
||||
_logTracer = logTracer;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Async.Task FailTask(Report report, Exception error) {
|
||||
_logTracer.Error($"notification failed: job_id:{report.JobId} task_id:{report.TaskId} err:{error}");
|
||||
|
||||
var task = await _context.TaskOperations.GetByJobIdAndTaskId(report.JobId, report.TaskId);
|
||||
if (task != null) {
|
||||
await _context.TaskOperations.MarkFailed(task, new Error(ErrorCode.NOTIFICATION_FAILURE, new string[] {
|
||||
"notification failed",
|
||||
error.ToString()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReplaceFirstSetup(string executable) {
|
||||
var setup = "setup/";
|
||||
int index = executable.IndexOf(setup, StringComparison.InvariantCultureIgnoreCase);
|
||||
var setupFileName = (index < 0) ? executable : executable.Remove(index, setup.Length);
|
||||
return setupFileName;
|
||||
}
|
||||
|
||||
protected class Renderer {
|
||||
private readonly Report _report;
|
||||
private readonly Container _container;
|
||||
private readonly string _filename;
|
||||
private readonly TaskConfig _taskConfig;
|
||||
private readonly JobConfig _jobConfig;
|
||||
private readonly Uri _targetUrl;
|
||||
private readonly Uri _inputUrl;
|
||||
private readonly Uri _reportUrl;
|
||||
|
||||
public static async Async.Task<Renderer> ConstructRenderer(IOnefuzzContext context, Container container, string filename, Report report, Task? task = null, Job? job = null, Uri? targetUrl = null, Uri? inputUrl = null, Uri? reportUrl = null) {
|
||||
task ??= await context.TaskOperations.GetByJobIdAndTaskId(report.JobId, report.TaskId);
|
||||
task.EnsureNotNull($"invalid task {report.TaskId}");
|
||||
|
||||
job ??= await context.JobOperations.Get(report.JobId);
|
||||
job.EnsureNotNull($"invalid job {report.JobId}");
|
||||
|
||||
if (targetUrl == null) {
|
||||
var setupContainer = Scheduler.GetSetupContainer(task?.Config!);
|
||||
targetUrl = new Uri(context.Containers.AuthDownloadUrl(setupContainer, ReplaceFirstSetup(report.Executable)));
|
||||
}
|
||||
|
||||
if (reportUrl == null) {
|
||||
reportUrl = new Uri(context.Containers.AuthDownloadUrl(container, filename));
|
||||
}
|
||||
|
||||
if (inputUrl == null && report.InputBlob != null) {
|
||||
inputUrl = new Uri(context.Containers.AuthDownloadUrl(report.InputBlob.Container, report.InputBlob.Name));
|
||||
}
|
||||
|
||||
return new Renderer(container, filename, report, task!, job!, targetUrl, inputUrl!, reportUrl);
|
||||
}
|
||||
public Renderer(Container container, string filename, Report report, Task task, Job job, Uri targetUrl, Uri inputUrl, Uri reportUrl) {
|
||||
_report = report;
|
||||
_container = container;
|
||||
_filename = filename;
|
||||
_taskConfig = task.Config;
|
||||
_jobConfig = job.Config;
|
||||
_reportUrl = reportUrl;
|
||||
_targetUrl = targetUrl;
|
||||
_inputUrl = inputUrl;
|
||||
}
|
||||
|
||||
// TODO: This function is fallible but the python
|
||||
// implementation doesn't have that so I'm trying to match it.
|
||||
// We should probably propagate any errors up
|
||||
public async Async.Task<string> Render(string templateString, Uri instanceUrl) {
|
||||
var template = Template.Parse(templateString);
|
||||
if (template != null) {
|
||||
return await template.RenderAsync(new {
|
||||
Report = this._report,
|
||||
Task = this._taskConfig,
|
||||
Job = this._jobConfig,
|
||||
ReportUrl = this._reportUrl,
|
||||
InputUrl = _inputUrl,
|
||||
TargetUrl = _targetUrl,
|
||||
ReportContainer = _container,
|
||||
ReportFilename = _filename,
|
||||
ReproCmd = $"onefuzz --endpoint {instanceUrl} repro create_and_connect {_container} {_filename}"
|
||||
});
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
124
src/ApiService/ApiService/onefuzzlib/notifications/Teams.cs
Normal file
124
src/ApiService/ApiService/onefuzzlib/notifications/Teams.cs
Normal file
@ -0,0 +1,124 @@
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public interface ITeams {
|
||||
Async.Task NotifyTeams(TeamsTemplate config, Container container, string filename, IReport reportOrRegression);
|
||||
}
|
||||
|
||||
public class Teams : ITeams {
|
||||
private readonly ILogTracer _logTracer;
|
||||
private readonly IOnefuzzContext _context;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
public Teams(IHttpClientFactory httpFactory, ILogTracer logTracer, IOnefuzzContext context) {
|
||||
_logTracer = logTracer;
|
||||
_context = context;
|
||||
_httpFactory = httpFactory;
|
||||
}
|
||||
|
||||
private static string CodeBlock(string data) {
|
||||
data = data.Replace("`", "``");
|
||||
return $"\n```\n{data}\n```\n";
|
||||
}
|
||||
|
||||
private async Async.Task SendTeamsWebhook(TeamsTemplate config, string title, IList<Dictionary<string, string>> facts, string? text) {
|
||||
title = MarkdownEscape(title);
|
||||
|
||||
var sections = new List<Dictionary<string, object>>() {
|
||||
new() {
|
||||
{"activityTitle", title},
|
||||
{"facts", facts}
|
||||
}
|
||||
};
|
||||
if (text != null) {
|
||||
sections.Add(new() {
|
||||
{ "text", text }
|
||||
});
|
||||
}
|
||||
|
||||
var message = new Dictionary<string, object>() {
|
||||
{"@type", "MessageCard"},
|
||||
{"@context", "https://schema.org/extensions"},
|
||||
{"summary", title},
|
||||
{"sections", sections}
|
||||
};
|
||||
|
||||
var configUrl = await _context.SecretsOperations.GetSecretStringValue(config.Url);
|
||||
var client = new Request(_httpFactory.CreateClient());
|
||||
var response = await client.Post(url: new Uri(configUrl!), JsonSerializer.Serialize(message));
|
||||
if (response == null || !response.IsSuccessStatusCode) {
|
||||
_logTracer.Error($"webhook failed {response?.StatusCode} {response?.Content}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Async.Task NotifyTeams(TeamsTemplate config, Container container, string filename, IReport reportOrRegression) {
|
||||
var facts = new List<Dictionary<string, string>>();
|
||||
string? text = null;
|
||||
var title = string.Empty;
|
||||
|
||||
if (reportOrRegression is Report report) {
|
||||
var task = await _context.TaskOperations.GetByJobIdAndTaskId(report.JobId, report.TaskId);
|
||||
if (task == null) {
|
||||
_logTracer.Error($"report with invalid task {report.JobId}:{report.TaskId}");
|
||||
return;
|
||||
}
|
||||
|
||||
title = $"new crash in {report.Executable}: {report.CrashType} @ {report.CrashSite}";
|
||||
|
||||
var links = new List<string> {
|
||||
$"[report]({_context.Containers.AuthDownloadUrl(container, filename)})"
|
||||
};
|
||||
|
||||
var setupContainer = Scheduler.GetSetupContainer(task.Config);
|
||||
if (setupContainer != null) {
|
||||
var setupFileName = NotificationsBase.ReplaceFirstSetup(report.Executable);
|
||||
links.Add(
|
||||
$"[executable]({_context.Containers.AuthDownloadUrl(setupContainer, setupFileName)})"
|
||||
);
|
||||
}
|
||||
|
||||
if (report.InputBlob != null) {
|
||||
links.Add(
|
||||
$"[input]({_context.Containers.AuthDownloadUrl(report.InputBlob.Container, report.InputBlob.Name)})"
|
||||
);
|
||||
}
|
||||
|
||||
facts.AddRange(new List<Dictionary<string, string>> {
|
||||
new() {{"name", "Files"}, {"value", string.Join(" | ", links)}},
|
||||
new() {
|
||||
{"name", "Task"},
|
||||
{"value", MarkdownEscape(
|
||||
$"job_id: {report.JobId} task_id: {report.TaskId}"
|
||||
)}
|
||||
},
|
||||
new() {
|
||||
{"name", "Repro"},
|
||||
{"value", CodeBlock($"onefuzz repro create_and_connect {container} {filename}")}
|
||||
}
|
||||
});
|
||||
|
||||
text = "## Call Stack\n" + string.Join("\n", report.CallStack.Select(cs => CodeBlock(cs)));
|
||||
} else {
|
||||
title = "new file found";
|
||||
var fileUrl = _context.Containers.AuthDownloadUrl(container, filename);
|
||||
|
||||
facts.Add(new Dictionary<string, string>() {
|
||||
{"name", "file"},
|
||||
{"value", $"[{MarkdownEscape(container.String)}/{MarkdownEscape(filename)}]({fileUrl})"}
|
||||
});
|
||||
}
|
||||
|
||||
await SendTeamsWebhook(config, title, facts, text);
|
||||
}
|
||||
|
||||
private static string MarkdownEscape(string data) {
|
||||
var values = "\\*_{}[]()#+-.!";
|
||||
foreach (var c in values) {
|
||||
data = data.Replace(c.ToString(), "\\" + c);
|
||||
}
|
||||
data = data.Replace("`", "``");
|
||||
return data;
|
||||
}
|
||||
}
|
@ -302,6 +302,25 @@
|
||||
"System.Text.Encodings.Web": "5.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundationServer.Client": {
|
||||
"type": "Direct",
|
||||
"requested": "[19.209.0-preview, )",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "dglVgITWfsps8pWA//2mBGVt/keD3UGdAVBXd50k9nVZiThUwWnaAoUzRf4fay/avLGXdvfkz6x9dBf6zGtfxg==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": "[19.209.0-preview]",
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.ComponentModel.Annotations": "4.4.1"
|
||||
}
|
||||
},
|
||||
"Octokit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.1, )",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "JVlfUY+sfItl6RSyVKDJTutuy28cDydUwKKfzcelwNMor2Sa18pYVKna6phO8lug1b+ep+pcuFh/FPayuImsQw=="
|
||||
},
|
||||
"Scriban": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.5.0, )",
|
||||
@ -399,6 +418,15 @@
|
||||
"System.Diagnostics.DiagnosticSource": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.2.7",
|
||||
"contentHash": "/76fAHknzvFqbznS6Uj2sOyE9rJB3PltY+f53TH8dX9RiGhk02EhuFCWljSj5nnqKaTsmma8DFR50OGyQ4yJ1g==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "10.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Cryptography.Internal": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.8",
|
||||
@ -869,6 +897,30 @@
|
||||
"Newtonsoft.Json": "10.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "32lLZPU8pZg+mVfA2smHso6fhWPSFXJPPyawvOFsFoNz9Yj5y2fsAR7O4zPwE3c/z2zzi8BMfiXRKOcbW6cdIg==",
|
||||
"dependencies": {
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]"
|
||||
}
|
||||
},
|
||||
"Microsoft.VisualStudio.Services.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "eQWZb5BhtOgywARvfHGGZsYuuZvFmJiXyE7P/EqKTLUplrUFmSVxo0J/KUC8GWJWmdarxH2vXZTAz9uW7BwRDQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.Configuration.ConfigurationManager": "4.4.1",
|
||||
"System.Data.SqlClient": "4.4.2",
|
||||
"System.Security.Cryptography.Cng": "4.4.0",
|
||||
"System.Security.Cryptography.OpenSsl": "4.4.0",
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.1",
|
||||
"System.Xml.XPath.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -949,15 +1001,16 @@
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.3",
|
||||
"contentHash": "hSXaFmh7hNCuEoC4XNY5DrRkLDzYHqPx/Ik23R4J86Z7PE/Y6YidhG602dFVdLBRSdG6xp9NabH3dXpcoxWvww==",
|
||||
"resolved": "12.0.3",
|
||||
"contentHash": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": "4.3.0",
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"System.ComponentModel.TypeConverter": "4.3.0",
|
||||
"System.Runtime.Serialization.Formatters": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
"Newtonsoft.Json": "10.0.1"
|
||||
}
|
||||
},
|
||||
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||
@ -984,6 +1037,16 @@
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==",
|
||||
"dependencies": {
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.IO.Compression": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1067,6 +1130,21 @@
|
||||
"resolved": "4.3.2",
|
||||
"contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg=="
|
||||
},
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
|
||||
},
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
|
||||
},
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
|
||||
},
|
||||
"System.AppContext": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1114,71 +1192,17 @@
|
||||
"System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Collections.NonGeneric": {
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0"
|
||||
}
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "ToiYqSCioqhtspq2O/jYKtyTC/T0uwWHBTYlzCi6PRbSSHArN1IaRWeHffDamvms5sye5FDUWCfNZgubQpNRsA=="
|
||||
},
|
||||
"System.Collections.Specialized": {
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==",
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "jz3TWKMAeuDEyrPCK5Jyt4bzQcmzUIMcY9Ud6PkElFxTfnsihV+9N/UCqvxe1z5gc7jMYAnj7V1COMS9QKIuHQ==",
|
||||
"dependencies": {
|
||||
"System.Collections.NonGeneric": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Globalization.Extensions": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==",
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==",
|
||||
"dependencies": {
|
||||
"System.ComponentModel": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.TypeConverter": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Collections.NonGeneric": "4.3.0",
|
||||
"System.Collections.Specialized": "4.3.0",
|
||||
"System.ComponentModel": "4.3.0",
|
||||
"System.ComponentModel.Primitives": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Linq": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Extensions": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Reflection.TypeExtensions": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0"
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Console": {
|
||||
@ -1193,6 +1217,17 @@
|
||||
"System.Text.Encoding": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Data.SqlClient": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.2",
|
||||
"contentHash": "Bv5J2EBAdP7FSgehKYN4O6iw1AaZrw4rFFqwt9vZSjRvC70FpwP2d9UG4aTaI2wh3vfrBKK+tjewowGM2Y6c1w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.Registry": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.0",
|
||||
"System.Text.Encoding.CodePages": "4.4.0",
|
||||
"runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.Debug": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1646,27 +1681,6 @@
|
||||
"System.Runtime.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Formatters": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
|
||||
"dependencies": {
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
@ -1760,22 +1774,10 @@
|
||||
},
|
||||
"System.Security.Cryptography.OpenSsl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "is11qLXIHKIvbTipyB1an8FT1ZKavmgf/qJUSIz7ZP830ALRRhPSt5NhplW0/wMk0tNDQWQLluVap6HsQN4HMg==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"System.Runtime.InteropServices": "4.3.0",
|
||||
"System.Runtime.Numerics": "4.3.0",
|
||||
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Cryptography.Pkcs": {
|
||||
@ -1879,6 +1881,14 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "6JX7ZdaceBiLKLkYt8zJcp4xTJd1uYyXXEkPw6mnlUIjh1gZPIVKPtRXPmY5kLf6DwZmf5YLwR3QUrRonl7l0A==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -2014,6 +2024,39 @@
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath.XmlDocument": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "A/uxsWi/Ifzkmd4ArTLISMbfFs6XpRPsXZonrIqyTY70xi8t+mDtvSM5Os0RqyRDobjMBwIDHDL4NOIbkDwf7A==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0",
|
||||
"System.Xml.XPath": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,9 @@ public sealed class TestContext : IOnefuzzContext {
|
||||
public ISubnet Subnet => throw new NotImplementedException();
|
||||
|
||||
public IImageOperations ImageOperations => throw new NotImplementedException();
|
||||
public ITeams Teams => throw new NotImplementedException();
|
||||
public IGithubIssues GithubIssues => throw new NotImplementedException();
|
||||
public IAdo Ado => throw new NotImplementedException();
|
||||
|
||||
|
||||
}
|
||||
|
@ -281,6 +281,15 @@
|
||||
"System.Diagnostics.DiagnosticSource": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.2.7",
|
||||
"contentHash": "/76fAHknzvFqbznS6Uj2sOyE9rJB3PltY+f53TH8dX9RiGhk02EhuFCWljSj5nnqKaTsmma8DFR50OGyQ4yJ1g==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "10.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Cryptography.Internal": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.8",
|
||||
@ -878,6 +887,26 @@
|
||||
"Newtonsoft.Json": "10.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "32lLZPU8pZg+mVfA2smHso6fhWPSFXJPPyawvOFsFoNz9Yj5y2fsAR7O4zPwE3c/z2zzi8BMfiXRKOcbW6cdIg==",
|
||||
"dependencies": {
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundationServer.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "dglVgITWfsps8pWA//2mBGVt/keD3UGdAVBXd50k9nVZiThUwWnaAoUzRf4fay/avLGXdvfkz6x9dBf6zGtfxg==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": "[19.209.0-preview]",
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.ComponentModel.Annotations": "4.4.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.1.0",
|
||||
@ -896,6 +925,22 @@
|
||||
"Newtonsoft.Json": "9.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.VisualStudio.Services.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "eQWZb5BhtOgywARvfHGGZsYuuZvFmJiXyE7P/EqKTLUplrUFmSVxo0J/KUC8GWJWmdarxH2vXZTAz9uW7BwRDQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.Configuration.ConfigurationManager": "4.4.1",
|
||||
"System.Data.SqlClient": "4.4.2",
|
||||
"System.Security.Cryptography.Cng": "4.4.0",
|
||||
"System.Security.Cryptography.OpenSsl": "4.4.0",
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.1",
|
||||
"System.Xml.XPath.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -976,15 +1021,16 @@
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.3",
|
||||
"contentHash": "hSXaFmh7hNCuEoC4XNY5DrRkLDzYHqPx/Ik23R4J86Z7PE/Y6YidhG602dFVdLBRSdG6xp9NabH3dXpcoxWvww==",
|
||||
"resolved": "12.0.3",
|
||||
"contentHash": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": "4.3.0",
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"System.ComponentModel.TypeConverter": "4.3.0",
|
||||
"System.Runtime.Serialization.Formatters": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
"Newtonsoft.Json": "10.0.1"
|
||||
}
|
||||
},
|
||||
"NuGet.Frameworks": {
|
||||
@ -992,6 +1038,11 @@
|
||||
"resolved": "5.11.0",
|
||||
"contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q=="
|
||||
},
|
||||
"Octokit": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "JVlfUY+sfItl6RSyVKDJTutuy28cDydUwKKfzcelwNMor2Sa18pYVKna6phO8lug1b+ep+pcuFh/FPayuImsQw=="
|
||||
},
|
||||
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.2",
|
||||
@ -1016,6 +1067,16 @@
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==",
|
||||
"dependencies": {
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.IO.Compression": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1099,6 +1160,21 @@
|
||||
"resolved": "4.3.2",
|
||||
"contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg=="
|
||||
},
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
|
||||
},
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
|
||||
},
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
|
||||
},
|
||||
"Scriban": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.5.0",
|
||||
@ -1191,6 +1267,11 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "ToiYqSCioqhtspq2O/jYKtyTC/T0uwWHBTYlzCi6PRbSSHArN1IaRWeHffDamvms5sye5FDUWCfNZgubQpNRsA=="
|
||||
},
|
||||
"System.ComponentModel.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1225,8 +1306,8 @@
|
||||
},
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==",
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "jz3TWKMAeuDEyrPCK5Jyt4bzQcmzUIMcY9Ud6PkElFxTfnsihV+9N/UCqvxe1z5gc7jMYAnj7V1COMS9QKIuHQ==",
|
||||
"dependencies": {
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0"
|
||||
}
|
||||
@ -1243,6 +1324,17 @@
|
||||
"System.Text.Encoding": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Data.SqlClient": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.2",
|
||||
"contentHash": "Bv5J2EBAdP7FSgehKYN4O6iw1AaZrw4rFFqwt9vZSjRvC70FpwP2d9UG4aTaI2wh3vfrBKK+tjewowGM2Y6c1w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.Registry": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.0",
|
||||
"System.Text.Encoding.CodePages": "4.4.0",
|
||||
"runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.Debug": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1755,27 +1847,6 @@
|
||||
"System.Runtime.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Formatters": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
|
||||
"dependencies": {
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
@ -1869,22 +1940,10 @@
|
||||
},
|
||||
"System.Security.Cryptography.OpenSsl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "is11qLXIHKIvbTipyB1an8FT1ZKavmgf/qJUSIz7ZP830ALRRhPSt5NhplW0/wMk0tNDQWQLluVap6HsQN4HMg==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"System.Runtime.InteropServices": "4.3.0",
|
||||
"System.Runtime.Numerics": "4.3.0",
|
||||
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Cryptography.Pkcs": {
|
||||
@ -1988,6 +2047,14 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "6JX7ZdaceBiLKLkYt8zJcp4xTJd1uYyXXEkPw6mnlUIjh1gZPIVKPtRXPmY5kLf6DwZmf5YLwR3QUrRonl7l0A==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -2124,6 +2191,39 @@
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath.XmlDocument": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "A/uxsWi/Ifzkmd4ArTLISMbfFs6XpRPsXZonrIqyTY70xi8t+mDtvSM5Os0RqyRDobjMBwIDHDL4NOIbkDwf7A==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0",
|
||||
"System.Xml.XPath": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"TaskTupleAwaiter": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.0",
|
||||
@ -2177,39 +2277,41 @@
|
||||
"apiservice": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Azure.Core": "[1.25.0, )",
|
||||
"Azure.Data.Tables": "[12.5.0, )",
|
||||
"Azure.Identity": "[1.6.0, )",
|
||||
"Azure.Messaging.EventGrid": "[4.10.0, )",
|
||||
"Azure.ResourceManager": "[1.3.1, )",
|
||||
"Azure.ResourceManager.Compute": "[1.0.0-beta.8, )",
|
||||
"Azure.ResourceManager.Monitor": "[1.0.0-beta.2, )",
|
||||
"Azure.ResourceManager.Network": "[1.0.0, )",
|
||||
"Azure.ResourceManager.Resources": "[1.3.0, )",
|
||||
"Azure.ResourceManager.Storage": "[1.0.0-beta.11, )",
|
||||
"Azure.Security.KeyVault.Secrets": "[4.3.0, )",
|
||||
"Azure.Storage.Blobs": "[12.13.0, )",
|
||||
"Azure.Storage.Queues": "[12.11.0, )",
|
||||
"Faithlife.Utility": "[0.12.2, )",
|
||||
"Microsoft.ApplicationInsights.DependencyCollector": "[2.21.0, )",
|
||||
"Microsoft.Azure.Functions.Worker": "[1.6.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "[2.1.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Http": "[3.0.13, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.SignalRService": "[1.7.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Storage": "[5.0.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Timer": "[4.1.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Sdk": "[1.3.0, )",
|
||||
"Microsoft.Azure.Management.Monitor": "[0.28.0-preview, )",
|
||||
"Microsoft.Azure.Management.OperationalInsights": "[0.24.0-preview, )",
|
||||
"Microsoft.Extensions.Logging.ApplicationInsights": "[2.21.0, )",
|
||||
"Microsoft.Graph": "[4.37.0, )",
|
||||
"Microsoft.Identity.Client": "[4.46.2, )",
|
||||
"Microsoft.Identity.Web.TokenCache": "[1.23.1, )",
|
||||
"Scriban": "[5.5.0, )",
|
||||
"Semver": "[2.1.0, )",
|
||||
"System.IdentityModel.Tokens.Jwt": "[6.22.1, )",
|
||||
"System.Linq.Async": "[6.0.1, )",
|
||||
"TaskTupleAwaiter": "[2.0.0, )"
|
||||
"Azure.Core": "1.25.0",
|
||||
"Azure.Data.Tables": "12.5.0",
|
||||
"Azure.Identity": "1.6.0",
|
||||
"Azure.Messaging.EventGrid": "4.10.0",
|
||||
"Azure.ResourceManager": "1.3.1",
|
||||
"Azure.ResourceManager.Compute": "1.0.0-beta.8",
|
||||
"Azure.ResourceManager.Monitor": "1.0.0-beta.2",
|
||||
"Azure.ResourceManager.Network": "1.0.0",
|
||||
"Azure.ResourceManager.Resources": "1.3.0",
|
||||
"Azure.ResourceManager.Storage": "1.0.0-beta.11",
|
||||
"Azure.Security.KeyVault.Secrets": "4.3.0",
|
||||
"Azure.Storage.Blobs": "12.13.0",
|
||||
"Azure.Storage.Queues": "12.11.0",
|
||||
"Faithlife.Utility": "0.12.2",
|
||||
"Microsoft.ApplicationInsights.DependencyCollector": "2.21.0",
|
||||
"Microsoft.Azure.Functions.Worker": "1.6.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "2.1.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Http": "3.0.13",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.SignalRService": "1.7.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Storage": "5.0.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Timer": "4.1.0",
|
||||
"Microsoft.Azure.Functions.Worker.Sdk": "1.3.0",
|
||||
"Microsoft.Azure.Management.Monitor": "0.28.0-preview",
|
||||
"Microsoft.Azure.Management.OperationalInsights": "0.24.0-preview",
|
||||
"Microsoft.Extensions.Logging.ApplicationInsights": "2.21.0",
|
||||
"Microsoft.Graph": "4.37.0",
|
||||
"Microsoft.Identity.Client": "4.46.2",
|
||||
"Microsoft.Identity.Web.TokenCache": "1.23.1",
|
||||
"Microsoft.TeamFoundationServer.Client": "19.209.0-preview",
|
||||
"Octokit": "2.0.1",
|
||||
"Scriban": "5.5.0",
|
||||
"Semver": "2.1.0",
|
||||
"System.IdentityModel.Tokens.Jwt": "6.22.1",
|
||||
"System.Linq.Async": "6.0.1",
|
||||
"TaskTupleAwaiter": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,11 +368,87 @@ namespace Tests {
|
||||
where Container.TryParse(nameString, out var _)
|
||||
select Container.Parse(nameString);
|
||||
|
||||
public static Gen<ADODuplicateTemplate> AdoDuplicateTemplate() {
|
||||
return Arb.Generate<Tuple<List<string>, Dictionary<string, string>, string?>>().Select(
|
||||
arg =>
|
||||
new ADODuplicateTemplate(
|
||||
arg.Item1,
|
||||
arg.Item2,
|
||||
arg.Item2,
|
||||
arg.Item3
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<AdoTemplate> AdoTemplate() {
|
||||
return Arb.Generate<Tuple<Uri, SecretData<string>, NonEmptyString, List<string>, Dictionary<string, string>, ADODuplicateTemplate, string?>>().Select(
|
||||
arg =>
|
||||
new AdoTemplate(
|
||||
arg.Item1,
|
||||
arg.Item2,
|
||||
arg.Item3.Item,
|
||||
arg.Item3.Item,
|
||||
arg.Item4,
|
||||
arg.Item5,
|
||||
AdoDuplicateTemplate().Sample(1, 1).First(),
|
||||
arg.Item7
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<TeamsTemplate> TeamsTemplate() {
|
||||
return Arb.Generate<Tuple<SecretData<string>>>().Select(
|
||||
arg =>
|
||||
new TeamsTemplate(
|
||||
arg.Item1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<GithubAuth> GithubAuth() {
|
||||
return Arb.Generate<Tuple<string>>().Select(
|
||||
arg =>
|
||||
new GithubAuth(
|
||||
arg.Item1,
|
||||
arg.Item1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<GithubIssueSearch> GithubIssueSearch() {
|
||||
return Arb.Generate<Tuple<List<GithubIssueSearchMatch>, string, string?, GithubIssueState?>>().Select(
|
||||
arg =>
|
||||
new GithubIssueSearch(
|
||||
arg.Item1,
|
||||
arg.Item2,
|
||||
arg.Item3,
|
||||
arg.Item4
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<GithubIssuesTemplate> GithubIssuesTemplate() {
|
||||
return Arb.Generate<Tuple<SecretData<GithubAuth>, NonEmptyString, GithubIssueSearch, List<string>, GithubIssueDuplicate>>().Select(
|
||||
arg =>
|
||||
new GithubIssuesTemplate(
|
||||
arg.Item1,
|
||||
arg.Item2.Item,
|
||||
arg.Item2.Item,
|
||||
arg.Item2.Item,
|
||||
arg.Item2.Item,
|
||||
arg.Item3,
|
||||
arg.Item4,
|
||||
arg.Item4,
|
||||
arg.Item5
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static Gen<NotificationTemplate> NotificationTemplate() {
|
||||
return Gen.OneOf(new[] {
|
||||
Arb.Generate<AdoTemplate>().Select(e => e as NotificationTemplate),
|
||||
Arb.Generate<TeamsTemplate>().Select(e => e as NotificationTemplate),
|
||||
Arb.Generate<GithubIssuesTemplate>().Select(e => e as NotificationTemplate)
|
||||
AdoTemplate().Select(a => a as NotificationTemplate),
|
||||
TeamsTemplate().Select(e => e as NotificationTemplate),
|
||||
GithubIssuesTemplate().Select(e => e as NotificationTemplate)
|
||||
});
|
||||
}
|
||||
|
||||
@ -1006,17 +1082,15 @@ namespace Tests {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
//Sample function on how repro a failing test run, using Replay
|
||||
//functionality of FsCheck. Feel free to
|
||||
[Property]
|
||||
void Replay()
|
||||
{
|
||||
var seed = FsCheck.Random.StdGen.NewStdGen(4570702, 297027754);
|
||||
var p = Prop.ForAll((WebhookMessageEventGrid x) => WebhookMessageEventGrid(x) );
|
||||
void Replay() {
|
||||
var seed = FsCheck.Random.StdGen.NewStdGen(811038773, 297085737);
|
||||
var p = Prop.ForAll((NotificationTemplate x) => NotificationTemplate(x));
|
||||
p.Check(new Configuration { Replay = seed });
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.OneFuzz.Service;
|
||||
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
|
||||
using Scriban;
|
||||
using Xunit;
|
||||
|
||||
@ -115,6 +117,19 @@ public class TemplateTests {
|
||||
output.Should().NotContainAny(report.CallStack);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TemplatesShouldDeserializeAppropriately() {
|
||||
var teamsTemplate = @"{""url"": {""secret"": {""url"": ""https://example.com""}}}";
|
||||
var template = JsonSerializer.Deserialize<NotificationTemplate>(teamsTemplate, EntityConverter.GetJsonSerializerOptions());
|
||||
var a = template is AdoTemplate;
|
||||
var t = template is TeamsTemplate;
|
||||
var g = template is GithubIssuesTemplate;
|
||||
|
||||
a.Should().BeFalse();
|
||||
t.Should().BeTrue();
|
||||
g.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static Report GetReport() {
|
||||
return new Report(
|
||||
"https://example.com",
|
||||
|
@ -330,6 +330,15 @@
|
||||
"System.Diagnostics.DiagnosticSource": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.2.7",
|
||||
"contentHash": "/76fAHknzvFqbznS6Uj2sOyE9rJB3PltY+f53TH8dX9RiGhk02EhuFCWljSj5nnqKaTsmma8DFR50OGyQ4yJ1g==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "10.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Cryptography.Internal": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.8",
|
||||
@ -927,6 +936,26 @@
|
||||
"Newtonsoft.Json": "10.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "32lLZPU8pZg+mVfA2smHso6fhWPSFXJPPyawvOFsFoNz9Yj5y2fsAR7O4zPwE3c/z2zzi8BMfiXRKOcbW6cdIg==",
|
||||
"dependencies": {
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]"
|
||||
}
|
||||
},
|
||||
"Microsoft.TeamFoundationServer.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "dglVgITWfsps8pWA//2mBGVt/keD3UGdAVBXd50k9nVZiThUwWnaAoUzRf4fay/avLGXdvfkz6x9dBf6zGtfxg==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Microsoft.TeamFoundation.DistributedTask.Common.Contracts": "[19.209.0-preview]",
|
||||
"Microsoft.VisualStudio.Services.Client": "[19.209.0-preview]",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.ComponentModel.Annotations": "4.4.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.1.0",
|
||||
@ -945,6 +974,22 @@
|
||||
"Newtonsoft.Json": "9.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.VisualStudio.Services.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "19.209.0-preview",
|
||||
"contentHash": "eQWZb5BhtOgywARvfHGGZsYuuZvFmJiXyE7P/EqKTLUplrUFmSVxo0J/KUC8GWJWmdarxH2vXZTAz9uW7BwRDQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.WebApi.Client": "5.2.7",
|
||||
"Newtonsoft.Json": "12.0.3",
|
||||
"System.Configuration.ConfigurationManager": "4.4.1",
|
||||
"System.Data.SqlClient": "4.4.2",
|
||||
"System.Security.Cryptography.Cng": "4.4.0",
|
||||
"System.Security.Cryptography.OpenSsl": "4.4.0",
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.1",
|
||||
"System.Xml.XPath.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1025,15 +1070,16 @@
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.3",
|
||||
"contentHash": "hSXaFmh7hNCuEoC4XNY5DrRkLDzYHqPx/Ik23R4J86Z7PE/Y6YidhG602dFVdLBRSdG6xp9NabH3dXpcoxWvww==",
|
||||
"resolved": "12.0.3",
|
||||
"contentHash": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": "4.3.0",
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"System.ComponentModel.TypeConverter": "4.3.0",
|
||||
"System.Runtime.Serialization.Formatters": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
"Newtonsoft.Json": "10.0.1"
|
||||
}
|
||||
},
|
||||
"NuGet.Frameworks": {
|
||||
@ -1041,6 +1087,11 @@
|
||||
"resolved": "5.11.0",
|
||||
"contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q=="
|
||||
},
|
||||
"Octokit": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.1",
|
||||
"contentHash": "JVlfUY+sfItl6RSyVKDJTutuy28cDydUwKKfzcelwNMor2Sa18pYVKna6phO8lug1b+ep+pcuFh/FPayuImsQw=="
|
||||
},
|
||||
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.2",
|
||||
@ -1065,6 +1116,16 @@
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==",
|
||||
"dependencies": {
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"runtime.native.System.IO.Compression": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1148,6 +1209,21 @@
|
||||
"resolved": "4.3.2",
|
||||
"contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg=="
|
||||
},
|
||||
"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
|
||||
},
|
||||
"runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
|
||||
},
|
||||
"runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
|
||||
},
|
||||
"Scriban": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.5.0",
|
||||
@ -1240,6 +1316,11 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "ToiYqSCioqhtspq2O/jYKtyTC/T0uwWHBTYlzCi6PRbSSHArN1IaRWeHffDamvms5sye5FDUWCfNZgubQpNRsA=="
|
||||
},
|
||||
"System.ComponentModel.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1274,8 +1355,8 @@
|
||||
},
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==",
|
||||
"resolved": "4.4.1",
|
||||
"contentHash": "jz3TWKMAeuDEyrPCK5Jyt4bzQcmzUIMcY9Ud6PkElFxTfnsihV+9N/UCqvxe1z5gc7jMYAnj7V1COMS9QKIuHQ==",
|
||||
"dependencies": {
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0"
|
||||
}
|
||||
@ -1292,6 +1373,17 @@
|
||||
"System.Text.Encoding": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Data.SqlClient": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.2",
|
||||
"contentHash": "Bv5J2EBAdP7FSgehKYN4O6iw1AaZrw4rFFqwt9vZSjRvC70FpwP2d9UG4aTaI2wh3vfrBKK+tjewowGM2Y6c1w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.Registry": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.4.0",
|
||||
"System.Text.Encoding.CodePages": "4.4.0",
|
||||
"runtime.native.System.Data.SqlClient.sni": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.Debug": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -1850,27 +1942,6 @@
|
||||
"System.Runtime.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Formatters": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Serialization.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==",
|
||||
"dependencies": {
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
@ -1964,22 +2035,10 @@
|
||||
},
|
||||
"System.Security.Cryptography.OpenSsl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "is11qLXIHKIvbTipyB1an8FT1ZKavmgf/qJUSIz7ZP830ALRRhPSt5NhplW0/wMk0tNDQWQLluVap6HsQN4HMg==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"System.Runtime.InteropServices": "4.3.0",
|
||||
"System.Runtime.Numerics": "4.3.0",
|
||||
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Cryptography.Pkcs": {
|
||||
@ -2083,6 +2142,14 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "6JX7ZdaceBiLKLkYt8zJcp4xTJd1uYyXXEkPw6mnlUIjh1gZPIVKPtRXPmY5kLf6DwZmf5YLwR3QUrRonl7l0A==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@ -2251,6 +2318,39 @@
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XPath.XmlDocument": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "A/uxsWi/Ifzkmd4ArTLISMbfFs6XpRPsXZonrIqyTY70xi8t+mDtvSM5Os0RqyRDobjMBwIDHDL4NOIbkDwf7A==",
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0",
|
||||
"System.Xml.XPath": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"TaskTupleAwaiter": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.0",
|
||||
@ -2304,39 +2404,41 @@
|
||||
"apiservice": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Azure.Core": "[1.25.0, )",
|
||||
"Azure.Data.Tables": "[12.5.0, )",
|
||||
"Azure.Identity": "[1.6.0, )",
|
||||
"Azure.Messaging.EventGrid": "[4.10.0, )",
|
||||
"Azure.ResourceManager": "[1.3.1, )",
|
||||
"Azure.ResourceManager.Compute": "[1.0.0-beta.8, )",
|
||||
"Azure.ResourceManager.Monitor": "[1.0.0-beta.2, )",
|
||||
"Azure.ResourceManager.Network": "[1.0.0, )",
|
||||
"Azure.ResourceManager.Resources": "[1.3.0, )",
|
||||
"Azure.ResourceManager.Storage": "[1.0.0-beta.11, )",
|
||||
"Azure.Security.KeyVault.Secrets": "[4.3.0, )",
|
||||
"Azure.Storage.Blobs": "[12.13.0, )",
|
||||
"Azure.Storage.Queues": "[12.11.0, )",
|
||||
"Faithlife.Utility": "[0.12.2, )",
|
||||
"Microsoft.ApplicationInsights.DependencyCollector": "[2.21.0, )",
|
||||
"Microsoft.Azure.Functions.Worker": "[1.6.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "[2.1.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Http": "[3.0.13, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.SignalRService": "[1.7.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Storage": "[5.0.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Timer": "[4.1.0, )",
|
||||
"Microsoft.Azure.Functions.Worker.Sdk": "[1.3.0, )",
|
||||
"Microsoft.Azure.Management.Monitor": "[0.28.0-preview, )",
|
||||
"Microsoft.Azure.Management.OperationalInsights": "[0.24.0-preview, )",
|
||||
"Microsoft.Extensions.Logging.ApplicationInsights": "[2.21.0, )",
|
||||
"Microsoft.Graph": "[4.37.0, )",
|
||||
"Microsoft.Identity.Client": "[4.46.2, )",
|
||||
"Microsoft.Identity.Web.TokenCache": "[1.23.1, )",
|
||||
"Scriban": "[5.5.0, )",
|
||||
"Semver": "[2.1.0, )",
|
||||
"System.IdentityModel.Tokens.Jwt": "[6.22.1, )",
|
||||
"System.Linq.Async": "[6.0.1, )",
|
||||
"TaskTupleAwaiter": "[2.0.0, )"
|
||||
"Azure.Core": "1.25.0",
|
||||
"Azure.Data.Tables": "12.5.0",
|
||||
"Azure.Identity": "1.6.0",
|
||||
"Azure.Messaging.EventGrid": "4.10.0",
|
||||
"Azure.ResourceManager": "1.3.1",
|
||||
"Azure.ResourceManager.Compute": "1.0.0-beta.8",
|
||||
"Azure.ResourceManager.Monitor": "1.0.0-beta.2",
|
||||
"Azure.ResourceManager.Network": "1.0.0",
|
||||
"Azure.ResourceManager.Resources": "1.3.0",
|
||||
"Azure.ResourceManager.Storage": "1.0.0-beta.11",
|
||||
"Azure.Security.KeyVault.Secrets": "4.3.0",
|
||||
"Azure.Storage.Blobs": "12.13.0",
|
||||
"Azure.Storage.Queues": "12.11.0",
|
||||
"Faithlife.Utility": "0.12.2",
|
||||
"Microsoft.ApplicationInsights.DependencyCollector": "2.21.0",
|
||||
"Microsoft.Azure.Functions.Worker": "1.6.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.EventGrid": "2.1.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Http": "3.0.13",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.SignalRService": "1.7.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Storage": "5.0.0",
|
||||
"Microsoft.Azure.Functions.Worker.Extensions.Timer": "4.1.0",
|
||||
"Microsoft.Azure.Functions.Worker.Sdk": "1.3.0",
|
||||
"Microsoft.Azure.Management.Monitor": "0.28.0-preview",
|
||||
"Microsoft.Azure.Management.OperationalInsights": "0.24.0-preview",
|
||||
"Microsoft.Extensions.Logging.ApplicationInsights": "2.21.0",
|
||||
"Microsoft.Graph": "4.37.0",
|
||||
"Microsoft.Identity.Client": "4.46.2",
|
||||
"Microsoft.Identity.Web.TokenCache": "1.23.1",
|
||||
"Microsoft.TeamFoundationServer.Client": "19.209.0-preview",
|
||||
"Octokit": "2.0.1",
|
||||
"Scriban": "5.5.0",
|
||||
"Semver": "2.1.0",
|
||||
"System.IdentityModel.Tokens.Jwt": "6.22.1",
|
||||
"System.Linq.Async": "6.0.1",
|
||||
"TaskTupleAwaiter": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user