- Add workspaces (gated behind configuration)
- Switch from handlers to providers for DI
- Fix for #597
This commit is contained in:
Guy Fankam 2024-07-29 11:21:48 -04:00
Родитель e7b1414f62 5b4b148094
Коммит bee3a2f378
169 изменённых файлов: 21417 добавлений и 13515 удалений

Просмотреть файл

@ -7,8 +7,8 @@ internal static class Program
{
var builder = DistributedApplication.CreateBuilder(args);
//builder.AddProject<extractor>("extractor");
builder.AddProject<integration_tests>("integration-tests");
//.WithEnvironment("CSCHECK_SEED", "0000KOIPe036");
builder.Build().Run();
}

Просмотреть файл

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
@ -9,11 +9,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.1" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\extractor\extractor.csproj" />
<ProjectReference Include="..\integration.tests\integration.tests.csproj" />
<ProjectReference Include="..\publisher\publisher.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -8,23 +8,26 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
namespace common.tests;
public static class Generator
{
public static Gen<Internet> Internet { get; } = Gen.Const(new Internet());
public static Gen<Internet> Internet { get; } =
new GenBogusDataSet<Internet>(() => new Internet());
public static Gen<Address> Address { get; } = Gen.Const(new Address());
public static Gen<Address> Address { get; } =
new GenBogusDataSet<Address>(() => new Address());
public static Gen<Lorem> Lorem { get; } = Gen.Const(new Lorem());
public static Gen<Lorem> Lorem { get; } =
new GenBogusDataSet<Lorem>(() => new Lorem());
public static Gen<Uri> AbsoluteUri { get; } =
from internet in Internet
select new Uri(internet.Url());
public static Gen<Bogus.DataSets.System> BogusSystem { get; } = Gen.Const(new Bogus.DataSets.System());
public static Gen<Bogus.DataSets.System> BogusSystem { get; } =
new GenBogusDataSet<Bogus.DataSets.System>(() => new Bogus.DataSets.System());
public static Gen<DirectoryInfo> DirectoryInfo { get; } =
from system in BogusSystem
@ -34,73 +37,34 @@ public static class Generator
from system in BogusSystem
select new FileInfo(system.FilePath());
public static Gen<string> FileName { get; } =
from system in BogusSystem
select system.FileName();
public static Gen<string> NonEmptyString { get; } =
Gen.String.Where(x => string.IsNullOrWhiteSpace(x) is false);
public static Gen<JsonValue> JsonValue { get; } = GenerateJsonValue();
public static Gen<JsonObject> JsonObject { get; } = GenerateJsonObject();
public static Gen<JsonArray> JsonArray { get; } = GenerateJsonArray();
public static Gen<JsonNode> JsonNode { get; } = GenerateJsonNode();
private static Gen<JsonValue> GenerateJsonValue() =>
Gen.OneOf(Gen.Bool.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Byte.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Char.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.DateTime.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.DateTimeOffset.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Decimal.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Double.Where(double.IsFinite).Where(x => double.IsNaN(x) is false).Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Float.Where(float.IsFinite).Where(x => float.IsNaN(x) is false).Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Guid.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Int.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Long.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.SByte.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.Short.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.String.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.UInt.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.ULong.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)),
Gen.UShort.Select(x => System.Text.Json.Nodes.JsonValue.Create(x)));
private static Gen<JsonObject> GenerateJsonObject() =>
GenerateJsonObject(GenerateJsonNode(), limits: 100);
private static Gen<JsonObject> GenerateJsonObject(Gen<JsonNode> nodeGen, uint limits) =>
Gen.Dictionary(Gen.String.AlphaNumeric,
nodeGen.Null())[0, (int)limits]
.Select(x => new JsonObject(x));
private static Gen<JsonArray> GenerateJsonArray() =>
GenerateJsonArray(GenerateJsonNode(), limits: 100);
private static Gen<JsonArray> GenerateJsonArray(Gen<JsonNode> nodeGen, uint limits) =>
nodeGen.Null()
.Array[0, (int)limits]
.Select(x => new JsonArray(x));
private static Gen<JsonNode> GenerateJsonNode() =>
Gen.Recursive<JsonNode>((depth, gen) =>
depth == 3
? GenerateJsonValue().Select(x => x as JsonNode)
: Gen.OneOf(GenerateJsonValue().Select(x => x as JsonNode),
GenerateJsonObject(gen, limits: (uint)depth).Select(x => x as JsonNode),
GenerateJsonArray(gen, limits: (uint)depth).Select(x => x as JsonNode)));
public static Gen<string> AlphaNumericStringBetween(int minimumLength, int maximumLength) =>
Gen.Char
.AlphaNumeric
.Array[minimumLength, maximumLength]
.Select(x => new string(x));
public static Gen<string> AlphaNumericStringWithLength(int length) =>
Gen.Char.AlphaNumeric.Array[length].Select(x => new string(x));
public static Gen<string> AlphaNumericStringBetween(int minimumLength, int maximumLength) =>
Gen.Char.AlphaNumeric.Array[minimumLength, maximumLength].Select(x => new string(x));
Gen.Char
.AlphaNumeric
.Array[length]
.Select(x => new string(x));
public static Gen<ImmutableArray<T>> ImmutableArrayOf<T>(this Gen<T> gen) =>
gen.List.Select(x => x.ToImmutableArray());
gen.List
.Select(x => x.ToImmutableArray());
public static Gen<ImmutableArray<T>> ImmutableArrayOf<T>(this Gen<T> gen, int minimumCount, int maximumCount) =>
gen.List[minimumCount, maximumCount].Select(x => x.ToImmutableArray());
gen.List[minimumCount, maximumCount]
.Select(x => x.ToImmutableArray());
public static Gen<ImmutableArray<T>> SubImmutableArrayOf<T>(IEnumerable<T> enumerable)
public static Gen<ImmutableArray<T>> SubImmutableArrayOf<T>(ICollection<T> enumerable)
{
var array = enumerable.ToArray();
@ -108,84 +72,54 @@ public static class Generator
select items.ToImmutableArray();
}
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen) =>
gen.List.Select(x => x.ToFrozenSet());
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen, int minimumCount, int maximumCount)
public static Gen<FrozenSet<T>> FrozenSetOf<T, TKey>(this Gen<T> gen, Func<T, TKey> keySelector, int minimumCount, int maximumCount)
{
if (maximumCount < minimumCount)
{
throw new ArgumentException("Maximum count must be greater than or equal to minimum count.", nameof(maximumCount));
}
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
ArgumentOutOfRangeException.ThrowIfNegative(minimumCount, nameof(minimumCount));
return gen.List[minimumCount, maximumCount].Select(x => x.ToFrozenSet());
}
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen, IEqualityComparer<T> comparer) =>
gen.List.Select(x => x.ToFrozenSet(comparer));
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen, IEqualityComparer<T> comparer, int minimumCount, int maximumCount)
{
if (maximumCount < minimumCount)
{
throw new ArgumentException("Maximum count must be greater than or equal to minimum count.", nameof(maximumCount));
}
ArgumentOutOfRangeException.ThrowIfNegative(minimumCount, nameof(minimumCount));
return gen.List[minimumCount, maximumCount].Select(x => x.ToFrozenSet(comparer));
return gen.FrozenSetOf(minimumCount, maximumCount, comparer);
}
public static Gen<FrozenSet<T>> FrozenSetOf<T, TKey>(this Gen<T> gen, Func<T, TKey> keySelector)
{
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
return gen.FrozenSetOf(comparer);
return gen.List
.Select(x => x.ToFrozenSet(comparer));
}
public static Gen<FrozenSet<T>> FrozenSetOf<T, TKey>(this Gen<T> gen, Func<T, TKey> keySelector, int minimumCount, int maximumCount)
{
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
return gen.FrozenSetOf(comparer, minimumCount, maximumCount);
}
public static Gen<FrozenSet<T>> SubFrozenSetOf<T>(IEnumerable<T> enumerable) =>
SubImmutableArrayOf(enumerable)
.Select(x => x.ToFrozenSet());
public static Gen<FrozenSet<T>> SubFrozenSetOf<T>(IEnumerable<T> enumerable, IEqualityComparer<T> comparer) =>
SubImmutableArrayOf(enumerable)
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen, IEqualityComparer<T>? comparer = default) =>
gen.List
.Select(x => x.ToFrozenSet(comparer));
public static Gen<FrozenSet<T>> SubFrozenSetOf<T, TKey>(IEnumerable<T> enumerable, Func<T, TKey> keySelector)
{
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
public static Gen<FrozenSet<T>> FrozenSetOf<T>(this Gen<T> gen, int minimumCount, int maximumCount, IEqualityComparer<T>? comparer = default) =>
gen.List[minimumCount, maximumCount]
.Select(x => x.ToFrozenSet(comparer));
return SubFrozenSetOf(enumerable, comparer);
public static Gen<FrozenSet<T>> SubFrozenSetOf<T>(ICollection<T> enumerable, IEqualityComparer<T>? comparer = default)
{
var comparerToUse = comparer switch
{
null => enumerable switch
{
FrozenSet<T> frozenSet => frozenSet.Comparer,
_ => comparer
},
_ => comparer
};
return SubImmutableArrayOf(enumerable)
.Select(x => x.ToFrozenSet(comparerToUse));
}
public static Gen<FrozenSet<T>> DistinctBy<T, TKey>(this Gen<FrozenSet<T>> gen, Func<T, TKey> keySelector) =>
from set in gen
select set.DistinctBy(keySelector)
.ToFrozenSet(set.Comparer);
public static Gen<Option<T>> OptionOf<T>(this Gen<T> gen) =>
Gen.Frequency((1, Gen.Const(Option<T>.None)),
(4, gen.Select(Option<T>.Some)));
public static Gen<string> ToUpperInvariant(this GenString gen) =>
gen.Select(x => x.ToUpperInvariant());
public static Gen<Option<T>> Sequence<T>(this Option<Gen<T>> option) =>
option.Match(gen => gen.Select(Option<T>.Some),
() => Gen.Const(Option<T>.None));
public static Gen<FrozenSet<T>> SequenceToFrozenSet<T, TKey>(this IEnumerable<Gen<T>> gens, Func<T, TKey> keySelector) =>
gens.SequenceToImmutableArray()
.Select(x => x.ToFrozenSet(keySelector));
public static Gen<FrozenSet<T>> SequenceToFrozenSet<T>(this IEnumerable<Gen<T>> gens, IEqualityComparer<T>? comparer = null) =>
gens.SequenceToImmutableArray()
.Select(x => x.ToFrozenSet(comparer));
/// <summary>
/// Converts a list of generators to a generator of lists
/// </summary>
@ -195,4 +129,95 @@ public static class Generator
from t in gen
select list.Add(t))
select list.ToImmutableArray();
/// <summary>
/// Converts a list of generators to a generator of frozen sets
/// </summary>
public static Gen<FrozenSet<T>> SequenceToFrozenSet<T, TKey>(this IEnumerable<Gen<T>> gens, Func<T, TKey> keySelector) =>
gens.SequenceToImmutableArray()
.Select(x => x.ToFrozenSet(keySelector));
/// <summary>
/// Converts a list of generators to a generator of frozen sets
/// </summary>
public static Gen<FrozenSet<T>> SequenceToFrozenSet<T>(this IEnumerable<Gen<T>> gens, IEqualityComparer<T>? comparer = null) =>
gens.SequenceToImmutableArray()
.Select(x => x.ToFrozenSet(comparer));
public static Gen<FrozenSet<T>> GenerateNewSet<T>(FrozenSet<T> original, Gen<FrozenSet<T>> newGen, Func<T, Gen<T>> updateGen) =>
GenerateNewSet(original, newGen, updateGen, ChangeParameters.All);
private static Gen<FrozenSet<T>> GenerateNewSet<T>(FrozenSet<T> original, Gen<FrozenSet<T>> newGen, Func<T, Gen<T>> updateGen, ChangeParameters changeParameters)
{
var generator = from originalItems in Gen.Const(original)
from itemsRemoved in changeParameters.Remove ? RemoveItems(originalItems) : Gen.Const(originalItems)
from itemsAdded in changeParameters.Add ? AddItems(itemsRemoved, newGen) : Gen.Const(itemsRemoved)
from itemsModified in changeParameters.Modify ? ModifyItems(itemsAdded, updateGen) : Gen.Const(itemsAdded)
select itemsModified;
return changeParameters.MaxSize.Map(maxSize => generator.SelectMany(set => set.Count <= maxSize
? generator
: from smallerSet in Gen.Shuffle(set.ToArray(), maxSize)
select smallerSet.ToFrozenSet(set.Comparer)))
.IfNone(generator);
}
private static Gen<FrozenSet<T>> RemoveItems<T>(FrozenSet<T> set) =>
from itemsToRemove in Generator.SubFrozenSetOf(set)
select set.Except(itemsToRemove, set.Comparer)
.ToFrozenSet(set.Comparer);
private static Gen<FrozenSet<T>> AddItems<T>(FrozenSet<T> set, Gen<FrozenSet<T>> gen) =>
from itemsToAdd in gen
select set.Concat(itemsToAdd)
.ToFrozenSet(set.Comparer);
private static Gen<FrozenSet<T>> ModifyItems<T>(FrozenSet<T> set, Func<T, Gen<T>> updateGen) =>
from itemsToModify in Generator.SubFrozenSetOf(set)
from modifiedItems in itemsToModify.Select(updateGen).SequenceToImmutableArray()
select set.Concat(itemsToModify)
.Concat(modifiedItems)
.ToFrozenSet(set.Comparer);
private sealed record ChangeParameters
{
public required bool Add { get; init; }
public required bool Modify { get; init; }
public required bool Remove { get; init; }
public static ChangeParameters None { get; } = new()
{
Add = false,
Modify = false,
Remove = false
};
public static ChangeParameters All { get; } = new()
{
Add = true,
Modify = true,
Remove = true
};
public Option<int> MaxSize { get; init; } = Option<int>.None;
}
}
/// <summary>
/// Generator for Bogus dataset <typeparamref name="T"/>. It's set to a constant value,
/// and its randomizer is seeded with the <see cref="Gen"/> seed.
/// </summary>
file sealed class GenBogusDataSet<T>(Func<T> creator) : Gen<T> where T : Bogus.DataSet
{
public override T Generate(PCG pcg, Size? min, out Size size)
{
var t = creator();
t.Random = new Bogus.Randomizer((int)pcg.Seed);
size = new Size(0);
return t;
}
}

Просмотреть файл

@ -1,6 +1,5 @@
using CsCheck;
using LanguageExt;
using Nito.Comparers;
using System.Collections.Frozen;
using System.Linq;
@ -35,8 +34,6 @@ public sealed record GroupModel
select lorem.Paragraph();
public static Gen<FrozenSet<GroupModel>> GenerateSet() =>
Generate().FrozenSetOf(EqualityComparerBuilder.For<GroupModel>()
.EquateBy(x => x.Name)
.ThenEquateBy(x => x.DisplayName),
0, 10);
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -1,6 +1,5 @@
using CsCheck;
using LanguageExt;
using Nito.Comparers;
using System.Collections.Frozen;
using System.Linq;
@ -60,8 +59,6 @@ public sealed record ProductModel
select lorem.Paragraph();
public static Gen<FrozenSet<ProductModel>> GenerateSet() =>
Generate().FrozenSetOf(EqualityComparerBuilder.For<ProductModel>()
.EquateBy(x => x.Name)
.ThenEquateBy(x => x.DisplayName),
0, 10);
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -3,6 +3,7 @@ using LanguageExt;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace common.tests;
@ -67,13 +68,13 @@ public record ServiceModel
public static Gen<FrozenSet<ApiModel>> UpdateApis(FrozenSet<ApiModel> apis,
ICollection<VersionSetModel> versionSets,
ICollection<TagModel> tags) =>
apis.Map(api => from version in UpdateApiVersion(versionSets)
from revisions in UpdateApiRevisions(api.Revisions, tags)
select api with
{
Version = version,
Revisions = revisions
})
apis.Select(api => from version in UpdateApiVersion(versionSets)
from revisions in UpdateApiRevisions(api.Revisions, tags)
select api with
{
Version = version,
Revisions = revisions
})
.SequenceToFrozenSet(apis.Comparer);
@ -92,11 +93,11 @@ public record ServiceModel
}
private static Gen<FrozenSet<ApiRevision>> UpdateApiRevisions(FrozenSet<ApiRevision> revisions, ICollection<TagModel> tags) =>
revisions.Map(revision => from tags in UpdateApiTags(revision.Tags, tags)
select revision with
{
Tags = tags
})
revisions.Select(revision => from tags in UpdateApiTags(revision.Tags, tags)
select revision with
{
Tags = tags
})
.SequenceToFrozenSet(revisions.Comparer);
private static Gen<FrozenSet<ApiTagModel>> UpdateApiTags(FrozenSet<ApiTagModel> apiTags, ICollection<TagModel> tags)
@ -107,7 +108,8 @@ public record ServiceModel
.ToFrozenSet(apiTags.Comparer));
}
var tagNames = tags.Select(tag => tag.Name);
var tagNames = tags.Select(tag => tag.Name)
.ToImmutableArray();
return from apiTagNames in Generator.SubImmutableArrayOf(tagNames)
select apiTagNames.Select(tagName => new ApiTagModel { Name = tagName })
@ -125,13 +127,13 @@ public record ServiceModel
var loggersArray = loggers.ToArray();
return from updates in diagnostics.Map(diagnostic => from logger in Gen.OneOfConst(loggersArray)
select diagnostic with
{
// Diagnostic name must be "azuremonitor" if the logger type is AzureMonitor
Name = logger.Type is LoggerType.AzureMonitor ? DiagnosticName.From("azuremonitor") : diagnostic.Name,
LoggerName = logger.Name
})
return from updates in diagnostics.Select(diagnostic => from logger in Gen.OneOfConst(loggersArray)
select diagnostic with
{
// Diagnostic name must be "azuremonitor" if the logger type is AzureMonitor
Name = logger.Type is LoggerType.AzureMonitor ? DiagnosticName.From("azuremonitor") : diagnostic.Name,
LoggerName = logger.Name
})
.SequenceToFrozenSet(diagnostics.Comparer)
select updates;
}
@ -153,11 +155,11 @@ public record ServiceModel
var productsArray = products.ToArray();
return from updates in subscriptions.Map(subscription => from product in Gen.OneOfConst(productsArray)
select subscription with
{
Scope = new SubscriptionScope.Product { Name = product.Name }
})
return from updates in subscriptions.Select(subscription => from product in Gen.OneOfConst(productsArray)
select subscription with
{
Scope = new SubscriptionScope.Product { Name = product.Name }
})
.SequenceToFrozenSet(subscriptions.Comparer)
select updates;
}
@ -172,11 +174,11 @@ public record ServiceModel
var apisArray = apis.ToArray();
return from updates in subscriptions.Map(subscription => from api in Gen.OneOfConst(apisArray)
select subscription with
{
Scope = new SubscriptionScope.Api { Name = api.Name }
})
return from updates in subscriptions.Select(subscription => from api in Gen.OneOfConst(apisArray)
select subscription with
{
Scope = new SubscriptionScope.Api { Name = api.Name }
})
.SequenceToFrozenSet(subscriptions.Comparer)
select updates;
}
@ -185,15 +187,15 @@ public record ServiceModel
ICollection<GroupModel> groups,
ICollection<TagModel> tags,
ICollection<ApiModel> apis) =>
products.Map(product => from productGroups in UpdateProductGroups(product.Groups, groups)
from productTags in UpdateProductTags(product.Tags, tags)
from productApis in UpdateProductApis(product.Apis, apis)
select product with
{
Groups = productGroups,
Tags = productTags,
Apis = productApis
})
products.Select(product => from productGroups in UpdateProductGroups(product.Groups, groups)
from productTags in UpdateProductTags(product.Tags, tags)
from productApis in UpdateProductApis(product.Apis, apis)
select product with
{
Groups = productGroups,
Tags = productTags,
Apis = productApis
})
.SequenceToFrozenSet(products.Comparer);
private static Gen<FrozenSet<ProductGroupModel>> UpdateProductGroups(FrozenSet<ProductGroupModel> productGroups, ICollection<GroupModel> groups)
@ -206,8 +208,8 @@ public record ServiceModel
var groupNames = groups.Select(group => group.Name).ToArray();
return productGroups.Map(productGroup => from groupName in Gen.OneOfConst(groupNames)
select productGroup with { Name = groupName })
return productGroups.Select(productGroup => from groupName in Gen.OneOfConst(groupNames)
select productGroup with { Name = groupName })
.SequenceToFrozenSet(productGroups.Comparer);
}
@ -221,8 +223,8 @@ public record ServiceModel
var tagNames = tags.Select(tag => tag.Name).ToArray();
return productTags.Map(productTag => from tagName in Gen.OneOfConst(tagNames)
select productTag with { Name = tagName })
return productTags.Select(productTag => from tagName in Gen.OneOfConst(tagNames)
select productTag with { Name = tagName })
.SequenceToFrozenSet(productTags.Comparer);
}
@ -236,17 +238,17 @@ public record ServiceModel
var apiNames = apis.Select(api => api.Name).ToArray();
return productApis.Map(productApi => from apiName in Gen.OneOfConst(apiNames)
select productApi with { Name = apiName })
return productApis.Select(productApi => from apiName in Gen.OneOfConst(apiNames)
select productApi with { Name = apiName })
.SequenceToFrozenSet(productApis.Comparer);
}
public static Gen<FrozenSet<GatewayModel>> UpdateGateways(FrozenSet<GatewayModel> gateways, ICollection<ApiModel> apis) =>
gateways.Map(gateway => from gatewayApis in UpdateGatewayApis(gateway.Apis, apis)
select gateway with
{
Apis = gatewayApis
})
gateways.Select(gateway => from gatewayApis in UpdateGatewayApis(gateway.Apis, apis)
select gateway with
{
Apis = gatewayApis
})
.SequenceToFrozenSet(gateways.Comparer);
private static Gen<FrozenSet<GatewayApiModel>> UpdateGatewayApis(FrozenSet<GatewayApiModel> gatewayApis, ICollection<ApiModel> apis)
@ -259,8 +261,8 @@ public record ServiceModel
var apiNames = apis.Select(api => api.Name).ToArray();
return gatewayApis.Map(gatewayApi => from apiName in Gen.OneOfConst(apiNames)
select gatewayApi with { Name = apiName })
return gatewayApis.Select(gatewayApi => from apiName in Gen.OneOfConst(apiNames)
select gatewayApi with { Name = apiName })
.SequenceToFrozenSet(gatewayApis.Comparer);
}
}

Просмотреть файл

@ -1,6 +1,5 @@
using CsCheck;
using LanguageExt;
using Nito.Comparers;
using System.Collections.Frozen;
using System.Linq;
@ -55,8 +54,6 @@ public sealed record SubscriptionModel
Generator.AlphaNumericStringBetween(10, 20);
public static Gen<FrozenSet<SubscriptionModel>> GenerateSet() =>
Generate().FrozenSetOf(EqualityComparerBuilder.For<SubscriptionModel>()
.EquateBy(x => x.Name)
.ThenEquateBy(x => x.DisplayName),
0, 10);
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -1,6 +1,5 @@
using CsCheck;
using LanguageExt;
using Nito.Comparers;
using System.Collections.Frozen;
using System.Linq;
@ -28,8 +27,6 @@ public sealed record TagModel
Generator.AlphaNumericStringBetween(10, 20);
public static Gen<FrozenSet<TagModel>> GenerateSet() =>
Generate().FrozenSetOf(EqualityComparerBuilder.For<TagModel>()
.EquateBy(x => x.Name)
.ThenEquateBy(x => x.DisplayName),
0, 10);
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -1,6 +1,5 @@
using CsCheck;
using LanguageExt;
using Nito.Comparers;
using System.Collections.Frozen;
using System.Linq;
@ -74,8 +73,6 @@ public sealed record VersionSetModel
select lorem.Paragraph();
public static Gen<FrozenSet<VersionSetModel>> GenerateSet() =>
Generate().FrozenSetOf(EqualityComparerBuilder.For<VersionSetModel>()
.EquateBy(x => x.Name)
.ThenEquateBy(x => x.DisplayName),
0, 10);
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -0,0 +1,39 @@
using CsCheck;
using LanguageExt;
using System.Collections.Frozen;
using System.Linq;
namespace common.tests;
public sealed record WorkspaceModel
{
public required WorkspaceName Name { get; init; }
public required string DisplayName { get; init; }
public Option<string> Description { get; init; }
public static Gen<WorkspaceModel> Generate() =>
from name in GenerateName()
from displayName in GenerateDisplayName()
from description in GenerateDescription().OptionOf()
select new WorkspaceModel
{
Name = name,
DisplayName = displayName,
Description = description
};
public static Gen<WorkspaceName> GenerateName() =>
from name in Generator.AlphaNumericStringBetween(10, 20)
select WorkspaceName.From(name);
public static Gen<string> GenerateDisplayName() =>
Generator.AlphaNumericStringBetween(10, 20);
public static Gen<string> GenerateDescription() =>
from lorem in Generator.Lorem
select lorem.Paragraph();
public static Gen<FrozenSet<WorkspaceModel>> GenerateSet() =>
Generate().FrozenSetOf(x => x.Name, 0, 10)
.DistinctBy(x => x.DisplayName);
}

Просмотреть файл

@ -5,12 +5,12 @@
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>8-all</AnalysisLevel>
<WarningsNotAsErrors>CA1034,CA1062,CA1724,CA2007,CA1848,CA1716</WarningsNotAsErrors>
<WarningsNotAsErrors>CA1034,CA1062,CA1724,CA2007,CA1848,CA1716,NU1903</WarningsNotAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.5.1" />
<PackageReference Include="Bogus" Version="35.6.0" />
<PackageReference Include="CsCheck" Version="3.2.2" />
</ItemGroup>

Просмотреть файл

@ -293,17 +293,9 @@ public sealed record ApiDto
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OAuth2AuthenticationSettingsContract? OAuth2 { get; init; }
[JsonPropertyName("oAuth2AuthenticationSettings")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<OAuth2AuthenticationSettingsContract>? OAuth2AuthenticationSettings { get; init; }
[JsonPropertyName("openid")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OpenIdAuthenticationSettingsContract? OpenId { get; init; }
[JsonPropertyName("openidAuthenticationSettings")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<OpenIdAuthenticationSettingsContract>? OpenIdAuthenticationSettings { get; init; }
}
public record OAuth2AuthenticationSettingsContract
@ -438,17 +430,6 @@ public static class ApiModule
return content.ToObjectFromJson<ApiDto>();
}
public static async ValueTask<Option<ApiDto>> TryGetDto(this ApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ApiDto>())
.Match(Option<ApiDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ApiDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask<Option<BinaryData>> TryGetSpecificationContents(this ApiUri apiUri, ApiSpecification specification, HttpPipeline pipeline, CancellationToken cancellationToken)
{
if (specification is ApiSpecification.GraphQl)
@ -664,7 +645,7 @@ public static class ApiModule
}
}
public static async ValueTask PutGraphQlSchema(this ApiUri uri, string schema, HttpPipeline pipeline, CancellationToken cancellationToken)
public static async ValueTask PutGraphQlSchema(this ApiUri uri, BinaryData schema, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var contents = BinaryData.FromObjectAsJson(new JsonObject()
{
@ -673,7 +654,7 @@ public static class ApiModule
["contentType"] = "application/vnd.ms-azure-apim.graphql.schema",
["document"] = new JsonObject()
{
["value"] = schema
["value"] = schema.ToString()
}
}
});
@ -691,13 +672,9 @@ public static class ApiModule
.AppendPathSegment("graphql")
.ToUri();
var schemaJsonEither = await pipeline.TryGetJsonObject(schemaUri, cancellationToken);
var schemaJsonOption = await pipeline.GetJsonObjectOption(schemaUri, cancellationToken);
return schemaJsonEither.Map(GetGraphQlSpecificationFromSchemaResponse)
.Match(Option<BinaryData>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<BinaryData>.None
: throw response.ToHttpRequestException(schemaUri));
return schemaJsonOption.Map(GetGraphQlSpecificationFromSchemaResponse);
}
private static BinaryData GetGraphQlSpecificationFromSchemaResponse(JsonObject responseJson)
@ -724,11 +701,11 @@ public static class ApiModule
.Select(directory => new ApiInformationFile { Parent = directory })
.Where(informationFile => informationFile.ToFileInfo().Exists());
public static IAsyncEnumerable<ApiSpecificationFile> ListSpecificationFiles(ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
public static IAsyncEnumerable<ApiSpecificationFile> ListSpecificationFiles(ManagementServiceDirectory serviceDirectory) =>
ListDirectories(serviceDirectory)
.SelectMany(directory => directory.ToDirectoryInfo().ListFiles("*"))
.ToAsyncEnumerable()
.Choose(async file => await ApiSpecificationFile.TryParse(file, serviceDirectory, cancellationToken));
.Choose(async (file, cancellationToken) => await ApiSpecificationFile.TryParse(file, serviceDirectory, cancellationToken));
public static async ValueTask WriteDto(this ApiInformationFile file, ApiDto dto, CancellationToken cancellationToken)
{
@ -745,26 +722,9 @@ public static class ApiModule
public static async ValueTask WriteSpecification(this ApiSpecificationFile file, BinaryData contents, CancellationToken cancellationToken) =>
await file.ToFileInfo().OverwriteWithBinaryData(contents, cancellationToken);
public static FileInfo ToFileInfo(this ApiSpecificationFile file) =>
file switch
{
GraphQlSpecificationFile graphQl => graphQl.ToFileInfo(),
WadlSpecificationFile wadl => wadl.ToFileInfo(),
WsdlSpecificationFile wsdl => wsdl.ToFileInfo(),
OpenApiSpecificationFile openApi => openApi switch
{
YamlOpenApiSpecificationFile yaml => yaml.ToFileInfo(),
JsonOpenApiSpecificationFile json => json.ToFileInfo(),
_ => throw new NotSupportedException()
},
_ => throw new NotSupportedException()
};
public static async ValueTask<BinaryData> ReadContents(this ApiSpecificationFile file, CancellationToken cancellationToken) =>
await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
public static Option<VersionSetName> TryGetVersionSetName(ApiDto dto) =>
from versionSetId in Prelude.Optional(dto.Properties.ApiVersionSetId)
from versionSetNameString in versionSetId.Split('/').LastOrNone()
from versionSetNameString in versionSetId.Split('/')
.LastOrNone()
select VersionSetName.From(versionSetNameString);
}

Просмотреть файл

@ -123,17 +123,6 @@ public static class ApiOperationPolicyModule
return content.ToObjectFromJson<ApiOperationPolicyDto>();
}
public static async ValueTask<Option<ApiOperationPolicyDto>> TryGetDto(this ApiOperationPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ApiOperationPolicyDto>())
.Match(Option<ApiOperationPolicyDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ApiOperationPolicyDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this ApiOperationPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -125,13 +124,9 @@ public static class ApiPolicyModule
public static async ValueTask<Option<ApiPolicyDto>> TryGetDto(this ApiPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
var option = await pipeline.GetContentOption(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ApiPolicyDto>())
.Match(Option<ApiPolicyDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ApiPolicyDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
return option.Map(content => content.ToObjectFromJson<ApiPolicyDto>());
}
public static async ValueTask Delete(this ApiPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>

Просмотреть файл

@ -4,7 +4,6 @@ using Microsoft.OpenApi.Readers;
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -93,8 +92,7 @@ public abstract record ApiSpecificationFile : ResourceFile
select specificationFile as ApiSpecificationFile;
return await ImmutableArray.Create(tryParseGraphQl, tryParseWadl, tryParseWsdl, tryParseOpenApi)
.ToAsyncEnumerable()
.Pick(async f => await f(), cancellationToken);
.Pick(async (f, cancellationToken) => await f(), cancellationToken);
}
}
@ -116,7 +114,7 @@ public sealed record GraphQlSpecificationFile : ApiSpecificationFile
: Option<GraphQlSpecificationFile>.None;
}
public sealed record class WadlSpecificationFile : ApiSpecificationFile
public sealed record WadlSpecificationFile : ApiSpecificationFile
{
public override ApiSpecification Specification { get; } = new ApiSpecification.Wadl();
@ -180,8 +178,7 @@ public abstract record OpenApiSpecificationFile : ApiSpecificationFile
select json as OpenApiSpecificationFile;
return await ImmutableArray.Create(tryParseYaml, tryParseJson)
.ToAsyncEnumerable()
.Pick(async f => await f(), cancellationToken);
.Pick(async (f, cancellationToken) => await f(), cancellationToken);
}
}

Просмотреть файл

@ -143,20 +143,21 @@ public static class ApiTagModule
public static IEnumerable<ApiTagInformationFile> ListInformationFiles(ApiName apiName, ManagementServiceDirectory serviceDirectory) =>
ListApiTagsDirectories(apiName, serviceDirectory)
.SelectMany(ListApiTagDirectories)
.Select(directory => ApiTagInformationFile.From(directory.Name, apiName, serviceDirectory));
.Select(directory => ApiTagInformationFile.From(directory.Name, apiName, serviceDirectory))
.Where(informationFile => informationFile.ToFileInfo().Exists());
private static IEnumerable<ApiTagsDirectory> ListApiTagsDirectories(ApiName apiName, ManagementServiceDirectory serviceDirectory) =>
ApiDirectory.From(apiName, serviceDirectory)
.ToDirectoryInfo()
.ListDirectories("*")
.Where(ApiTagsDirectory.IsDirectoryNameValid)
.Select(_ => ApiTagsDirectory.From(apiName, serviceDirectory));
.ToDirectoryInfo()
.ListDirectories("*")
.Where(ApiTagsDirectory.IsDirectoryNameValid)
.Select(_ => ApiTagsDirectory.From(apiName, serviceDirectory));
private static IEnumerable<ApiTagDirectory> ListApiTagDirectories(ApiTagsDirectory apiTagsDirectory) =>
apiTagsDirectory.ToDirectoryInfo()
.ListDirectories("*")
.Choose(directory => from name in ApiTagDirectory.TryParseApiTagName(directory)
select new ApiTagDirectory { Name = name, Parent = apiTagsDirectory });
.ListDirectories("*")
.Choose(directory => from name in ApiTagDirectory.TryParseApiTagName(directory)
select new ApiTagDirectory { Name = name, Parent = apiTagsDirectory });
public static async ValueTask WriteDto(this ApiTagInformationFile file, ApiTagDto dto, CancellationToken cancellationToken)
{

Просмотреть файл

@ -1,24 +1,17 @@
using Azure.Core;
using Azure.Core.Pipeline;
using Azure.Identity;
using Azure.ResourceManager;
using Flurl;
using LanguageExt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http.Resilience;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Polly.Retry;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
namespace common;
@ -33,232 +26,205 @@ public sealed record AzureEnvironment(Uri AuthorityHost, string DefaultScope, Ur
public static AzureEnvironment China { get; } = new(AzureAuthorityHosts.AzureChina, ArmEnvironment.AzureChina.DefaultScope, ArmEnvironment.AzureChina.Endpoint);
}
public class ApimHttpClient(HttpClient client)
public sealed record SubscriptionId : NonEmptyString
{
public HttpClient HttpClient { get; } = client;
public SubscriptionId(string value) : base(value) { }
}
public static class ApimHttpClientExtensions
public sealed record ResourceGroupName : NonEmptyString
{
public static IServiceCollection ConfigureApimHttpClient(this IServiceCollection services)
{
services.AddHttpClient<ApimHttpClient>()
.AddHttpMessageHandler<LoggingHandler>()
.AddHttpMessageHandler<TokenCredentialHandler>()
.AddStandardResilienceHandler(ConfigureResilienceOptions);
services.TryAddTransient<LoggingHandler>();
services.TryAddTransient<TokenCredentialHandler>();
return services;
}
private static void ConfigureResilienceOptions(HttpStandardResilienceOptions options)
{
options.Retry.ShouldHandle = ShouldRetry;
}
private static async ValueTask<bool> ShouldRetry(RetryPredicateArguments<HttpResponseMessage> arguments) =>
HttpClientResiliencePredicates.IsTransient(arguments.Outcome)
|| arguments.Outcome switch
{
{ Result: { } response } =>
await HasManagementApiRequestFailed(response, arguments.Context.CancellationToken)
|| await IsEntityNotFound(response, arguments.Context.CancellationToken),
_ => false
};
private static async ValueTask<bool> HasManagementApiRequestFailed(HttpResponseMessage response, CancellationToken cancellationToken)
{
try
{
var responseJsonOption = await Common.TryGetJsonObjectCopy(response.Content, cancellationToken);
return responseJsonOption.Bind(responseJson => responseJson.TryGetJsonObjectProperty("error")
.Bind(error => error.TryGetStringProperty("code"))
.ToOption()
.Where(code => code.Equals("ManagementApiRequestFailed", StringComparison.OrdinalIgnoreCase)))
.IsSome;
}
catch (JsonException)
{
return false;
}
}
private static async ValueTask<bool> IsEntityNotFound(HttpResponseMessage response, CancellationToken cancellationToken)
{
if (response.StatusCode is not HttpStatusCode.BadRequest)
{
return false;
}
var content = await Common.GetStringCopy(response.Content, cancellationToken);
return content.Contains("Entity with specified identifier not found", StringComparison.OrdinalIgnoreCase);
}
public ResourceGroupName(string value) : base(value) { }
}
# pragma warning disable CA1812
file sealed class LoggingHandler(ILoggerFactory loggerFactory) : DelegatingHandler
public static class AzureModule
{
private readonly ILogger logger = loggerFactory.CreateLogger(nameof(ApimHttpClient));
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
private static void ConfigureAzureEnvironment(IHostApplicationBuilder builder)
{
await LogRequest(request, cancellationToken);
var stopWatch = Stopwatch.StartNew();
var response = await base.SendAsync(request, cancellationToken);
stopWatch.Stop();
await LogResponse(response, stopWatch.Elapsed, cancellationToken);
return response;
builder.Services.TryAddSingleton(GetAzureEnvironment);
}
private async ValueTask LogRequest(HttpRequestMessage request, CancellationToken cancellationToken)
private static AzureEnvironment GetAzureEnvironment(IServiceProvider provider)
{
if (logger.IsEnabled(LogLevel.Trace))
{
logger.LogTrace("""
Starting request
Method: {HttpMethod}
Uri: {Uri}
Content: {RequestContent}
""",
request.Method,
request.RequestUri,
await GetContentString(request.Content, request.Headers, cancellationToken));
}
else if (logger.IsEnabled(LogLevel.Debug))
{
logger.LogDebug("""
Starting request
Method: {HttpMethod}
Uri: {Uri}
""",
request.Method,
request.RequestUri);
}
var configuration = provider.GetRequiredService<IConfiguration>();
return configuration.TryGetValue("AZURE_CLOUD_ENVIRONMENT")
.Map(value => value switch
{
"AzureGlobalCloud" or nameof(ArmEnvironment.AzurePublicCloud) => AzureEnvironment.Public,
"AzureChinaCloud" or nameof(ArmEnvironment.AzureChina) => AzureEnvironment.China,
"AzureUSGovernment" or nameof(ArmEnvironment.AzureGovernment) => AzureEnvironment.USGovernment,
"AzureGermanCloud" or nameof(ArmEnvironment.AzureGermany) => AzureEnvironment.Germany,
_ => throw new InvalidOperationException($"AZURE_CLOUD_ENVIRONMENT is invalid. Valid values are {nameof(ArmEnvironment.AzurePublicCloud)}, {nameof(ArmEnvironment.AzureChina)}, {nameof(ArmEnvironment.AzureGovernment)}, {nameof(ArmEnvironment.AzureGermany)}")
})
.IfNone(() => AzureEnvironment.Public);
}
private static async ValueTask<string> GetContentString(HttpContent? content, HttpHeaders headers, CancellationToken cancellationToken) =>
content switch
{
null => "<null>",
_ => HeaderIsJson(headers)
? await Common.GetStringCopy(content, cancellationToken)
: "<non-json>"
};
private static bool HeaderIsJson(HttpHeaders headers) =>
headers.TryGetValues("Content-Type", out var values) &&
values.Contains("application/json", StringComparer.OrdinalIgnoreCase);
private async ValueTask LogResponse(HttpResponseMessage response, TimeSpan duration, CancellationToken cancellationToken)
private static void ConfigureTokenCredential(IHostApplicationBuilder builder)
{
if (logger.IsEnabled(LogLevel.Trace))
{
logger.LogTrace("""
Starting request
Method: {HttpMethod}
Uri: {Uri}
Duration (hh:mm:ss): {Duration}
Content: {RequestContent}
""",
response.RequestMessage?.Method,
response.RequestMessage?.RequestUri,
duration.ToString("c"),
await GetContentString(response.Content, response.Headers, cancellationToken));
}
else if (logger.IsEnabled(LogLevel.Debug))
{
logger.LogDebug("""
Starting request
Method: {HttpMethod}
Duration (hh:mm:ss): {Duration}
Uri: {Uri}
""",
response.RequestMessage?.Method,
response.RequestMessage?.RequestUri,
duration.ToString("c"));
}
ConfigureAzureEnvironment(builder);
builder.Services.TryAddSingleton(GetTokenCredential);
}
}
file sealed class TokenCredentialHandler(TokenCredential tokenCredential) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
private static TokenCredential GetTokenCredential(IServiceProvider provider)
{
if (request.RequestUri is null)
var environment = provider.GetRequiredService<AzureEnvironment>();
var configuration = provider.GetRequiredService<IConfiguration>();
return configuration.TryGetValue("AZURE_BEARER_TOKEN")
.Map(GetCredentialFromToken)
.IfNone(() => GetDefaultAzureCredential(environment.AuthorityHost));
static TokenCredential GetCredentialFromToken(string token)
{
return await base.SendAsync(request, cancellationToken);
var jsonWebToken = new JsonWebToken(token);
var expirationDate = new DateTimeOffset(jsonWebToken.ValidTo);
var accessToken = new AccessToken(token, expirationDate);
return DelegatedTokenCredential.Create((context, cancellationToken) => accessToken);
}
request.Headers.Authorization = await GetAuthenticationHeader(request.RequestUri, cancellationToken);
return await base.SendAsync(request, cancellationToken);
static DefaultAzureCredential GetDefaultAzureCredential(Uri azureAuthorityHost) =>
new(new DefaultAzureCredentialOptions
{
AuthorityHost = azureAuthorityHost
});
}
private async ValueTask<AuthenticationHeaderValue> GetAuthenticationHeader(Uri uri, CancellationToken cancellationToken)
public static void ConfigureHttpPipeline(IHostApplicationBuilder builder)
{
var accessToken = await GetAccessToken(uri, cancellationToken);
ConfigureTokenCredential(builder);
ConfigureAzureEnvironment(builder);
return new AuthenticationHeaderValue("Bearer", accessToken.Token);
builder.Services.TryAddSingleton(GetHttpPipeline);
}
private async ValueTask<AccessToken> GetAccessToken(Uri uri, CancellationToken cancellationToken)
private static HttpPipeline GetHttpPipeline(IServiceProvider provider)
{
var scopeUrl = uri.GetLeftPart(UriPartial.Authority)
.AppendPathSegment(".default")
.ToString();
var tokenCredential = provider.GetRequiredService<TokenCredential>();
var azureEnvironment = provider.GetRequiredService<AzureEnvironment>();
return await GetAccessToken([scopeUrl], cancellationToken);
var clientOptions = ClientOptions.Default;
clientOptions.RetryPolicy = new CommonRetryPolicy();
var bearerAuthenticationPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, azureEnvironment.DefaultScope);
var logger = provider.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(HttpPipeline));
var loggingPolicy = new ILoggerHttpPipelinePolicy(logger);
var version = Assembly.GetEntryAssembly()?.GetName().Version ?? new Version("-1");
var telemetryPolicy = new TelemetryPolicy(version);
return HttpPipelineBuilder.Build(clientOptions, bearerAuthenticationPolicy, loggingPolicy, telemetryPolicy);
}
private async ValueTask<AccessToken> GetAccessToken(string[] scopes, CancellationToken cancellationToken)
private static void ConfigureManagementServiceName(IHostApplicationBuilder builder)
{
var context = new TokenRequestContext(scopes);
return await tokenCredential.GetTokenAsync(context, cancellationToken);
}
}
#pragma warning restore CA1812
file static class Common
{
public static async ValueTask<Stream> GetStreamCopy(HttpContent content, CancellationToken cancellationToken)
{
using var stream = new MemoryStream();
await content.CopyToAsync(stream, cancellationToken);
stream.Position = 0;
return stream;
builder.Services.TryAddSingleton(GetManagementServiceName);
}
public static async ValueTask<string> GetStringCopy(HttpContent content, CancellationToken cancellationToken)
private static ManagementServiceName GetManagementServiceName(IServiceProvider provider)
{
using var stream = await GetStreamCopy(content, cancellationToken);
var data = await BinaryData.FromStreamAsync(stream, cancellationToken);
var configuration = provider.GetRequiredService<IConfiguration>();
return data.ToString();
var name = configuration.TryGetValue("API_MANAGEMENT_SERVICE_NAME")
.IfNone(() => configuration.GetValue("apimServiceName"));
return ManagementServiceName.From(name);
}
public static async ValueTask<Option<JsonObject>> TryGetJsonObjectCopy(HttpContent content, CancellationToken cancellationToken)
public static void ConfigureManagementServiceUri(IHostApplicationBuilder builder)
{
using var stream = await GetStreamCopy(content, cancellationToken);
ConfigureManagementServiceProviderUri(builder);
ConfigureManagementServiceName(builder);
try
{
var node = await JsonNode.ParseAsync(stream, cancellationToken: cancellationToken);
builder.Services.TryAddSingleton(GetManagementServiceUri);
}
return node is JsonObject jsonObject
? Option<JsonObject>.Some(jsonObject)
: Option<JsonObject>.None;
}
catch (JsonException)
{
return Option<JsonObject>.None;
}
private static ManagementServiceUri GetManagementServiceUri(IServiceProvider provider)
{
var serviceProviderUri = provider.GetRequiredService<ManagementServiceProviderUri>();
var serviceName = provider.GetRequiredService<ManagementServiceName>();
var uri = serviceProviderUri.ToUri()
.AppendPathSegment(serviceName)
.ToUri();
return ManagementServiceUri.From(uri);
}
public static void ConfigureManagementServiceProviderUri(IHostApplicationBuilder builder)
{
ConfigureAzureEnvironment(builder);
ConfigureSubscriptionId(builder);
ConfigureResourceGroupName(builder);
builder.Services.TryAddSingleton(GetManagementServiceProviderUri);
}
private static ManagementServiceProviderUri GetManagementServiceProviderUri(IServiceProvider provider)
{
var azureEnvironment = provider.GetRequiredService<AzureEnvironment>();
var subscriptionId = provider.GetRequiredService<SubscriptionId>();
var resourceGroupName = provider.GetRequiredService<ResourceGroupName>();
var configuration = provider.GetRequiredService<IConfiguration>();
var apiVersion = configuration.TryGetValue("ARM_API_VERSION")
.IfNone(() => "2023-09-01-preview");
var uri = azureEnvironment.ManagementEndpoint
.AppendPathSegment("subscriptions")
.AppendPathSegment(subscriptionId)
.AppendPathSegment("resourceGroups")
.AppendPathSegment(resourceGroupName)
.AppendPathSegment("providers/Microsoft.ApiManagement/service")
.SetQueryParam("api-version", apiVersion)
.ToUri();
return ManagementServiceProviderUri.From(uri);
}
private static void ConfigureSubscriptionId(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetSubscriptionId);
}
private static SubscriptionId GetSubscriptionId(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var subscriptionId = configuration.GetValue("AZURE_SUBSCRIPTION_ID");
return new SubscriptionId(subscriptionId);
}
private static void ConfigureResourceGroupName(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetResourceGroupName);
}
private static ResourceGroupName GetResourceGroupName(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var resourceGroupName = configuration.GetValue("AZURE_RESOURCE_GROUP_NAME");
return new ResourceGroupName(resourceGroupName);
}
public static void ConfigureManagementServiceDirectory(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetManagementServiceDirectory);
}
private static ManagementServiceDirectory GetManagementServiceDirectory(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var directoryPath = configuration.GetValue("API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH");
var directory = new DirectoryInfo(directoryPath);
return ManagementServiceDirectory.From(directory);
}
}

Просмотреть файл

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
@ -27,7 +26,8 @@ public sealed record BackendsUri : ResourceUri
private static string PathSegment { get; } = "backends";
protected override Uri Value => ServiceUri.ToUri().AppendPathSegment(PathSegment).ToUri();
protected override Uri Value =>
ServiceUri.ToUri().AppendPathSegment(PathSegment).ToUri();
public static BackendsUri From(ManagementServiceUri serviceUri) =>
new() { ServiceUri = serviceUri };
@ -36,9 +36,11 @@ public sealed record BackendsUri : ResourceUri
public sealed record BackendUri : ResourceUri
{
public required BackendsUri Parent { get; init; }
public required BackendName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static BackendUri From(BackendName name, ManagementServiceUri serviceUri) =>
new()
@ -61,9 +63,8 @@ public sealed record BackendsDirectory : ResourceDirectory
new() { ServiceDirectory = serviceDirectory };
public static Option<BackendsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null &&
directory.Name == Name &&
directory.Parent?.FullName == serviceDirectory.ToDirectoryInfo().FullName
directory?.Name == Name &&
directory?.Parent?.FullName == serviceDirectory.ToDirectoryInfo().FullName
? new BackendsDirectory { ServiceDirectory = serviceDirectory }
: Option<BackendsDirectory>.None;
}
@ -75,7 +76,7 @@ public sealed record BackendDirectory : ResourceDirectory
public required BackendName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static BackendDirectory From(BackendName name, ManagementServiceDirectory serviceDirectory) =>
new()
@ -86,17 +87,19 @@ public sealed record BackendDirectory : ResourceDirectory
public static Option<BackendDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in BackendsDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = BackendName.From(directory!.Name)
select new BackendDirectory
{
Parent = parent,
Name = BackendName.From(directory!.Name)
Name = name
};
}
public sealed record BackendInformationFile : ResourceFile
{
public required BackendDirectory Parent { get; init; }
private static string Name { get; } = "backendInformation.json";
public static string Name { get; } = "backendInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
@ -104,17 +107,17 @@ public sealed record BackendInformationFile : ResourceFile
public static BackendInformationFile From(BackendName name, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = new BackendDirectory
{
Parent = BackendsDirectory.From(serviceDirectory),
Name = name
}
Parent = BackendDirectory.From(name, serviceDirectory)
};
public static Option<BackendInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
file is not null &&
file.Name == Name
? from parent in BackendDirectory.TryParse(file.Directory, serviceDirectory)
select new BackendInformationFile { Parent = parent }
select new BackendInformationFile
{
Parent = parent
}
: Option<BackendInformationFile>.None;
}
@ -277,23 +280,25 @@ public static class BackendModule
{
public static async ValueTask DeleteAll(this BackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name => await BackendUri.From(name, uri.ServiceUri)
.Delete(pipeline, cancellationToken),
cancellationToken);
.IterParallel(async name =>
{
var backendUri = new BackendUri { Parent = uri, Name = name };
await backendUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<BackendName> ListNames(this BackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(BackendName.From);
public static IAsyncEnumerable<(BackendName Name, BackendDto Dto)> List(this BackendsUri backendsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
backendsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new BackendUri { Parent = backendsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static IAsyncEnumerable<(BackendName Name, BackendDto Dto)> List(this BackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var backendUri = new BackendUri { Parent = uri, Name = name };
var dto = await backendUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<BackendDto> GetDto(this BackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
@ -301,17 +306,6 @@ public static class BackendModule
return content.ToObjectFromJson<BackendDto>();
}
public static async ValueTask<Option<BackendDto>> TryGetDto(this BackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<BackendDto>())
.Match(Option<BackendDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<BackendDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this BackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
@ -325,16 +319,20 @@ public static class BackendModule
{
var backendsDirectory = BackendsDirectory.From(serviceDirectory);
return backendsDirectory.ToDirectoryInfo()
.ListDirectories("*")
.Select(directoryInfo => BackendName.From(directoryInfo.Name))
.Select(name => new BackendDirectory { Parent = backendsDirectory, Name = name });
return from backendsDirectoryInfo in backendsDirectory.ToDirectoryInfo().ListDirectories("*")
let name = BackendName.From(backendsDirectoryInfo.Name)
select new BackendDirectory
{
Parent = backendsDirectory,
Name = name
};
}
public static IEnumerable<BackendInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
ListDirectories(serviceDirectory)
.Select(directory => new BackendInformationFile { Parent = directory })
.Where(informationFile => informationFile.ToFileInfo().Exists());
from backendDirectory in ListDirectories(serviceDirectory)
let informationFile = new BackendInformationFile { Parent = backendDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this BackendInformationFile file, BackendDto dto, CancellationToken cancellationToken)
{

Просмотреть файл

@ -1,12 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System;
using System.IO;
namespace common;
public abstract record ResourceName
public abstract record NonEmptyString
{
protected ResourceName(string value)
protected NonEmptyString(string value)
{
ArgumentException.ThrowIfNullOrWhiteSpace(value, nameof(value));
Value = value;
@ -14,11 +13,16 @@ public abstract record ResourceName
public string Value { get; }
public sealed override string ToString() => Value;
}
public abstract record ResourceName : NonEmptyString
{
protected ResourceName(string value) : base(value) { }
public virtual bool Equals(ResourceName? other) => string.Equals(Value, other?.Value, StringComparison.OrdinalIgnoreCase);
public override int GetHashCode() => Value.GetHashCode(StringComparison.OrdinalIgnoreCase);
public sealed override string ToString() => Value;
}
public abstract record ResourceDirectory

Просмотреть файл

@ -1,8 +1,14 @@
using LanguageExt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using Yaml2JsonNode;
@ -12,6 +18,8 @@ namespace common;
public record ConfigurationJson
{
private static readonly JsonNodeOptions nodeOptions = new() { PropertyNameCaseInsensitive = true };
public required JsonObject Value { get; init; }
public static ConfigurationJson From(IConfiguration configuration) =>
@ -19,18 +27,18 @@ public record ConfigurationJson
{
Value = SerializeConfiguration(configuration) is JsonObject configurationJsonObject
? configurationJsonObject
: new JsonObject(JsonNodeExtensions.Options)
: new JsonObject(nodeOptions)
};
private static JsonNode? SerializeConfiguration(IConfiguration configuration)
{
var jsonObject = new JsonObject(JsonNodeExtensions.Options);
var jsonObject = new JsonObject();
foreach (var child in configuration.GetChildren())
{
if (child.Path.EndsWith(":0", StringComparison.Ordinal))
{
var jsonArray = new JsonArray(JsonNodeExtensions.Options);
var jsonArray = new JsonArray(nodeOptions);
foreach (var arrayChild in configuration.GetChildren())
{
@ -85,7 +93,7 @@ public record ConfigurationJson
return yamlStream.Documents switch
{
[] => new JsonObject(JsonNodeExtensions.Options),
[] => new JsonObject(nodeOptions),
[var document] => document.ToJsonNode()?.AsObject() ?? throw new JsonException("Failed to convert YAML to JSON."),
_ => throw new JsonException("More than one YAML document was found.")
};
@ -157,4 +165,51 @@ public static class ConfigurationExtensions
? Option<IConfigurationSection>.Some(section)
: Option<IConfigurationSection>.None;
}
public static IConfigurationBuilder AddUserSecretsWithLowestPriority(this IConfigurationBuilder builder, Assembly assembly, bool optional = true) =>
builder.AddWithLowestPriority(b => b.AddUserSecrets(assembly, optional));
private static IConfigurationBuilder AddWithLowestPriority(this IConfigurationBuilder builder, Func<IConfigurationBuilder, IConfigurationBuilder> adder)
{
// Configuration sources added last have the highest priority. We empty existing sources,
// add the new sources, and then add the existing sources back.
var adderSources = adder(new ConfigurationBuilder()).Sources;
var existingSources = builder.Sources;
var sources = adderSources.Concat(existingSources)
.ToImmutableArray();
builder.Sources.Clear();
sources.Iter(source => builder.Add(source));
return builder;
}
}
public static class ConfigurationModule
{
public static void ConfigureConfigurationJson(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetConfigurationJson);
}
private static ConfigurationJson GetConfigurationJson(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var configurationJson = ConfigurationJson.From(configuration);
return TryGetConfigurationJsonFromYaml(configuration)
.Map(configurationJson.MergeWith)
.IfNone(configurationJson);
}
private static Option<ConfigurationJson> TryGetConfigurationJsonFromYaml(IConfiguration configuration) =>
configuration.TryGetValue("CONFIGURATION_YAML_PATH")
.Map(path => new FileInfo(path))
.Where(file => file.Exists)
.Map(file =>
{
using var reader = File.OpenText(file.FullName);
return ConfigurationJson.FromYaml(reader);
});
}

Просмотреть файл

@ -261,17 +261,6 @@ public static class DiagnosticModule
return content.ToObjectFromJson<DiagnosticDto>();
}
public static async ValueTask<Option<DiagnosticDto>> TryGetDto(this DiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<DiagnosticDto>())
.Match(Option<DiagnosticDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<DiagnosticDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this DiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -2,11 +2,10 @@
using LanguageExt.UnsafeValueAccess;
using Nito.Comparers;
using System;
using System.Collections;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
@ -14,15 +13,43 @@ namespace common;
public static class IEnumerableExtensions
{
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static void Iter<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var t in enumerable)
{
action(t);
}
}
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: 1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(action, maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// <paramref name="maxDegreeOfParallelism"/> controls the maximum number of parallel actions. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, int maxDegreeOfParallelism, CancellationToken cancellationToken)
{
var options = new ParallelOptions
@ -34,71 +61,112 @@ public static class IEnumerableExtensions
await Parallel.ForEachAsync(enumerable, parallelOptions: options, action);
}
public static IAsyncEnumerable<T2> Choose<T, T2>(this IEnumerable<T> enumerable, Func<T, ValueTask<Option<T2>>> f) =>
enumerable.ToAsyncEnumerable()
.Choose(f);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T1, T2>(this IEnumerable<(T1, T2)> enumerable, Func<T1, T2, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, cancellationToken) => await action(t.Item1, t.Item2, cancellationToken), cancellationToken);
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IEnumerable<T2> Choose<T, T2>(this IEnumerable<T> enumerable, Func<T, Option<T2>> f)
{
var enumerableM = enumerable.AsEnumerableM();
return EnumerableMExtensions.Choose(enumerableM, f);
}
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and returns the first Option of <typeparamref name="T2"/>
/// that is Some. If all options are None, returns a None.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="enumerable"></param>
/// <param name="f"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static Option<T2> Pick<T, T2>(this IEnumerable<T> enumerable, Func<T, Option<T2>> f) =>
enumerable.Select(f)
.Where(option => option.IsSome)
.DefaultIfEmpty(Option<T2>.None)
.First();
public static async ValueTask<Option<T2>> Pick<T, T2>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f, CancellationToken cancellationToken)
{
foreach (var item in enumerable)
{
var option = await f(item, cancellationToken);
public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this IEnumerable<(TKey Key, TValue Value)> enumerable, IEqualityComparer<TKey>? comparer = default) where TKey : notnull =>
enumerable.ToFrozenDictionary(x => x.Key, x => x.Value, comparer);
if (option.IsSome)
{
return option;
}
}
public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue, TComparison>(this IEnumerable<(TKey Key, TValue Value)> enumerable, Func<TKey, TComparison> comparer) where TKey : notnull =>
enumerable.ToFrozenDictionary(x => x.Key, x => x.Value, EqualityComparerBuilder.For<TKey>().EquateBy(comparer));
return Option<T2>.None;
}
public static FrozenSet<T> ToFrozenSet<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector) =>
enumerable.ToFrozenSet(EqualityComparerBuilder.For<T>().EquateBy(keySelector));
/// <summary>
/// Returns the first item in the enumerable. If the enumerable is empty, returns <seealso cref="Option.None"/>.
/// </summary>
public static Option<T> HeadOrNone<T>(this IEnumerable<T> enumerable)
{
var m = enumerable.AsEnumerableM();
return FoldableExtensions.Head(m);
}
/// <summary>
/// Returns the last item in the enumerable. If the enumerable is empty, returns <seealso cref="Option.None"/>.
/// </summary>
public static Option<T> LastOrNone<T>(this IEnumerable<T> enumerable)
{
var m = enumerable.AsEnumerableM();
return FoldableExtensions.Last(m);
}
public static FrozenSet<T> ToFrozenSet<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
{
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
return enumerable.ToFrozenSet(comparer);
}
public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this IEnumerable<(TKey, TValue)> enumerable, IEqualityComparer<TKey>? comparer = default) where TKey : notnull =>
enumerable.ToFrozenDictionary(kvp => kvp.Item1, kvp => kvp.Item2, comparer);
}
public static class IAsyncEnumerableExtensions
{
public static IAsyncEnumerable<T> Do<T>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask> action) =>
enumerable.SelectAwait(async t =>
{
await action(t);
return t;
});
public static IAsyncEnumerable<T> Do<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action) =>
enumerable.SelectAwaitWithCancellation(async (t, cancellationToken) =>
{
await action(t, cancellationToken);
return t;
});
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Action<T> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) =>
{
action(t);
await ValueTask.CompletedTask;
}, maxDegreeOfParallelism: 1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.Iter(async (t, _) => await action(t), cancellationToken);
await enumerable.Iter(async (t, cancellationToken) => await action(t), cancellationToken);
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(action, maxDegreeOfParallelism: 1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken)
{
await foreach (var item in enumerable.WithCancellation(cancellationToken))
{
await action(item, cancellationToken);
}
}
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(action, maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// <paramref name="maxDegreeOfParallelism"/> controls the maximum number of parallel actions. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, int maxDegreeOfParallelism, CancellationToken cancellationToken)
{
var options = new ParallelOptions
@ -111,133 +179,120 @@ public static class IAsyncEnumerableExtensions
}
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and filters out cases where the resulting Option of <typeparamref name="T2"/> is None.
/// Iterates over an <seealso cref="IAsyncEnumerable"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T1, T2>(this IAsyncEnumerable<(T1, T2)> enumerable, Func<T1, T2, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, cancellationToken) => await action(t.Item1, t.Item2, cancellationToken), cancellationToken);
public static async ValueTask<FrozenSet<T>> ToFrozenSet<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken, IEqualityComparer<T>? comparer = default)
{
var items = await enumerable.ToListAsync(cancellationToken);
return items.ToFrozenSet(comparer);
}
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, Option<T2>> f) =>
enumerable.Choose(async t => await f(t).AsValueTask());
enumerable.Choose(async (t, cancellationToken) => await ValueTask.FromResult(f(t)));
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and filters out cases where the resulting Option of <typeparamref name="T2"/> is None.
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask<Option<T2>>> f) =>
enumerable.Choose((t, cancellationToken) => f(t));
enumerable.Choose(async (t, cancellationToken) => await f(t));
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and filters out cases where the resulting Option of <typeparamref name="T2"/> is None.
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f) =>
enumerable.SelectAwaitWithCancellation(f)
.Where(option => option.IsSome)
.Select(option => option.ValueUnsafe());
.Select(option => option.ValueUnsafe()!);
public static async ValueTask<Option<T>> HeadOrNone<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken) =>
/// <summary>
///
/// </summary>
public static async ValueTask<Option<T>> FirstOrNone<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken) =>
await enumerable.Select(Option<T>.Some)
.DefaultIfEmpty(Option<T>.None)
.FirstOrDefaultAsync(cancellationToken);
.FirstAsync(cancellationToken);
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and returns the first Option of <typeparamref name="T2"/>
/// that is Some. If all options are None, returns a None.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="enumerable"></param>
/// <param name="f"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async ValueTask<Option<T2>> Pick<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask<Option<T2>>> f, CancellationToken cancellationToken) =>
await enumerable.SelectAwait(f)
.Where(option => option.IsSome)
.DefaultIfEmpty(Option<T2>.None)
.FirstAsync(cancellationToken);
public static async ValueTask<Option<T2>> Pick<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f, CancellationToken cancellationToken) =>
await enumerable.Choose(f)
.FirstOrNone(cancellationToken);
public static async IAsyncEnumerable<TResult> FullJoin<T1, T2, TKey, TResult>(this IAsyncEnumerable<T1> first,
IAsyncEnumerable<T2> second,
Func<T1, ValueTask<TKey>> firstKeySelector,
Func<T2, ValueTask<TKey>> secondKeySelector,
Func<T1, ValueTask<TResult>> firstResultSelector,
Func<T2, ValueTask<TResult>> secondResultSelector,
Func<T1, T2, ValueTask<TResult>> bothResultSelector,
[EnumeratorCancellation] CancellationToken cancellationToken)
public static async ValueTask<FrozenDictionary<TKey, TValue>> ToFrozenDictionary<TKey, TValue>(this IAsyncEnumerable<(TKey, TValue)> enumerable, CancellationToken cancellationToken, IEqualityComparer<TKey>? comparer = default) where TKey : notnull
{
var secondLookup = await second.ToLookupAwaitAsync(secondKeySelector, cancellationToken);
var keys = new System.Collections.Generic.HashSet<TKey>();
var array = await enumerable.ToArrayAsync(cancellationToken);
await foreach (var firstItem in first.WithCancellation(cancellationToken))
{
var firstKey = await firstKeySelector(firstItem);
keys.Add(firstKey);
if (secondLookup.Contains(firstKey))
{
var secondItems = secondLookup[firstKey];
foreach (var secondItem in secondItems)
{
yield return await bothResultSelector(firstItem, secondItem);
}
}
else
{
yield return await firstResultSelector(firstItem);
}
}
foreach (var group in secondLookup)
{
if (keys.Contains(group.Key) is false)
{
foreach (var secondItem in group)
{
yield return await secondResultSelector(secondItem);
}
}
}
return array.ToFrozenDictionary(comparer);
}
}
public static async ValueTask<FrozenSet<T>> ToFrozenSet<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken, IEqualityComparer<T>? comparer = null)
{
var result = await enumerable.ToArrayAsync(cancellationToken);
public static class KeyValuePairExtensions
{
/// <summary>
/// Applies <paramref name="f"/> to each value and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue2>> ChooseValues<TKey, TValue, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, Func<TValue, Option<TValue2>> f) =>
keyValuePairs.Choose(kvp => from value2 in f(kvp.Value)
select KeyValuePair.Create(kvp.Key, value2));
return result.ToFrozenSet(comparer);
}
/// <summary>
/// Applies <paramref name="f"/> to each key and filters out <seealso cref="Option.None"/> keys.
/// </summary>
public static IEnumerable<KeyValuePair<TKey2, TValue>> ChooseKeys<TKey, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, Func<TKey, Option<TKey2>> f) =>
keyValuePairs.Choose(kvp => from key2 in f(kvp.Key)
select KeyValuePair.Create(key2, kvp.Value));
public static async ValueTask<FrozenDictionary<TKey, TValue>> ToFrozenDictionary<TKey, TValue>(this IAsyncEnumerable<(TKey Key, TValue Value)> enumerable, CancellationToken cancellationToken) where TKey : notnull
{
var list = await enumerable.ToArrayAsync(cancellationToken);
/// <summary>
/// Creates a new key value pair whose value is <paramref name="f"/>(<paramref name="keyValuePair"/>.Value).
/// </summary>
public static KeyValuePair<TKey, TValue2> MapValue<TKey, TValue, TValue2>(this KeyValuePair<TKey, TValue> keyValuePair, Func<TValue, TValue2> f) =>
KeyValuePair.Create(keyValuePair.Key, f(keyValuePair.Value));
return list.ToFrozenDictionary();
}
/// <summary>
/// Creates a new key value pair whose key is <paramref name="f"/>(<paramref name="keyValuePair"/>.Key).
/// </summary>
public static KeyValuePair<TKey2, TValue> MapKey<TKey, TKey2, TValue>(this KeyValuePair<TKey, TValue> keyValuePair, Func<TKey, TKey2> f) =>
KeyValuePair.Create(f(keyValuePair.Key), keyValuePair.Value);
/// <summary>
/// Applies <paramref name="f"/> to each key
/// </summary>
public static IEnumerable<KeyValuePair<TKey2, TValue>> MapKey<TKey, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TKey, TKey2> f) =>
enumerable.Select(kvp => kvp.MapKey(f));
/// <summary>
/// Applies <paramref name="f"/> to each value
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue2>> MapValue<TKey, TValue, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TValue, TValue2> f) =>
enumerable.Select(kvp => kvp.MapValue(f));
/// <summary>
/// Removes keys where the predicate is false
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue>> WhereKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TKey, bool> predicate) =>
enumerable.Where(kvp => predicate(kvp.Key));
/// <summary>
/// Removes values where the predicate is false
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue>> WhereValue<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TValue, bool> predicate) =>
enumerable.Where(kvp => predicate(kvp.Value));
}
public static class DictionaryExtensions
{
public static Option<TValue> Find<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) where TKey : notnull =>
public static Option<TValue> Find<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var value)
? value
? Option<TValue>.Some(value)
: Option<TValue>.None;
public static ImmutableDictionary<TKey, TValue> WhereKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> dictionary, Func<TKey, bool> predicate) where TKey : notnull =>
dictionary.Where(kvp => predicate(kvp.Key))
.ToImmutableDictionary();
public static ImmutableDictionary<TKey, TValue> WhereValue<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> dictionary, Func<TValue, bool> predicate) where TKey : notnull =>
dictionary.Where(kvp => predicate(kvp.Value))
.ToImmutableDictionary();
public static ImmutableDictionary<TKey2, TValue> MapKey<TKey1, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey1, TValue>> dictionary, Func<TKey1, TKey2> f) where TKey2 : notnull =>
dictionary.ToImmutableDictionary(kvp => f(kvp.Key), kvp => kvp.Value);
public static ImmutableDictionary<TKey, TValue2> MapValue<TKey, TValue1, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue1>> dictionary, Func<TValue1, TValue2> f) where TKey : notnull =>
dictionary.ToImmutableDictionary(kvp => kvp.Key, kvp => f(kvp.Value));
public static ImmutableDictionary<TKey2, TValue> ChooseKey<TKey, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> dictionary, Func<TKey, Option<TKey2>> f) where TKey2 : notnull =>
dictionary.Choose(kvp => from key2 in f(kvp.Key)
select KeyValuePair.Create(key2, kvp.Value))
.ToImmutableDictionary();
public static ImmutableDictionary<TKey, TValue2> ChooseValue<TKey, TValue1, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue1>> dictionary, Func<TValue1, Option<TValue2>> f) where TKey : notnull =>
dictionary.Choose(kvp => from value2 in f(kvp.Value)
select KeyValuePair.Create(kvp.Key, value2))
.ToImmutableDictionary();
}

Просмотреть файл

@ -191,17 +191,6 @@ public static class GatewayModule
return content.ToObjectFromJson<GatewayDto>();
}
public static async ValueTask<Option<GatewayDto>> TryGetDto(this GatewayUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<GatewayDto>())
.Match(Option<GatewayDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<GatewayDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this GatewayUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -169,17 +169,6 @@ public static class GroupModule
return content.ToObjectFromJson<GroupDto>();
}
public static async ValueTask<Option<GroupDto>> TryGetDto(this GroupUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<GroupDto>())
.Match(Option<GroupDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<GroupDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this GroupUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryDeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -0,0 +1,103 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public delegate ValueTask RunApplication(CancellationToken cancellationToken);
public static class HostingModule
{
/// <param name="applicationName">Will be used to set the logger name and the OpenTelemetry activity source name.</param>
/// <param name="configureRunApplication">Delegate that adds <see cref="common.RunApplication" /> to the builder services.</param>
/// <returns></returns>
public static async ValueTask RunHost(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
using var host = GetHost(arguments, applicationName, configureRunApplication);
await StartHost(host);
await RunApplication(host);
}
private static IHost GetHost(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
var builder = GetBuilder(arguments, applicationName, configureRunApplication);
return builder.Build();
}
private static HostApplicationBuilder GetBuilder(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
var builder = Host.CreateApplicationBuilder(arguments);
ConfigureBuilder(builder, applicationName, configureRunApplication);
return builder;
}
private static void ConfigureBuilder(HostApplicationBuilder builder, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
ConfigureConfiguration(builder);
OpenTelemetryModule.Configure(builder, applicationName);
ConfigureLogging(builder, applicationName);
configureRunApplication(builder);
}
private static void ConfigureConfiguration(HostApplicationBuilder builder)
{
if (Assembly.GetEntryAssembly() is Assembly entryAssembly)
{
builder.Configuration.AddUserSecretsWithLowestPriority(entryAssembly);
}
builder.Configuration
.TryGetValue("CONFIGURATION_YAML_PATH")
.Iter(path => builder.Configuration.AddYamlFile(path));
}
private static void ConfigureLogging(HostApplicationBuilder builder, string applicationName)
{
builder.Services.TryAddSingleton(provider => GetCommonLogger(provider, applicationName));
}
private static ILogger GetCommonLogger(IServiceProvider provider, string applicationName)
{
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
return loggerFactory.CreateLogger(applicationName);
}
private static async ValueTask StartHost(IHost host)
{
var applicationLifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var cancellationToken = applicationLifetime.ApplicationStopping;
await host.StartAsync(cancellationToken);
}
/// <summary>
/// Extracts the delegate <see cref="common.RunApplication"/> from the host's services and runs it.
/// </summary>
private static async ValueTask RunApplication(IHost host)
{
var applicationLifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var cancellationToken = applicationLifetime.ApplicationStopping;
try
{
var runApplication = host.Services.GetRequiredService<RunApplication>();
await runApplication(cancellationToken);
}
catch (Exception exception)
{
var logger = host.Services.GetRequiredService<ILogger>();
logger.LogCritical(exception, "Application failed.");
Environment.ExitCode = -1;
throw;
}
finally
{
applicationLifetime.StopApplication();
}
}
}

Просмотреть файл

@ -26,13 +26,25 @@ public static class HttpPipelineExtensions
{
var either = await pipeline.TryGetContent(uri, cancellationToken);
return either.IfLeft(response =>
return either.IfLeftThrow(uri);
}
/// <summary>
/// Gets the response content. If the status code is <see cref="HttpStatusCode.NotFound"/>, returns <see cref="Option.None"/>.
/// </summary>
public static async ValueTask<Option<BinaryData>> GetContentOption(this HttpPipeline pipeline, Uri uri, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri, cancellationToken);
return either.Match(response =>
{
using (response)
{
throw response.ToHttpRequestException(uri);
return response.Status == (int)HttpStatusCode.NotFound
? Option<BinaryData>.None
: throw response.ToHttpRequestException(uri);
}
});
}, Option<BinaryData>.Some);
}
public static async ValueTask<Either<Response, BinaryData>> TryGetContent(this HttpPipeline pipeline, Uri uri, CancellationToken cancellationToken)
@ -57,6 +69,15 @@ public static class HttpPipelineExtensions
public static HttpRequestException ToHttpRequestException(this Response response, Uri requestUri) =>
new(message: $"HTTP request to URI {requestUri} failed with status code {response.Status}. Content is '{response.Content}'.", inner: null, statusCode: (HttpStatusCode)response.Status);
private static T IfLeftThrow<T>(this Either<Response, T> either, Uri requestUri) =>
either.IfLeft(response =>
{
using (response)
{
throw response.ToHttpRequestException(requestUri);
}
});
public static async IAsyncEnumerable<JsonObject> ListJsonObjects(this HttpPipeline pipeline, Uri uri, [EnumeratorCancellation] CancellationToken cancellationToken)
{
Uri? nextLink = uri;
@ -66,8 +87,8 @@ public static class HttpPipelineExtensions
var responseJson = await pipeline.GetJsonObject(nextLink, cancellationToken);
var values = responseJson.TryGetJsonArrayProperty("value")
.IfLeft(() => new JsonArray())
.GetJsonObjects();
.IfLeft(() => [])
.PickJsonObjects();
foreach (var value in values)
{
@ -83,13 +104,17 @@ public static class HttpPipelineExtensions
{
var either = await pipeline.TryGetJsonObject(uri, cancellationToken);
return either.IfLeft(response =>
{
using (response)
{
throw response.ToHttpRequestException(uri);
}
});
return either.IfLeftThrow(uri);
}
/// <summary>
/// Gets the response content as a JSON object. If the status code is <see cref="HttpStatusCode.NotFound"/>, returns <see cref="Option.None"/>.
/// </summary>
public static async ValueTask<Option<JsonObject>> GetJsonObjectOption(this HttpPipeline pipeline, Uri uri, CancellationToken cancellationToken)
{
var option = await pipeline.GetContentOption(uri, cancellationToken);
return option.Map(content => content.ToObjectFromJson<JsonObject>());
}
public static async ValueTask<Either<Response, JsonObject>> TryGetJsonObject(this HttpPipeline pipeline, Uri uri, CancellationToken cancellationToken)
@ -103,16 +128,7 @@ public static class HttpPipelineExtensions
{
var either = await pipeline.TryDeleteResource(uri, waitForCompletion, cancellationToken);
either.IfLeft(response =>
{
using (response)
{
if (response.Status is not (int)HttpStatusCode.NotFound)
{
throw response.ToHttpRequestException(uri);
}
}
});
either.IfLeftThrow(uri);
}
public static async ValueTask<Either<Response, Unit>> TryDeleteResource(this HttpPipeline pipeline, Uri uri, bool waitForCompletion, CancellationToken cancellationToken)

Просмотреть файл

@ -1,5 +1,4 @@
using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@ -7,98 +6,63 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public static class JsonArrayExtensions
public static class JsonValueExtensions
{
public static JsonArray ToJsonArray(this IEnumerable<JsonNode?> nodes) =>
nodes.Aggregate(new JsonArray(),
(array, node) =>
{
array.Add(node);
return array;
});
public static Either<string, string> TryAsString(this JsonValue? jsonValue) =>
jsonValue is null
? Either<string, string>.Left("JSON value is null.")
: jsonValue.TryGetValue<string>(out var stringValue)
? Either<string, string>.Right(stringValue)
: Either<string, string>.Left("JSON value is not a string.");
public static ValueTask<JsonArray> ToJsonArray(this IAsyncEnumerable<JsonNode?> nodes, CancellationToken cancellationToken) =>
nodes.AggregateAsync(new JsonArray(),
(array, node) =>
{
array.Add(node);
return array;
},
cancellationToken);
public static ImmutableArray<JsonObject> GetJsonObjects(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsJsonObject())
.ToImmutableArray();
public static ImmutableArray<JsonArray> GetJsonArrays(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsJsonArray())
.ToImmutableArray();
public static ImmutableArray<JsonValue> GetJsonValues(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsJsonValue())
.ToImmutableArray();
public static ImmutableArray<string> GetNonEmptyOrWhitespaceStrings(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsString())
.Where(value => !string.IsNullOrWhiteSpace(value))
.ToImmutableArray();
public static Either<string, Uri> TryAsAbsoluteUri(this JsonValue? jsonValue) =>
jsonValue.TryAsString()
.Bind(uriString => Uri.TryCreate(uriString, UriKind.Absolute, out var uri)
? Either<string, Uri>.Right(uri)
: $"JSON value '{uriString}' is not a valid absolute URI.");
}
public static class JsonNodeExtensions
{
public static JsonNodeOptions Options { get; } = new() { PropertyNameCaseInsensitive = true };
public static Option<JsonObject> TryAsJsonObject(this JsonNode? node) =>
public static Either<string, JsonObject> TryAsJsonObject(this JsonNode? node) =>
node is JsonObject jsonObject
? jsonObject
: Option<JsonObject>.None;
? jsonObject
: "Node is not a JSON object.";
public static Option<JsonArray> TryAsJsonArray(this JsonNode? node) =>
public static Either<string, JsonArray> TryAsJsonArray(this JsonNode? node) =>
node is JsonArray jsonArray
? jsonArray
: Option<JsonArray>.None;
? jsonArray
: "Node is not a JSON array.";
public static Option<JsonValue> TryAsJsonValue(this JsonNode? node) =>
public static Either<string, JsonValue> TryAsJsonValue(this JsonNode? node) =>
node is JsonValue jsonValue
? jsonValue
: Option<JsonValue>.None;
? jsonValue
: "Node is not a JSON value.";
public static Option<string> TryAsString(this JsonNode? node) =>
public static Either<string, string> TryAsString(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsString);
.Bind(jsonValue => jsonValue.TryAsString());
public static Option<Guid> TryAsGuid(this JsonNode? node) =>
public static Either<string, Uri> TryAsAbsoluteUri(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsGuid);
.Bind(jsonValue => jsonValue.TryAsAbsoluteUri());
}
public static Option<Uri> TryAsAbsoluteUri(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsAbsoluteUri);
public static class JsonArrayExtensions
{
public static ImmutableArray<JsonObject> PickJsonObjects(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsJsonObject().ToOption())
.ToImmutableArray();
public static Option<DateTimeOffset> TryAsDateTimeOffset(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsDateTimeOffset);
public static ImmutableArray<string> PickStrings(this JsonArray jsonArray) =>
jsonArray.Choose(node => node.TryAsString().ToOption())
.ToImmutableArray();
public static Option<DateTime> TryAsDateTime(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsDateTime);
public static Option<int> TryAsInt(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsInt);
public static Option<double> TryAsDouble(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsDouble);
public static Option<bool> TryAsBool(this JsonNode? node) =>
node.TryAsJsonValue()
.Bind(JsonValueExtensions.TryAsBool);
public static JsonArray ToJsonArray(this IEnumerable<JsonNode> nodes) =>
new(nodes.ToArray());
}
public static class JsonObjectExtensions
@ -108,169 +72,76 @@ public static class JsonObjectExtensions
WriteIndented = true
};
public static JsonNode GetProperty(this JsonObject jsonObject, string propertyName) =>
public static JsonNode GetProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.IfLeftThrowJsonException();
public static Option<JsonNode> GetOptionalProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.ToOption();
.IfLeftThrow();
public static Either<string, JsonNode> TryGetProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject is null
? "JSON object is null."
: jsonObject.TryGetPropertyValue(propertyName, out var node)
? node is null
? $"Property '{propertyName}' is null."
: Either<string, JsonNode>.Right(node)
: $"Property '{propertyName}' is missing.";
? "JSON object is null."
: jsonObject.TryGetPropertyValue(propertyName, out var property)
? property is null
? $"Property '{propertyName}' is null."
: property
: $"Property '{propertyName}' is missing.";
public static JsonObject GetJsonObjectProperty(this JsonObject jsonObject, string propertyName) =>
private static T IfLeftThrow<T>(this Either<string, T> either) =>
either.IfLeft(error => throw new JsonException(error));
public static JsonObject GetJsonObjectProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetJsonObjectProperty(propertyName)
.IfLeftThrowJsonException();
.IfLeftThrow();
public static Either<string, JsonObject> TryGetJsonObjectProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsJsonObject(propertyName));
.Bind(JsonNodeExtensions.TryAsJsonObject)
.BindPropertyError(propertyName);
private static Either<string, JsonObject> TryAsJsonObject(this JsonNode node, string propertyName) =>
node.TryAsJsonObject()
.ToEither(() => $"Property '{propertyName}' is not a JSON object.");
public static JsonArray GetJsonArrayProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetJsonArrayProperty(propertyName)
.IfLeftThrowJsonException();
private static Either<string, T> BindPropertyError<T>(this Either<string, T> either, string propertyName) =>
either.MapLeft(error => $"Property '{propertyName}' is invalid. {error}");
public static Either<string, JsonArray> TryGetJsonArrayProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsJsonArray(propertyName));
private static Either<string, JsonArray> TryAsJsonArray(this JsonNode node, string propertyName) =>
node.TryAsJsonArray()
.ToEither(() => $"Property '{propertyName}' is not a JSON array.");
public static JsonValue GetJsonValueProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetJsonValueProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, JsonValue> TryGetJsonValueProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsJsonValue(propertyName));
private static Either<string, JsonValue> TryAsJsonValue(this JsonNode node, string propertyName) =>
node.TryAsJsonValue()
.ToEither(() => $"Property '{propertyName}' is not a JSON value.");
public static string GetStringProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetStringProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, string> TryGetStringProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsString(propertyName));
private static Either<string, string> TryAsString(this JsonNode node, string propertyName) =>
node.TryAsString()
.ToEither(() => $"Property '{propertyName}' is not a string.");
public static string GetNonEmptyOrWhiteSpaceStringProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetNonEmptyOrWhiteSpaceStringProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, string> TryGetNonEmptyOrWhiteSpaceStringProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetStringProperty(propertyName)
.Bind(value => string.IsNullOrWhiteSpace(value)
? Either<string, string>.Left($"Property '{propertyName}' is empty or whitespace.")
: Either<string, string>.Right(value));
public static Guid GetGuidProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetGuidProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, Guid> TryGetGuidProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsGuid(propertyName));
private static Either<string, Guid> TryAsGuid(this JsonNode node, string propertyName) =>
node.TryAsGuid()
.ToEither(() => $"Property '{propertyName}' is not a GUID.");
.Bind(JsonNodeExtensions.TryAsJsonArray)
.BindPropertyError(propertyName);
public static Uri GetAbsoluteUriProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetAbsoluteUriProperty(propertyName)
.IfLeftThrowJsonException();
.IfLeftThrow();
public static Either<string, Uri> TryGetAbsoluteUriProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsAbsoluteUri(propertyName));
.Bind(JsonNodeExtensions.TryAsAbsoluteUri)
.BindPropertyError(propertyName);
public static DateTimeOffset GetDateTimeOffsetProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetDateTimeOffsetProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, DateTimeOffset> TryGetDateTimeOffsetProperty(this JsonObject? jsonObject, string propertyName) =>
public static Either<string, string> TryGetStringProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsDateTimeOffset(propertyName));
.Bind(JsonNodeExtensions.TryAsString)
.BindPropertyError(propertyName);
private static Either<string, DateTimeOffset> TryAsDateTimeOffset(this JsonNode node, string propertyName) =>
node.TryAsDateTimeOffset()
.ToEither(() => $"Property '{propertyName}' is not a valid DateTimeOffset.");
public static string GetNonEmptyOrWhiteSpaceStringProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetNonEmptyOrWhiteSpaceStringProperty(propertyName)
.IfLeftThrow();
public static DateTime GetDateTimeProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetDateTimeProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, DateTime> TryGetDateTimeProperty(this JsonObject? jsonObject, string propertyName) =>
public static Either<string, string> TryGetNonEmptyOrWhiteSpaceStringProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsDateTime(propertyName));
.Bind(JsonNodeExtensions.TryAsString)
.Bind(value => string.IsNullOrWhiteSpace(value)
? Either<string, string>.Left($"Property '{propertyName}' is empty or whitespace.")
: Either<string, string>.Right(value))
.BindPropertyError(propertyName);
private static Either<string, DateTime> TryAsDateTime(this JsonNode node, string propertyName) =>
node.TryAsDateTime()
.ToEither(() => $"Property '{propertyName}' is not a valid DateTime.");
public static string GetStringProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetStringProperty(propertyName)
.IfLeftThrow();
public static JsonObject Parse<T>(T obj) =>
TryParse(obj)
.IfLeft(() => throw new JsonException($"Could not parse {typeof(T).Name} as a JSON object."));
private static Either<string, Uri> TryAsAbsoluteUri(this JsonNode node, string propertyName) =>
node.TryAsAbsoluteUri()
.ToEither(() => $"Property '{propertyName}' is not an absolute URI.");
public static int GetIntProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetIntProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, int> TryGetIntProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsInt(propertyName));
private static Either<string, int> TryAsInt(this JsonNode node, string propertyName) =>
node.TryAsInt()
.ToEither(() => $"Property '{propertyName}' is not an integer.");
public static double GetDoubleProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetDoubleProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, double> TryGetDoubleProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsDouble(propertyName));
private static Either<string, double> TryAsDouble(this JsonNode node, string propertyName) =>
node.TryAsDouble()
.ToEither(() => $"Property '{propertyName}' is not a double.");
public static bool GetBoolProperty(this JsonObject jsonObject, string propertyName) =>
jsonObject.TryGetBoolProperty(propertyName)
.IfLeftThrowJsonException();
public static Either<string, bool> TryGetBoolProperty(this JsonObject? jsonObject, string propertyName) =>
jsonObject.TryGetProperty(propertyName)
.Bind(node => node.TryAsBool(propertyName));
private static Either<string, bool> TryAsBool(this JsonNode node, string propertyName) =>
node.TryAsBool()
.ToEither(() => $"Property '{propertyName}' is not a boolean.");
private static T IfLeftThrowJsonException<T>(this Either<string, T> either)
{
return either.IfLeft(left => throw new JsonException(left));
}
public static Either<string, JsonObject> TryParse<T>(T obj) =>
JsonSerializer.SerializeToNode(obj, SerializerOptions)
.TryAsJsonObject();
[return: NotNullIfNotNull(nameof(jsonObject))]
public static JsonObject? SetProperty(this JsonObject? jsonObject, string propertyName, JsonNode? jsonNode)
@ -285,97 +156,4 @@ public static class JsonObjectExtensions
return jsonObject;
}
}
/// <summary>
/// Sets <paramref name="jsonObject"/>[<paramref name="propertyName"/>] = <paramref name="jsonNode"/> if <paramref name="jsonNode"/> is not null.
/// </summary>
[return: NotNullIfNotNull(nameof(jsonObject))]
public static JsonObject? SetPropertyIfNotNull(this JsonObject? jsonObject, string propertyName, JsonNode? jsonNode) =>
jsonNode is null
? jsonObject
: jsonObject.SetProperty(propertyName, jsonNode);
/// <summary>
/// Sets <paramref name="jsonObject"/>'s property <paramref name="propertyName"/> to the value of <paramref name="option"/> if <paramref name="option"/> is Some.
/// </summary>
[return: NotNullIfNotNull(nameof(jsonObject))]
public static JsonObject? SetPropertyIfSome(this JsonObject? jsonObject, string propertyName, Option<JsonNode> option) =>
jsonObject.SetPropertyIfNotNull(propertyName, option.ValueUnsafe());
[return: NotNullIfNotNull(nameof(jsonObject))]
public static JsonObject? RemoveProperty(this JsonObject? jsonObject, string propertyName)
{
if (jsonObject is null)
{
return null;
}
else
{
jsonObject.Remove(propertyName);
return jsonObject;
}
}
private static readonly JsonSerializerOptions serializerOptions = new() { PropertyNameCaseInsensitive = true };
public static JsonObject Parse<T>(T obj) =>
TryParse(obj)
.IfNone(() => throw new JsonException($"Could not parse {typeof(T).Name} as a JSON object."));
public static Option<JsonObject> TryParse<T>(T obj) =>
JsonSerializer.SerializeToNode(obj, serializerOptions)
.TryAsJsonObject();
}
public static class JsonValueExtensions
{
public static Option<string> TryAsString(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<string>(out var value)
? value
: Option<string>.None;
public static Option<Guid> TryAsGuid(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<Guid>(out var guid)
? guid
: jsonValue.TryAsString()
.Bind(x => Guid.TryParse(x, out var guidFromString)
? guidFromString
: Option<Guid>.None);
public static Option<Uri> TryAsAbsoluteUri(this JsonValue? jsonValue) =>
jsonValue.TryAsString()
.Bind(x => Uri.TryCreate(x, UriKind.Absolute, out var uri)
? uri
: Option<Uri>.None);
public static Option<DateTimeOffset> TryAsDateTimeOffset(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<DateTimeOffset>(out var dateTimeOffset)
? dateTimeOffset
: jsonValue.TryAsString()
.Bind(x => DateTimeOffset.TryParse(x, out var dateTime)
? dateTime
: Option<DateTimeOffset>.None);
public static Option<DateTime> TryAsDateTime(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<DateTime>(out var dateTime)
? dateTime
: jsonValue.TryAsString()
.Bind(x => DateTime.TryParse(x, out var dateTime)
? dateTime
: Option<DateTime>.None);
public static Option<int> TryAsInt(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<int>(out var value)
? value
: Option<int>.None;
public static Option<double> TryAsDouble(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<double>(out var value)
? value
: Option<double>.None;
public static Option<bool> TryAsBool(this JsonValue? jsonValue) =>
jsonValue is not null && jsonValue.TryGetValue<bool>(out var value)
? value
: Option<bool>.None;
}
}

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
@ -17,12 +16,6 @@ public sealed record LoggerName : ResourceName
{
private LoggerName(string value) : base(value) { }
/// <summary>
/// Logger names with revisions have the format 'loggerName;revision'
/// </summary>
public LoggerName ToNonRevisionedName() =>
new(Value.Split(';').First());
public static LoggerName From(string value) => new(value);
}
@ -181,17 +174,6 @@ public static class LoggerModule
return content.ToObjectFromJson<LoggerDto>();
}
public static async ValueTask<Option<LoggerDto>> TryGetDto(this LoggerUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<LoggerDto>())
.Match(Option<LoggerDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<LoggerDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this LoggerUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -19,6 +19,15 @@ public sealed record ManagementServiceDirectory : ResourceDirectory
public static ManagementServiceDirectory From(DirectoryInfo value) => new(value);
}
public sealed record ManagementServiceProviderUri : ResourceUri
{
private ManagementServiceProviderUri(Uri value) => Value = value;
protected override Uri Value { get; }
public static ManagementServiceProviderUri From(Uri value) => new(value);
}
public sealed record ManagementServiceUri : ResourceUri
{
private ManagementServiceUri(Uri value) => Value = value;

Просмотреть файл

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -186,17 +185,6 @@ public static class NamedValueModule
return content.ToObjectFromJson<NamedValueDto>();
}
public static async ValueTask<Option<NamedValueDto>> TryGetDto(this NamedValueUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<NamedValueDto>())
.Match(Option<NamedValueDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<NamedValueDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this NamedValueUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -1,7 +1,9 @@
using Microsoft.Extensions.Configuration;
using Azure.Monitor.OpenTelemetry.AspNetCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Instrumentation.Http;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@ -9,22 +11,34 @@ using System.Diagnostics;
namespace common;
public static class OpenTelemetryServices
public static class OpenTelemetryModule
{
public static void Configure(IServiceCollection services)
public static void Configure(IHostApplicationBuilder builder, string activitySourceName)
{
var sourceName = services.BuildServiceProvider().GetService<ActivitySource>()?.Name ?? "ApiOps.*";
builder.Logging.AddOpenTelemetry(Configure);
Configure(builder.Services, builder.Configuration, activitySourceName);
}
private static void Configure(OpenTelemetryLoggerOptions options)
{
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
}
private static void Configure(IServiceCollection services, IConfiguration configuration, string activitySourceName)
{
#pragma warning disable CA2000 // Dispose objects before losing scope
services.TryAddSingleton(new ActivitySource(activitySourceName));
#pragma warning restore CA2000 // Dispose objects before losing scope
services.AddOpenTelemetry()
.WithMetrics(metrics => metrics.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation()
.AddMeter("Azure.*"))
.WithTracing(tracing => tracing.AddHttpClientInstrumentation(ConfigureHttpClientTraceInstrumentationOptions)
.AddSource("Azure.*")
.AddSource(sourceName)
.AddMeter("*"))
.WithTracing(tracing => tracing.AddHttpClientInstrumentation()
.AddSource("*")
.SetSampler<AlwaysOnSampler>());
var configuration = services.BuildServiceProvider().GetRequiredService<IConfiguration>();
configuration.TryGetValue("OTEL_EXPORTER_OTLP_ENDPOINT")
.Iter(_ =>
{
@ -33,10 +47,9 @@ public static class OpenTelemetryServices
.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter())
.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
});
}
private static void ConfigureHttpClientTraceInstrumentationOptions(HttpClientTraceInstrumentationOptions options)
{
options.FilterHttpRequestMessage = (_) => Activity.Current?.Parent?.Source?.Name != "Azure.Core.Http";
configuration.TryGetValue("APPLICATIONINSIGHTS_CONNECTION_STRING")
.Iter(_ => services.AddOpenTelemetry()
.UseAzureMonitor());
}
}
}

Просмотреть файл

@ -46,28 +46,6 @@ public static class OptionExtensions
public static async ValueTask<Option<T2>> BindTask<T, T2>(this Option<T> option, Func<T, CancellationToken, ValueTask<Option<T2>>> bind, CancellationToken cancellationToken) =>
await option.Match(t => bind(t, cancellationToken), () => ValueTask.FromResult(Option<T2>.None));
public static async ValueTask<Option<T>> Where<T>(this Option<T> option, Func<T, ValueTask<bool>> predicate) =>
await option.BindTask(async t => await predicate(t)
? Option<T>.Some(t)
: Option<T>.None);
/// <summary>
/// Run each function until one returns Some. If none return Some, return None.
/// </summary>
public static async ValueTask<Option<T>> PickFirst<T>(this IEnumerable<Func<ValueTask<Option<T>>>> functions, CancellationToken cancellationToken) =>
await functions.ToAsyncEnumerable()
.Pick(async f => await f(), cancellationToken);
/// <summary>
/// Run each function until one returns Some. If none return Some, return None.
/// </summary>
public static async ValueTask<Option<T>> PickFirst<T>(this IEnumerable<Func<Task<Option<T>>>> functions, CancellationToken cancellationToken) =>
await functions.ToAsyncEnumerable()
.Pick(async f => await f(), cancellationToken);
public static Option<T> Or<T>(this Option<T> option, Func<Option<T>> alternative) =>
option.Match(Option<T>.Some, alternative);
public static async ValueTask<Option<T>> Or<T>(this Option<T> option, Func<ValueTask<Option<T>>> alternative) =>
await option.Match(t => ValueTask.FromResult(Option<T>.Some(t)), alternative);

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -192,17 +191,6 @@ public static class PolicyFragmentModule
return content.ToObjectFromJson<PolicyFragmentDto>();
}
public static async ValueTask<Option<PolicyFragmentDto>> TryGetDto(this PolicyFragmentUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<PolicyFragmentDto>())
.Match(Option<PolicyFragmentDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<PolicyFragmentDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this PolicyFragmentUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -182,17 +181,6 @@ public static class ProductModule
return content.ToObjectFromJson<ProductDto>();
}
public static async ValueTask<Option<ProductDto>> TryGetDto(this ProductUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ProductDto>())
.Match(Option<ProductDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ProductDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this ProductUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -124,17 +124,6 @@ public static class ProductPolicyModule
return content.ToObjectFromJson<ProductPolicyDto>();
}
public static async ValueTask<Option<ProductPolicyDto>> TryGetDto(this ProductPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ProductPolicyDto>())
.Match(Option<ProductPolicyDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ProductPolicyDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this ProductPolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -124,17 +124,6 @@ public static class ServicePolicyModule
return content.ToObjectFromJson<ServicePolicyDto>();
}
public static async ValueTask<Option<ServicePolicyDto>> TryGetDto(this ServicePolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<ServicePolicyDto>())
.Match(Option<ServicePolicyDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<ServicePolicyDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this ServicePolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -73,7 +72,7 @@ public sealed record SubscriptionDirectory : ResourceDirectory
public required SubscriptionName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static SubscriptionDirectory From(SubscriptionName name, ManagementServiceDirectory serviceDirectory) =>
new()
@ -94,7 +93,8 @@ public sealed record SubscriptionDirectory : ResourceDirectory
public sealed record SubscriptionInformationFile : ResourceFile
{
public required SubscriptionDirectory Parent { get; init; }
private static string Name { get; } = "subscriptionInformation.json";
public static string Name { get; } = "subscriptionInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
@ -102,17 +102,14 @@ public sealed record SubscriptionInformationFile : ResourceFile
public static SubscriptionInformationFile From(SubscriptionName name, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = new SubscriptionDirectory
{
Parent = SubscriptionsDirectory.From(serviceDirectory),
Name = name
}
Parent = SubscriptionDirectory.From(name, serviceDirectory)
};
public static Option<SubscriptionInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
file is not null &&
file.Name == Name
? from parent in SubscriptionDirectory.TryParse(file.Directory, serviceDirectory)
select new SubscriptionInformationFile { Parent = parent }
select From(parent.Name, serviceDirectory)
: Option<SubscriptionInformationFile>.None;
}
@ -158,23 +155,25 @@ public static class SubscriptionModule
{
public static async ValueTask DeleteAll(this SubscriptionsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name => await SubscriptionUri.From(name, uri.ServiceUri)
.Delete(pipeline, cancellationToken),
cancellationToken);
.IterParallel(async name =>
{
var resourceUri = SubscriptionUri.From(name, uri.ServiceUri);
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<SubscriptionName> ListNames(this SubscriptionsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(SubscriptionName.From);
public static IAsyncEnumerable<(SubscriptionName Name, SubscriptionDto Dto)> List(this SubscriptionsUri subscriptionsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
subscriptionsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new SubscriptionUri { Parent = subscriptionsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static IAsyncEnumerable<(SubscriptionName Name, SubscriptionDto Dto)> List(this SubscriptionsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new SubscriptionUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<SubscriptionDto> GetDto(this SubscriptionUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
@ -182,17 +181,6 @@ public static class SubscriptionModule
return content.ToObjectFromJson<SubscriptionDto>();
}
public static async ValueTask<Option<SubscriptionDto>> TryGetDto(this SubscriptionUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<SubscriptionDto>())
.Match(Option<SubscriptionDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<SubscriptionDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this SubscriptionUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -59,8 +58,7 @@ public sealed record TagsDirectory : ResourceDirectory
new() { ServiceDirectory = serviceDirectory };
public static Option<TagsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null &&
directory.Name == Name &&
directory?.Name == Name &&
directory.Parent?.FullName == serviceDirectory.ToDirectoryInfo().FullName
? new TagsDirectory { ServiceDirectory = serviceDirectory }
: Option<TagsDirectory>.None;
@ -102,15 +100,11 @@ public sealed record TagInformationFile : ResourceFile
public static TagInformationFile From(TagName name, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = new TagDirectory
{
Parent = TagsDirectory.From(serviceDirectory),
Name = name
}
Parent = TagDirectory.From(name, serviceDirectory)
};
public static Option<TagInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
file?.Name == Name
? from parent in TagDirectory.TryParse(file.Directory, serviceDirectory)
select new TagInformationFile { Parent = parent }
: Option<TagInformationFile>.None;
@ -145,12 +139,12 @@ public static class TagModule
public static IAsyncEnumerable<(TagName Name, TagDto Dto)> List(this TagsUri tagsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
tagsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new TagUri { Parent = tagsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
.SelectAwait(async name =>
{
var uri = new TagUri { Parent = tagsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<TagDto> GetDto(this TagUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
@ -158,17 +152,6 @@ public static class TagModule
return content.ToObjectFromJson<TagDto>();
}
public static async ValueTask<Option<TagDto>> TryGetDto(this TagUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<TagDto>())
.Match(Option<TagDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<TagDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this TagUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
@ -183,9 +166,9 @@ public static class TagModule
var tagsDirectory = TagsDirectory.From(serviceDirectory);
return tagsDirectory.ToDirectoryInfo()
.ListDirectories("*")
.Select(directoryInfo => TagName.From(directoryInfo.Name))
.Select(name => new TagDirectory { Parent = tagsDirectory, Name = name });
.ListDirectories("*")
.Select(directoryInfo => TagName.From(directoryInfo.Name))
.Select(name => new TagDirectory { Parent = tagsDirectory, Name = name });
}
public static IEnumerable<TagInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>

Просмотреть файл

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -174,17 +173,6 @@ public static class VersionSetModule
return content.ToObjectFromJson<VersionSetDto>();
}
public static async ValueTask<Option<VersionSetDto>> TryGetDto(this VersionSetUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var either = await pipeline.TryGetContent(uri.ToUri(), cancellationToken);
return either.Map(content => content.ToObjectFromJson<VersionSetDto>())
.Match(Option<VersionSetDto>.Some,
response => response.Status == (int)HttpStatusCode.NotFound
? Option<VersionSetDto>.None
: throw response.ToHttpRequestException(uri.ToUri()));
}
public static async ValueTask Delete(this VersionSetUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);

Просмотреть файл

@ -0,0 +1,223 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceName : ResourceName
{
private WorkspaceName(string value) : base(value) { }
public static WorkspaceName From(string value) => new(value);
}
public sealed record WorkspacesUri : ResourceUri
{
public required ManagementServiceUri ServiceUri { get; init; }
private static string PathSegment { get; } = "workspaces";
protected override Uri Value =>
ServiceUri.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspacesUri From(ManagementServiceUri serviceUri) =>
new() { ServiceUri = serviceUri };
}
public sealed record WorkspaceUri : ResourceUri
{
public required WorkspacesUri Parent { get; init; }
public required WorkspaceName Name { get; init; }
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspacesUri.From(serviceUri),
Name = name
};
}
public sealed record WorkspacesDirectory : ResourceDirectory
{
public required ManagementServiceDirectory ServiceDirectory { get; init; }
private static string Name { get; } = "workspaces";
protected override DirectoryInfo Value =>
ServiceDirectory.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspacesDirectory From(ManagementServiceDirectory serviceDirectory) =>
new() { ServiceDirectory = serviceDirectory };
public static Option<WorkspacesDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null &&
directory.Name == Name &&
directory.Parent?.FullName == serviceDirectory.ToDirectoryInfo().FullName
? new WorkspacesDirectory { ServiceDirectory = serviceDirectory }
: Option<WorkspacesDirectory>.None;
}
public sealed record WorkspaceDirectory : ResourceDirectory
{
public required WorkspacesDirectory Parent { get; init; }
public required WorkspaceName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static WorkspaceDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspacesDirectory.From(serviceDirectory),
Name = name
};
public static Option<WorkspaceDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null
? from parent in WorkspacesDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = WorkspaceName.From(directory!.Name)
select new WorkspaceDirectory
{
Parent = parent,
Name = name
}
: Option<WorkspaceDirectory>.None;
}
public sealed record WorkspaceInformationFile : ResourceFile
{
public required WorkspaceDirectory Parent { get; init; }
public static string Name { get; } = "workspaceInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceInformationFile From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceDirectory.From(name, serviceDirectory)
};
public static Option<WorkspaceInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null &&
file.Name == Name
? from parent in WorkspaceDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceInformationFile
{
Parent = parent
}
: Option<WorkspaceInformationFile>.None;
}
public sealed record WorkspaceDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required WorkspaceContract Properties { get; init; }
public sealed record WorkspaceContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
}
}
public static class WorkspaceModule
{
public static async ValueTask DeleteAll(this WorkspacesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name =>
{
var resourceUri = new WorkspaceUri { Parent = uri, Name = name };
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<WorkspaceName> ListNames(this WorkspacesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var exceptionHandler = (HttpRequestException exception) =>
exception.StatusCode == HttpStatusCode.BadRequest
&& exception.Message.Contains("MethodNotAllowedInPricingTier", StringComparison.OrdinalIgnoreCase)
? AsyncEnumerable.Empty<WorkspaceName>()
: throw exception;
return pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(WorkspaceName.From)
.Catch(exceptionHandler);
}
public static IAsyncEnumerable<(WorkspaceName Name, WorkspaceDto Dto)> List(this WorkspacesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new WorkspaceUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceDto> GetDto(this WorkspaceUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceDto>();
}
public static async ValueTask Delete(this WorkspaceUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceUri uri, WorkspaceDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static IEnumerable<WorkspaceDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory)
{
var workspacesDirectory = WorkspacesDirectory.From(serviceDirectory);
return from workspacesDirectoryInfo in workspacesDirectory.ToDirectoryInfo().ListDirectories("*")
let name = WorkspaceName.From(workspacesDirectoryInfo.Name)
select new WorkspaceDirectory
{
Parent = workspacesDirectory,
Name = name
};
}
public static IEnumerable<WorkspaceInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
from workspaceDirectory in ListDirectories(serviceDirectory)
let informationFile = new WorkspaceInformationFile { Parent = workspaceDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this WorkspaceInformationFile file, WorkspaceDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceDto> ReadDto(this WorkspaceInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceDto>();
}
}

Просмотреть файл

@ -0,0 +1,672 @@
using Azure;
using Azure.Core;
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using Polly;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using YamlDotNet.System.Text.Json;
namespace common;
public sealed record WorkspaceApisUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "apis";
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceApisUri From(WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(workspaceName, serviceUri) };
}
public sealed record WorkspaceApiUri : ResourceUri
{
public required WorkspaceApisUri Parent { get; init; }
public required ApiName Name { get; init; }
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceApiUri From(ApiName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceApisUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceApisDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "apis";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceApisDirectory From(WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(workspaceName, serviceDirectory) };
public static Option<WorkspaceApisDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null
&& directory.Name == Name
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceApisDirectory { Parent = parent }
: Option<WorkspaceApisDirectory>.None;
}
public sealed record WorkspaceApiDirectory : ResourceDirectory
{
public required WorkspaceApisDirectory Parent { get; init; }
public required ApiName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static WorkspaceApiDirectory From(ApiName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApisDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceApiDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null
? from parent in WorkspaceApisDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = ApiName.From(directory!.Name)
select new WorkspaceApiDirectory
{
Parent = parent,
Name = name
}
: Option<WorkspaceApiDirectory>.None;
}
public sealed record WorkspaceApiInformationFile : ResourceFile
{
public required WorkspaceApiDirectory Parent { get; init; }
public static string Name { get; } = "apiInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceApiInformationFile From(ApiName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceApiInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null &&
file.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceApiInformationFile
{
Parent = parent
}
: Option<WorkspaceApiInformationFile>.None;
}
public sealed record WorkspaceApiDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required ApiCreateOrUpdateProperties Properties { get; init; }
public record ApiCreateOrUpdateProperties
{
[JsonPropertyName("path")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Path { get; init; }
[JsonPropertyName("apiRevision")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiRevision { get; init; }
[JsonPropertyName("apiRevisionDescription")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiRevisionDescription { get; init; }
[JsonPropertyName("apiVersion")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersion { get; init; }
[JsonPropertyName("apiVersionDescription")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersionDescription { get; init; }
[JsonPropertyName("apiVersionSetId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersionSetId { get; init; }
[JsonPropertyName("authenticationSettings")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public AuthenticationSettingsContract? AuthenticationSettings { get; init; }
[JsonPropertyName("contact")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiContactInformation? Contact { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("isCurrent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? IsCurrent { get; init; }
[JsonPropertyName("license")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiLicenseInformation? License { get; init; }
[JsonPropertyName("apiType")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiType { get; init; }
[JsonPropertyName("apiVersionSet")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiVersionSetContractDetails? ApiVersionSet { get; init; }
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("format")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Format { get; init; }
[JsonPropertyName("protocols")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? Protocols { get; init; }
[JsonPropertyName("serviceUrl")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? ServiceUrl { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
[JsonPropertyName("sourceApiId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? SourceApiId { get; init; }
[JsonPropertyName("translateRequiredQueryParameters")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? TranslateRequiredQueryParameters { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
[JsonPropertyName("wsdlSelector")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public WsdlSelectorContract? WsdlSelector { get; init; }
[JsonPropertyName("subscriptionKeyParameterNames")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public SubscriptionKeyParameterNamesContract? SubscriptionKeyParameterNames { get; init; }
[JsonPropertyName("subscriptionRequired")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? SubscriptionRequired { get; init; }
[JsonPropertyName("termsOfServiceUrl")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? TermsOfServiceUrl { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
[JsonPropertyName("type")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Type { get; init; }
public record AuthenticationSettingsContract
{
[JsonPropertyName("oAuth2")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OAuth2AuthenticationSettingsContract? OAuth2 { get; init; }
[JsonPropertyName("openid")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OpenIdAuthenticationSettingsContract? OpenId { get; init; }
}
public record OAuth2AuthenticationSettingsContract
{
[JsonPropertyName("authorizationServerId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? AuthorizationServerId { get; init; }
[JsonPropertyName("scope")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Scope { get; init; }
}
public record OpenIdAuthenticationSettingsContract
{
[JsonPropertyName("bearerTokenSendingMethods")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? BearerTokenSendingMethods { get; init; }
[JsonPropertyName("openidProviderId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? OpenIdProviderId { get; init; }
}
public record ApiContactInformation
{
[JsonPropertyName("email")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Email { get; init; }
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { get; init; }
[JsonPropertyName("url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? Url { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
}
public record ApiLicenseInformation
{
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { get; init; }
[JsonPropertyName("url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? Url { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
}
public record ApiVersionSetContractDetails
{
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Id { get; init; }
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { get; init; }
[JsonPropertyName("versionHeaderName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionHeaderName { get; init; }
[JsonPropertyName("versionQueryName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionQueryName { get; init; }
[JsonPropertyName("versioningScheme")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersioningScheme { get; init; }
}
public record SubscriptionKeyParameterNamesContract
{
[JsonPropertyName("header")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Header { get; init; }
[JsonPropertyName("query")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Query { get; init; }
}
public record WsdlSelectorContract
{
[JsonPropertyName("wsdlEndpointName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? WsdlEndpointName { get; init; }
[JsonPropertyName("wsdlServiceName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? WsdlServiceName { get; init; }
}
}
}
public static class WorkspaceApiModule
{
public static async ValueTask DeleteAll(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name =>
{
var resourceUri = new WorkspaceApiUri { Parent = uri, Name = name };
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<ApiName> ListNames(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(ApiName.From);
public static IAsyncEnumerable<(ApiName Name, WorkspaceApiDto Dto)> List(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new WorkspaceApiUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceApiDto> GetDto(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceApiDto>();
}
public static async ValueTask<Option<BinaryData>> TryGetSpecificationContents(this WorkspaceApiUri apiUri, ApiSpecification specification, HttpPipeline pipeline, CancellationToken cancellationToken)
{
if (specification is ApiSpecification.GraphQl)
{
return await apiUri.TryGetGraphQlSchema(pipeline, cancellationToken);
}
BinaryData? content;
try
{
var exportUri = GetExportUri(apiUri, specification, includeLink: true);
var downloadUri = await GetSpecificationDownloadUri(exportUri, pipeline, cancellationToken);
var nonAuthenticatedHttpPipeline = HttpPipelineBuilder.Build(ClientOptions.Default);
content = await nonAuthenticatedHttpPipeline.GetContent(downloadUri, cancellationToken);
}
// If we can't download the specification through the download link, get it directly.
catch (HttpRequestException exception) when (exception.StatusCode == HttpStatusCode.InternalServerError)
{
// Don't export XML specifications, as the non-link exports cannot be reimported.
if (specification is ApiSpecification.Wsdl or ApiSpecification.Wadl)
{
return Option<BinaryData>.None;
}
var exportUri = GetExportUri(apiUri, specification, includeLink: false);
var json = await pipeline.GetJsonObject(exportUri, cancellationToken);
var contentString = json.GetProperty("value") switch
{
JsonValue jsonValue => jsonValue.ToString(),
var node => node.ToJsonString(JsonObjectExtensions.SerializerOptions)
};
content = BinaryData.FromString(contentString);
}
// APIM exports OpenApiV2 to JSON. Convert to YAML if needed.
if (specification is ApiSpecification.OpenApi openApi && openApi.Format is OpenApiFormat.Yaml && openApi.Version is OpenApiVersion.V2)
{
var yaml = YamlConverter.SerializeJson(content.ToString());
content = BinaryData.FromString(yaml);
}
return content;
}
private static Uri GetExportUri(WorkspaceApiUri apiUri, ApiSpecification specification, bool includeLink)
{
var format = GetExportFormat(specification, includeLink);
return apiUri.ToUri()
.SetQueryParam("format", format)
.SetQueryParam("export", "true")
.SetQueryParam("api-version", "2022-09-01-preview")
.ToUri();
}
private static string GetExportFormat(ApiSpecification specification, bool includeLink)
{
var formatWithoutLink = specification switch
{
ApiSpecification.Wadl => "wadl",
ApiSpecification.Wsdl => "wsdl",
ApiSpecification.OpenApi openApiSpecification =>
(openApiSpecification.Version, openApiSpecification.Format) switch
{
(OpenApiVersion.V2, _) => "swagger",
(OpenApiVersion.V3, OpenApiFormat.Yaml) => "openapi",
(OpenApiVersion.V3, OpenApiFormat.Json) => "openapi+json",
_ => throw new NotSupportedException()
},
_ => throw new NotSupportedException()
};
return includeLink ? $"{formatWithoutLink}-link" : formatWithoutLink;
}
private static async ValueTask<Uri> GetSpecificationDownloadUri(Uri exportUri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var json = await pipeline.GetJsonObject(exportUri, cancellationToken);
return json.GetJsonObjectProperty("properties")
.GetJsonObjectProperty("value")
.GetAbsoluteUriProperty("link");
}
public static async ValueTask Delete(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask DeleteAllRevisions(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri()
.SetQueryParam("deleteRevisions", "true")
.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
if (dto.Properties.Format is null && dto.Properties.Value is null)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
else
{
if (dto.Properties.Type is "soap")
{
await PutSoapApi(uri, dto, pipeline, cancellationToken);
}
else
{
await PutNonSoapApi(uri, dto, pipeline, cancellationToken);
}
}
using var _ =
await new ResiliencePipelineBuilder<Response>()
.AddRetry(new()
{
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 5,
ShouldHandle = new PredicateBuilder<Response>().HandleResult(CreationInProgress)
})
.Build()
.ExecuteAsync(async cancellationToken =>
{
using var request = pipeline.CreateRequest(uri.ToUri(), RequestMethod.Get);
return await pipeline.SendRequestAsync(request, cancellationToken);
}, cancellationToken);
}
private static async ValueTask PutSoapApi(WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
// Import API with specification
var soapUri = uri.ToUri().SetQueryParam("import", "true").ToUri();
var soapDto = new ApiDto
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
{
Format = "wsdl",
Value = dto.Properties.Value,
ApiType = "soap",
DisplayName = dto.Properties.DisplayName,
Path = dto.Properties.Path,
Protocols = dto.Properties.Protocols,
ApiVersion = dto.Properties.ApiVersion,
ApiVersionDescription = dto.Properties.ApiVersionDescription,
ApiVersionSetId = dto.Properties.ApiVersionSetId
}
};
await pipeline.PutContent(soapUri, BinaryData.FromObjectAsJson(soapDto), cancellationToken);
// Put API again without specification
var updatedDto = dto with { Properties = dto.Properties with { Format = null, Value = null } };
// SOAP apis sometimes fail on put; retry if needed
await soapApiResiliencePipeline.Value
.ExecuteAsync(async cancellationToken => await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(updatedDto), cancellationToken), cancellationToken);
}
private static readonly Lazy<ResiliencePipeline> soapApiResiliencePipeline = new(() =>
new ResiliencePipelineBuilder()
.AddRetry(new()
{
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 3,
ShouldHandle = new PredicateBuilder().Handle<HttpRequestException>(exception => exception.StatusCode == HttpStatusCode.Conflict && exception.Message.Contains("IdentifierAlreadyInUse", StringComparison.OrdinalIgnoreCase))
})
.Build());
private static async ValueTask PutNonSoapApi(WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
// Put API without the specification.
var modelWithoutSpecification = dto with
{
Properties = dto.Properties with
{
Format = null,
Value = null,
ServiceUrl = null,
Type = dto.Properties.Type,
ApiType = dto.Properties.ApiType
}
};
await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(modelWithoutSpecification), cancellationToken);
// Put API again with specification
await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(dto), cancellationToken);
}
private static bool CreationInProgress(Response response)
{
if (response.Status != (int)HttpStatusCode.Created)
{
return false;
}
if (response.Headers.Any(header => header.Name.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)
&& header.Value.Contains("application/json", StringComparison.OrdinalIgnoreCase)) is false)
{
return false;
}
try
{
return response.Content.ToObjectFromJson<JsonObject>()
.TryGetJsonObjectProperty("properties")
.Bind(json => json.TryGetStringProperty("ProvisioningState"))
.ToOption()
.Where(state => state.Equals("InProgress", StringComparison.OrdinalIgnoreCase))
.IsSome;
}
catch (JsonException)
{
return false;
}
}
public static async ValueTask PutGraphQlSchema(this WorkspaceApiUri uri, BinaryData schema, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var contents = BinaryData.FromObjectAsJson(new JsonObject()
{
["properties"] = new JsonObject
{
["contentType"] = "application/vnd.ms-azure-apim.graphql.schema",
["document"] = new JsonObject()
{
["value"] = schema.ToString()
}
}
});
await pipeline.PutContent(uri.ToUri()
.AppendPathSegment("schemas")
.AppendPathSegment("graphql")
.ToUri(), contents, cancellationToken);
}
public static async ValueTask<Option<BinaryData>> TryGetGraphQlSchema(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var schemaUri = uri.ToUri()
.AppendPathSegment("schemas")
.AppendPathSegment("graphql")
.ToUri();
var schemaJsonOption = await pipeline.GetJsonObjectOption(schemaUri, cancellationToken);
return schemaJsonOption.Map(GetGraphQlSpecificationFromSchemaResponse);
}
private static BinaryData GetGraphQlSpecificationFromSchemaResponse(JsonObject responseJson)
{
var schema = responseJson.GetJsonObjectProperty("properties")
.GetJsonObjectProperty("document")
.GetNonEmptyOrWhiteSpaceStringProperty("value");
return BinaryData.FromString(schema);
}
public static IEnumerable<WorkspaceApiDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) =>
from workspaceDirectory in WorkspaceModule.ListDirectories(serviceDirectory)
let workspaceApisDirectory = new WorkspaceApisDirectory { Parent = workspaceDirectory }
where workspaceApisDirectory.ToDirectoryInfo().Exists()
from workspaceApiDirectoryInfo in workspaceApisDirectory.ToDirectoryInfo().ListDirectories("*")
let name = ApiName.From(workspaceApiDirectoryInfo.Name)
select new WorkspaceApiDirectory
{
Parent = workspaceApisDirectory,
Name = name
};
public static IEnumerable<WorkspaceApiInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
from workspaceApiDirectory in ListDirectories(serviceDirectory)
let informationFile = new WorkspaceApiInformationFile { Parent = workspaceApiDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this WorkspaceApiInformationFile file, WorkspaceApiDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask WriteSpecification(this WorkspaceApiSpecificationFile file, BinaryData contents, CancellationToken cancellationToken) =>
await file.ToFileInfo().OverwriteWithBinaryData(contents, cancellationToken);
public static async ValueTask<WorkspaceApiDto> ReadDto(this WorkspaceApiInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceApiDto>();
}
}

Просмотреть файл

@ -0,0 +1,207 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceApiReleaseName : ResourceName
{
private WorkspaceApiReleaseName(string value) : base(value) { }
public static WorkspaceApiReleaseName From(string value) => new(value);
}
public sealed record WorkspaceApiReleasesUri : ResourceUri
{
public required WorkspaceApiUri Parent { get; init; }
private static string PathSegment { get; } = "releases";
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceApiReleasesUri From(ApiName apiName, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceApiUri.From(apiName, workspaceName, serviceUri) };
}
public sealed record WorkspaceApiReleaseUri : ResourceUri
{
public required WorkspaceApiReleasesUri Parent { get; init; }
public required WorkspaceApiReleaseName Name { get; init; }
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceApiReleaseUri From(WorkspaceApiReleaseName name, ApiName apiName, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceApiReleasesUri.From(apiName, workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceApiReleasesDirectory : ResourceDirectory
{
public required WorkspaceApiDirectory Parent { get; init; }
private static string Name { get; } = "releases";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceApiReleasesDirectory From(ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory) };
public static Option<WorkspaceApiReleasesDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory?.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceApiReleasesDirectory { Parent = parent }
: Option<WorkspaceApiReleasesDirectory>.None;
}
public sealed record WorkspaceApiReleaseDirectory : ResourceDirectory
{
public required WorkspaceApiReleasesDirectory Parent { get; init; }
public required WorkspaceApiReleaseName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static WorkspaceApiReleaseDirectory From(WorkspaceApiReleaseName name, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiReleasesDirectory.From(apiName, workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceApiReleaseDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceApiReleasesDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = WorkspaceApiReleaseName.From(directory!.Name)
select new WorkspaceApiReleaseDirectory
{
Parent = parent,
Name = name
};
}
public sealed record WorkspaceApiReleaseInformationFile : ResourceFile
{
public required WorkspaceApiReleaseDirectory Parent { get; init; }
private static string Name { get; } = "releaseInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceApiReleaseInformationFile From(WorkspaceApiReleaseName name, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiReleaseDirectory.From(name, apiName, workspaceName, serviceDirectory)
};
public static Option<WorkspaceApiReleaseInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file?.Name == Name
? from parent in WorkspaceApiReleaseDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceApiReleaseInformationFile
{
Parent = parent
}
: Option<WorkspaceApiReleaseInformationFile>.None;
}
public sealed record WorkspaceApiReleaseDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required ApiReleaseContract Properties { get; init; }
public record ApiReleaseContract
{
[JsonPropertyName("apiId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiId { get; init; }
[JsonPropertyName("notes")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Notes { get; init; }
}
}
public static class WorkspaceApiReleaseModule
{
public static async ValueTask DeleteAll(this WorkspaceApiReleasesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name =>
{
var resourceUri = new WorkspaceApiReleaseUri { Parent = uri, Name = name };
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<WorkspaceApiReleaseName> ListNames(this WorkspaceApiReleasesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(WorkspaceApiReleaseName.From);
public static IAsyncEnumerable<(WorkspaceApiReleaseName Name, WorkspaceApiReleaseDto Dto)> List(this WorkspaceApiReleasesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new WorkspaceApiReleaseUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceApiReleaseDto> GetDto(this WorkspaceApiReleaseUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceApiReleaseDto>();
}
public static async ValueTask Delete(this WorkspaceApiReleaseUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceApiReleaseUri uri, WorkspaceApiReleaseDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static IEnumerable<WorkspaceApiReleaseDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) =>
from workspaceApiDirectory in WorkspaceApiModule.ListDirectories(serviceDirectory)
let workspaceApiReleasesDirectory = new WorkspaceApiReleasesDirectory { Parent = workspaceApiDirectory }
where workspaceApiReleasesDirectory.ToDirectoryInfo().Exists()
from workspaceApiReleaseDirectoryInfo in workspaceApiReleasesDirectory.ToDirectoryInfo().ListDirectories("*")
let name = WorkspaceApiReleaseName.From(workspaceApiReleaseDirectoryInfo.Name)
select new WorkspaceApiReleaseDirectory
{
Parent = workspaceApiReleasesDirectory,
Name = name
};
public static IEnumerable<WorkspaceApiReleaseInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
from workspaceApiReleaseDirectory in ListDirectories(serviceDirectory)
let informationFile = new WorkspaceApiReleaseInformationFile { Parent = workspaceApiReleaseDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this WorkspaceApiReleaseInformationFile file, WorkspaceApiReleaseDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceApiReleaseDto> ReadDto(this WorkspaceApiReleaseInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceApiReleaseDto>();
}
}

Просмотреть файл

@ -0,0 +1,209 @@
using LanguageExt;
using System;
using System.Collections.Immutable;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public abstract record WorkspaceApiSpecificationFile : ResourceFile
{
public required WorkspaceApiDirectory Parent { get; init; }
public abstract ApiSpecification Specification { get; }
public static WorkspaceApiSpecificationFile From(ApiSpecification specification, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
specification switch
{
ApiSpecification.GraphQl => WorkspaceGraphQlSpecificationFile.From(apiName, workspaceName, serviceDirectory),
ApiSpecification.Wadl => WorkspaceWadlSpecificationFile.From(apiName, workspaceName, serviceDirectory),
ApiSpecification.Wsdl => WorkspaceWsdlSpecificationFile.From(apiName, workspaceName, serviceDirectory),
ApiSpecification.OpenApi openApi => WorkspaceOpenApiSpecificationFile.From(openApi, apiName, workspaceName, serviceDirectory),
_ => throw new NotImplementedException()
};
public static async ValueTask<Option<WorkspaceApiSpecificationFile>> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await TryParse(file,
getFileContents: async file => await file.ReadAsBinaryData(cancellationToken),
serviceDirectory,
cancellationToken);
public static async ValueTask<Option<WorkspaceApiSpecificationFile>> TryParse(FileInfo? file, Func<FileInfo, ValueTask<BinaryData>> getFileContents, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var tryParseGraphQl = () => (from specificationFile in WorkspaceGraphQlSpecificationFile.TryParse(file, serviceDirectory)
select specificationFile as WorkspaceApiSpecificationFile).AsTask();
var tryParseWadl = () => (from specificationFile in WorkspaceWadlSpecificationFile.TryParse(file, serviceDirectory)
select specificationFile as WorkspaceApiSpecificationFile).AsTask();
var tryParseWsdl = () => (from specificationFile in WorkspaceWsdlSpecificationFile.TryParse(file, serviceDirectory)
select specificationFile as WorkspaceApiSpecificationFile).AsTask();
var tryParseOpenApi = async () => from specificationFile in await WorkspaceOpenApiSpecificationFile.TryParse(file, getFileContents, serviceDirectory, cancellationToken)
select specificationFile as WorkspaceApiSpecificationFile;
return await ImmutableArray.Create(tryParseGraphQl, tryParseWadl, tryParseWsdl, tryParseOpenApi)
.Pick(async (f, cancellationToken) => await f(), cancellationToken);
}
}
public sealed record WorkspaceGraphQlSpecificationFile : WorkspaceApiSpecificationFile
{
public override ApiSpecification Specification { get; } = new ApiSpecification.GraphQl();
public static string Name => "specification.graphql";
protected override FileInfo Value => new(Path.Combine(Parent.ToDirectoryInfo().FullName, Name));
public static WorkspaceGraphQlSpecificationFile From(ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory) };
public static Option<WorkspaceGraphQlSpecificationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceGraphQlSpecificationFile { Parent = parent }
: Option<WorkspaceGraphQlSpecificationFile>.None;
}
public sealed record WorkspaceWadlSpecificationFile : WorkspaceApiSpecificationFile
{
public override ApiSpecification Specification { get; } = new ApiSpecification.Wadl();
public static string Name => "specification.wadl";
protected override FileInfo Value => new(Path.Combine(Parent.ToDirectoryInfo().FullName, Name));
public static WorkspaceWadlSpecificationFile From(ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory) };
public static Option<WorkspaceWadlSpecificationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceWadlSpecificationFile { Parent = parent }
: Option<WorkspaceWadlSpecificationFile>.None;
}
public sealed record WorkspaceWsdlSpecificationFile : WorkspaceApiSpecificationFile
{
public override ApiSpecification Specification { get; } = new ApiSpecification.Wsdl();
public static string Name => "specification.wsdl";
protected override FileInfo Value => new(Path.Combine(Parent.ToDirectoryInfo().FullName, Name));
public static WorkspaceWsdlSpecificationFile From(ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory) };
public static Option<WorkspaceWsdlSpecificationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceWsdlSpecificationFile { Parent = parent }
: Option<WorkspaceWsdlSpecificationFile>.None;
}
public abstract record WorkspaceOpenApiSpecificationFile : WorkspaceApiSpecificationFile
{
public abstract OpenApiFormat Format { get; }
public required OpenApiVersion Version { get; init; }
public static WorkspaceOpenApiSpecificationFile From(ApiSpecification.OpenApi openApi, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
openApi.Format switch
{
OpenApiFormat.Json => JsonWorkspaceOpenApiSpecificationFile.From(openApi.Version, apiName, workspaceName, serviceDirectory),
OpenApiFormat.Yaml => YamlWorkspaceOpenApiSpecificationFile.From(openApi.Version, apiName, workspaceName, serviceDirectory),
_ => throw new NotImplementedException()
};
public static new async ValueTask<Option<WorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await TryParse(file,
getFileContents: async file => await file.ReadAsBinaryData(cancellationToken),
serviceDirectory,
cancellationToken);
public static new async ValueTask<Option<WorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, Func<FileInfo, ValueTask<BinaryData>> getFileContents, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var tryParseYaml = async () => from yaml in await YamlWorkspaceOpenApiSpecificationFile.TryParse(file, getFileContents, serviceDirectory, cancellationToken)
select yaml as WorkspaceOpenApiSpecificationFile;
var tryParseJson = async () => from json in await JsonWorkspaceOpenApiSpecificationFile.TryParse(file, getFileContents, serviceDirectory, cancellationToken)
select json as WorkspaceOpenApiSpecificationFile;
return await ImmutableArray.Create(tryParseYaml, tryParseJson)
.Pick(async (f, cancellationToken) => await f(), cancellationToken);
}
}
public sealed record YamlWorkspaceOpenApiSpecificationFile : WorkspaceOpenApiSpecificationFile
{
public override OpenApiFormat Format { get; } = new OpenApiFormat.Yaml();
public override ApiSpecification Specification => new ApiSpecification.OpenApi
{
Format = Format,
Version = Version
};
public static string Name { get; } = "specification.yaml";
protected override FileInfo Value => new(Path.Combine(Parent.ToDirectoryInfo().FullName, Name));
public static YamlWorkspaceOpenApiSpecificationFile From(OpenApiVersion version, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory),
Version = version
};
public static new async ValueTask<Option<YamlWorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await TryParse(file,
getFileContents: async file => await file.ReadAsBinaryData(cancellationToken),
serviceDirectory,
cancellationToken);
public static new async ValueTask<Option<YamlWorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, Func<FileInfo, ValueTask<BinaryData>> getFileContents, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
file is not null && file.Name == Name
? await WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
.BindTask(async parent => from version in await OpenApiVersion.TryParse(await getFileContents(file), cancellationToken)
select new YamlWorkspaceOpenApiSpecificationFile
{
Parent = parent,
Version = version
})
: Option<YamlWorkspaceOpenApiSpecificationFile>.None;
}
public sealed record JsonWorkspaceOpenApiSpecificationFile : WorkspaceOpenApiSpecificationFile
{
public override OpenApiFormat Format { get; } = new OpenApiFormat.Json();
public override ApiSpecification Specification => new ApiSpecification.OpenApi
{
Format = Format,
Version = Version
};
public static string Name { get; } = "specification.json";
protected override FileInfo Value => new(Path.Combine(Parent.ToDirectoryInfo().FullName, Name));
public static JsonWorkspaceOpenApiSpecificationFile From(OpenApiVersion version, ApiName apiName, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiDirectory.From(apiName, workspaceName, serviceDirectory),
Version = version
};
public static new async ValueTask<Option<JsonWorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await TryParse(file,
getFileContents: async file => await file.ReadAsBinaryData(cancellationToken),
serviceDirectory,
cancellationToken);
public static new async ValueTask<Option<JsonWorkspaceOpenApiSpecificationFile>> TryParse(FileInfo? file, Func<FileInfo, ValueTask<BinaryData>> getFileContents, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
file is not null && file.Name == Name
? await WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
.BindTask(async parent => from version in await OpenApiVersion.TryParse(await getFileContents(file), cancellationToken)
select new JsonWorkspaceOpenApiSpecificationFile
{
Parent = parent,
Version = version
})
: Option<JsonWorkspaceOpenApiSpecificationFile>.None;
}

Просмотреть файл

@ -0,0 +1,339 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceBackendsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "backends";
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceBackendsUri From(WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(workspaceName, serviceUri) };
}
public sealed record WorkspaceBackendUri : ResourceUri
{
public required WorkspaceBackendsUri Parent { get; init; }
public required BackendName Name { get; init; }
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceBackendUri From(BackendName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceBackendsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceBackendsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "backends";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceBackendsDirectory From(WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(workspaceName, serviceDirectory) };
public static Option<WorkspaceBackendsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory?.Name == Name
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceBackendsDirectory { Parent = parent }
: Option<WorkspaceBackendsDirectory>.None;
}
public sealed record WorkspaceBackendDirectory : ResourceDirectory
{
public required WorkspaceBackendsDirectory Parent { get; init; }
public required BackendName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static WorkspaceBackendDirectory From(BackendName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceBackendsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceBackendDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceBackendsDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = BackendName.From(directory!.Name)
select new WorkspaceBackendDirectory
{
Parent = parent,
Name = name
};
}
public sealed record WorkspaceBackendInformationFile : ResourceFile
{
public required WorkspaceBackendDirectory Parent { get; init; }
private static string Name { get; } = "backendInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceBackendInformationFile From(BackendName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceBackendDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceBackendInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file?.Name == Name
? from parent in WorkspaceBackendDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceBackendInformationFile
{
Parent = parent
}
: Option<WorkspaceBackendInformationFile>.None;
}
public sealed record WorkspaceBackendDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required BackendContract Properties { get; init; }
public record BackendContract
{
[JsonPropertyName("credentials")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendCredentialsContract? Credentials { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendProperties? Properties { get; init; }
[JsonPropertyName("protocol")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Protocol { get; init; }
[JsonPropertyName("proxy")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendProxyContract? Proxy { get; init; }
[JsonPropertyName("resourceId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ResourceId { get; init; }
[JsonPropertyName("title")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Title { get; init; }
[JsonPropertyName("tls")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendTlsProperties? Tls { get; init; }
[JsonPropertyName("url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? Url { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
}
public record BackendCredentialsContract
{
[JsonPropertyName("authorization")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendAuthorizationHeaderCredentials? Authorization { get; init; }
[JsonPropertyName("certificate")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableList<string>? Certificate { get; init; }
[JsonPropertyName("certificateIds")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableList<string>? CertificateIds { get; init; }
[JsonPropertyName("header")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonObject? Header { get; init; }
[JsonPropertyName("query")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonObject? Query { get; init; }
}
public record BackendAuthorizationHeaderCredentials
{
[JsonPropertyName("parameter")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Parameter { get; init; }
[JsonPropertyName("scheme")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Scheme { get; init; }
}
public record BackendProperties
{
[JsonPropertyName("serviceFabricCluster")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BackendServiceFabricClusterProperties? ServiceFabricCluster { get; init; }
}
public record BackendProxyContract
{
[JsonPropertyName("password")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Password { get; init; }
[JsonPropertyName("url")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? Url { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
[JsonPropertyName("username")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Username { get; init; }
}
public record BackendServiceFabricClusterProperties
{
[JsonPropertyName("clientCertificateId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ClientCertificateId { get; init; }
[JsonPropertyName("clientCertificatethumbprint")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ClientCertificateThumbprint { get; init; }
[JsonPropertyName("managementEndpoints")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableList<string>? ManagementEndpoints { get; init; }
[JsonPropertyName("maxPartitionResolutionRetries")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? MaxPartitionResolutionRetries { get; init; }
[JsonPropertyName("serverCertificateThumbprints")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableList<string>? ServerCertificateThumbprints { get; init; }
[JsonPropertyName("serverX509Names")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableList<X509CertificateName>? ServerX509Names { get; init; }
}
public record BackendTlsProperties
{
[JsonPropertyName("validateCertificateChain")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? ValidateCertificateChain { get; init; }
[JsonPropertyName("validateCertificateName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? ValidateCertificateName { get; init; }
}
public record X509CertificateName
{
[JsonPropertyName("issuerCertificateThumbprint")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? IssuerCertificateThumbprint { get; init; }
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { get; init; }
}
}
public static class WorkspaceBackendModule
{
public static async ValueTask DeleteAll(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name =>
{
var resourceUri = new WorkspaceBackendUri { Parent = uri, Name = name };
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<BackendName> ListNames(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(BackendName.From);
public static IAsyncEnumerable<(BackendName Name, WorkspaceBackendDto Dto)> List(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new WorkspaceBackendUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceBackendDto> GetDto(this WorkspaceBackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceBackendDto>();
}
public static async ValueTask Delete(this WorkspaceBackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceBackendUri uri, WorkspaceBackendDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static IEnumerable<WorkspaceBackendDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) =>
from workspaceDirectory in WorkspaceModule.ListDirectories(serviceDirectory)
let workspacebackendsDirectory = new WorkspaceBackendsDirectory { Parent = workspaceDirectory }
where workspacebackendsDirectory.ToDirectoryInfo().Exists()
from workspaceBackendDirectoryInfo in workspacebackendsDirectory.ToDirectoryInfo().ListDirectories("*")
let name = BackendName.From(workspaceBackendDirectoryInfo.Name)
select new WorkspaceBackendDirectory
{
Parent = workspacebackendsDirectory,
Name = name
};
public static IEnumerable<WorkspaceBackendInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
from workspaceBackendDirectory in ListDirectories(serviceDirectory)
let informationFile = new WorkspaceBackendInformationFile { Parent = workspaceBackendDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this WorkspaceBackendInformationFile file, WorkspaceBackendDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceBackendDto> ReadDto(this WorkspaceBackendInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceBackendDto>();
}
}

Просмотреть файл

@ -0,0 +1,272 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceDiagnosticsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "diagnostics";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceDiagnosticsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceDiagnosticUri : ResourceUri
{
public required WorkspaceDiagnosticsUri Parent { get; init; }
public required DiagnosticName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceDiagnosticUri From(DiagnosticName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceDiagnosticsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceDiagnosticsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "diagnostics";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceDiagnosticsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceDiagnosticsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceDiagnosticsDirectory { Parent = parent }
: Option<WorkspaceDiagnosticsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceDiagnosticDirectory : ResourceDirectory
{
public required WorkspaceDiagnosticsDirectory Parent { get; init; }
public required DiagnosticName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceDiagnosticDirectory From(DiagnosticName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceDiagnosticsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceDiagnosticDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceDiagnosticsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceDiagnosticDirectory
{
Parent = parent,
Name = DiagnosticName.From(directory!.Name)
};
}
public sealed record WorkspaceDiagnosticInformationFile : ResourceFile
{
public required WorkspaceDiagnosticDirectory Parent { get; init; }
private static string Name { get; } = "diagnosticInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceDiagnosticInformationFile From(DiagnosticName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceDiagnosticDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceDiagnosticInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceDiagnosticDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceDiagnosticInformationFile { Parent = parent }
: Option<WorkspaceDiagnosticInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceDiagnosticDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required DiagnosticContract Properties { get; init; }
public sealed record DiagnosticContract
{
[JsonPropertyName("loggerId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? LoggerId { get; init; }
[JsonPropertyName("alwaysLog")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? AlwaysLog { get; init; }
[JsonPropertyName("backend")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public PipelineDiagnosticSettings? Backend { get; init; }
[JsonPropertyName("frontend")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public PipelineDiagnosticSettings? Frontend { get; init; }
[JsonPropertyName("httpCorrelationProtocol")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? HttpCorrelationProtocol { get; init; }
[JsonPropertyName("logClientIp")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? LogClientIp { get; init; }
[JsonPropertyName("metrics")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? Metrics { get; init; }
[JsonPropertyName("operationNameFormat")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? OperationNameFormat { get; init; }
[JsonPropertyName("sampling")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public SamplingSettings? Sampling { get; init; }
[JsonPropertyName("verbosity")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Verbosity { get; init; }
}
public sealed record PipelineDiagnosticSettings
{
[JsonPropertyName("request")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public HttpMessageDiagnostic? Request { get; init; }
[JsonPropertyName("response")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public HttpMessageDiagnostic? Response { get; init; }
}
public sealed record HttpMessageDiagnostic
{
[JsonPropertyName("body")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public BodyDiagnosticSettings? Body { get; init; }
[JsonPropertyName("dataMasking")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DataMasking? DataMasking { get; init; }
[JsonPropertyName("headers")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? Headers { get; init; }
}
public sealed record BodyDiagnosticSettings
{
[JsonPropertyName("bytes")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? Bytes { get; init; }
}
public sealed record DataMasking
{
[JsonPropertyName("headers")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<DataMaskingEntity>? Headers { get; init; }
[JsonPropertyName("queryParams")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<DataMaskingEntity>? QueryParams { get; init; }
}
public sealed record DataMaskingEntity
{
[JsonPropertyName("mode")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Mode { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
}
public sealed record SamplingSettings
{
[JsonPropertyName("percentage")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public float? Percentage { get; init; }
[JsonPropertyName("samplingType")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? SamplingType { get; init; }
}
}
public static class WorkspaceDiagnosticModule
{
public static IAsyncEnumerable<DiagnosticName> ListNames(this WorkspaceDiagnosticsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(DiagnosticName.From);
public static IAsyncEnumerable<(DiagnosticName Name, WorkspaceDiagnosticDto Dto)> List(this WorkspaceDiagnosticsUri workspaceDiagnosticsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceDiagnosticsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceDiagnosticUri { Parent = workspaceDiagnosticsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceDiagnosticDto> GetDto(this WorkspaceDiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceDiagnosticDto>();
}
public static async ValueTask Delete(this WorkspaceDiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceDiagnosticUri uri, WorkspaceDiagnosticDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceDiagnosticInformationFile file, WorkspaceDiagnosticDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceDiagnosticDto> ReadDto(this WorkspaceDiagnosticInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceDiagnosticDto>();
}
}

Просмотреть файл

@ -0,0 +1,183 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceGroupsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "groups";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceGroupsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceGroupUri : ResourceUri
{
public required WorkspaceGroupsUri Parent { get; init; }
public required GroupName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceGroupUri From(GroupName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceGroupsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceGroupsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "groups";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceGroupsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceGroupsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceGroupsDirectory { Parent = parent }
: Option<WorkspaceGroupsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceGroupDirectory : ResourceDirectory
{
public required WorkspaceGroupsDirectory Parent { get; init; }
public required GroupName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceGroupDirectory From(GroupName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceGroupsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceGroupDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceGroupsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceGroupDirectory
{
Parent = parent,
Name = GroupName.From(directory!.Name)
};
}
public sealed record WorkspaceGroupInformationFile : ResourceFile
{
public required WorkspaceGroupDirectory Parent { get; init; }
private static string Name { get; } = "groupInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceGroupInformationFile From(GroupName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceGroupDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceGroupInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceGroupDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceGroupInformationFile { Parent = parent }
: Option<WorkspaceGroupInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceGroupDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required GroupContract Properties { get; init; }
public sealed record GroupContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("externalId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ExternalId { get; init; }
[JsonPropertyName("type")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Type { get; init; }
}
}
public static class WorkspaceGroupModule
{
public static IAsyncEnumerable<GroupName> ListNames(this WorkspaceGroupsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(GroupName.From);
public static IAsyncEnumerable<(GroupName Name, WorkspaceGroupDto Dto)> List(this WorkspaceGroupsUri workspaceGroupsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceGroupsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceGroupUri { Parent = workspaceGroupsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceGroupDto> GetDto(this WorkspaceGroupUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceGroupDto>();
}
public static async ValueTask Delete(this WorkspaceGroupUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceGroupUri uri, WorkspaceGroupDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceGroupInformationFile file, WorkspaceGroupDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceGroupDto> ReadDto(this WorkspaceGroupInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceGroupDto>();
}
}

Просмотреть файл

@ -0,0 +1,186 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceLoggersUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "loggers";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceLoggersUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceLoggerUri : ResourceUri
{
public required WorkspaceLoggersUri Parent { get; init; }
public required LoggerName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceLoggerUri From(LoggerName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceLoggersUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceLoggersDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "loggers";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceLoggersDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceLoggersDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceLoggersDirectory { Parent = parent }
: Option<WorkspaceLoggersDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceLoggerDirectory : ResourceDirectory
{
public required WorkspaceLoggersDirectory Parent { get; init; }
public required LoggerName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceLoggerDirectory From(LoggerName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceLoggersDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceLoggerDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceLoggersDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceLoggerDirectory
{
Parent = parent,
Name = LoggerName.From(directory!.Name)
};
}
public sealed record WorkspaceLoggerInformationFile : ResourceFile
{
public required WorkspaceLoggerDirectory Parent { get; init; }
private static string Name { get; } = "loggerInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceLoggerInformationFile From(LoggerName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceLoggerDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceLoggerInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceLoggerDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceLoggerInformationFile { Parent = parent }
: Option<WorkspaceLoggerInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceLoggerDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required LoggerContract Properties { get; init; }
public record LoggerContract
{
[JsonPropertyName("loggerType")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? LoggerType { get; init; }
[JsonPropertyName("credentials")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonObject? Credentials { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("isBuffered")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? IsBuffered { get; init; }
[JsonPropertyName("resourceId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ResourceId { get; init; }
}
}
public static class WorkspaceLoggerModule
{
public static IAsyncEnumerable<LoggerName> ListNames(this WorkspaceLoggersUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(LoggerName.From);
public static IAsyncEnumerable<(LoggerName Name, WorkspaceLoggerDto Dto)> List(this WorkspaceLoggersUri workspaceLoggersUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceLoggersUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceLoggerUri { Parent = workspaceLoggersUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceLoggerDto> GetDto(this WorkspaceLoggerUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceLoggerDto>();
}
public static async ValueTask Delete(this WorkspaceLoggerUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceLoggerUri uri, WorkspaceLoggerDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceLoggerInformationFile file, WorkspaceLoggerDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceLoggerDto> ReadDto(this WorkspaceLoggerInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceLoggerDto>();
}
}

Просмотреть файл

@ -0,0 +1,197 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceNamedValuesUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "namedValues";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceNamedValuesUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceNamedValueUri : ResourceUri
{
public required WorkspaceNamedValuesUri Parent { get; init; }
public required NamedValueName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceNamedValueUri From(NamedValueName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceNamedValuesUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceNamedValuesDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "named values";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceNamedValuesDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceNamedValuesDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceNamedValuesDirectory { Parent = parent }
: Option<WorkspaceNamedValuesDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceNamedValueDirectory : ResourceDirectory
{
public required WorkspaceNamedValuesDirectory Parent { get; init; }
public required NamedValueName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceNamedValueDirectory From(NamedValueName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceNamedValuesDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceNamedValueDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceNamedValuesDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceNamedValueDirectory
{
Parent = parent,
Name = NamedValueName.From(directory!.Name)
};
}
public sealed record WorkspaceNamedValueInformationFile : ResourceFile
{
public required WorkspaceNamedValueDirectory Parent { get; init; }
private static string Name { get; } = "namedValueInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceNamedValueInformationFile From(NamedValueName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceNamedValueDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceNamedValueInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceNamedValueDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceNamedValueInformationFile { Parent = parent }
: Option<WorkspaceNamedValueInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceNamedValueDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required NamedValueContract Properties { get; init; }
public sealed record NamedValueContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("keyVault")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public KeyVaultContract? KeyVault { get; init; }
[JsonPropertyName("secret")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? Secret { get; init; }
[JsonPropertyName("tags")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? Tags { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
}
public sealed record KeyVaultContract
{
[JsonPropertyName("identityClientId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? IdentityClientId { get; init; }
[JsonPropertyName("secretIdentifier")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? SecretIdentifier { get; init; }
}
}
public static class WorkspaceNamedValueModule
{
public static IAsyncEnumerable<NamedValueName> ListNames(this WorkspaceNamedValuesUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(NamedValueName.From);
public static IAsyncEnumerable<(NamedValueName Name, WorkspaceNamedValueDto Dto)> List(this WorkspaceNamedValuesUri workspaceNamedValuesUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceNamedValuesUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceNamedValueUri { Parent = workspaceNamedValuesUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceNamedValueDto> GetDto(this WorkspaceNamedValueUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceNamedValueDto>();
}
public static async ValueTask Delete(this WorkspaceNamedValueUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceNamedValueUri uri, WorkspaceNamedValueDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceNamedValueInformationFile file, WorkspaceNamedValueDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceNamedValueDto> ReadDto(this WorkspaceNamedValueInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceNamedValueDto>();
}
}

Просмотреть файл

@ -0,0 +1,166 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspacePolicyName : ResourceName
{
private WorkspacePolicyName(string value) : base(value) { }
public static WorkspacePolicyName From(string value) => new(value);
}
public sealed record WorkspacePoliciesUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "policies";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspacePoliciesUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspacePolicyUri : ResourceUri
{
public required WorkspacePoliciesUri Parent { get; init; }
public required WorkspacePolicyName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspacePolicyUri From(WorkspacePolicyName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspacePoliciesUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspacePolicyFile : ResourceFile
{
public required WorkspaceDirectory Parent { get; init; }
public required WorkspacePolicyName Name { get; init; }
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile($"{Name}.xml");
public static WorkspacePolicyFile From(WorkspacePolicyName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspacePolicyFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
from name in TryParseWorkspacePolicyName(file)
from parent in WorkspaceDirectory.TryParse(file?.Directory, serviceDirectory)
select new WorkspacePolicyFile
{
Name = name,
Parent = parent
};
internal static Option<WorkspacePolicyName> TryParseWorkspacePolicyName(FileInfo? file) =>
file?.Name.EndsWith(".xml", StringComparison.Ordinal) switch
{
true => WorkspacePolicyName.From(Path.GetFileNameWithoutExtension(file.Name)),
_ => Option<WorkspacePolicyName>.None
};
}
public sealed record WorkspacePolicyDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required WorkspacePolicyContract Properties { get; init; }
public sealed record WorkspacePolicyContract
{
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("format")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Format { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
}
}
public static class WorkspacePolicyModule
{
public static async IAsyncEnumerable<WorkspacePolicyName> ListNames(this WorkspacePoliciesUri uri, HttpPipeline pipeline, [EnumeratorCancellation] CancellationToken cancellationToken)
{
// The REST API call to list policy names returns incorrectly formatted names.
// For now, we'll return the single policy named "policy" if it exists
var policyName = WorkspacePolicyName.From("policy");
var policyUri = uri.ToUri().AppendPathSegment(policyName.Value).ToUri();
var option = await pipeline.GetJsonObjectOption(policyUri, cancellationToken);
if (option.IsSome)
{
yield return policyName;
}
}
public static IAsyncEnumerable<(WorkspacePolicyName Name, WorkspacePolicyDto Dto)> List(this WorkspacePoliciesUri workspacePoliciesUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspacePoliciesUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspacePolicyUri { Parent = workspacePoliciesUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspacePolicyDto> GetDto(this WorkspacePolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var contentUri = uri.ToUri().AppendQueryParam("format", "rawxml").ToUri();
var content = await pipeline.GetContent(contentUri, cancellationToken);
return content.ToObjectFromJson<WorkspacePolicyDto>();
}
public static async ValueTask Delete(this WorkspacePolicyUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspacePolicyUri uri, WorkspacePolicyDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static IEnumerable<WorkspacePolicyFile> ListPolicyFiles(WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory)
{
var workspaceDirectory = WorkspaceDirectory.From(workspaceName, serviceDirectory);
return workspaceDirectory.ToDirectoryInfo()
.ListFiles("*")
.Choose(WorkspacePolicyFile.TryParseWorkspacePolicyName)
.Select(name => new WorkspacePolicyFile { Name = name, Parent = workspaceDirectory });
}
public static async ValueTask WritePolicy(this WorkspacePolicyFile file, string policy, CancellationToken cancellationToken)
{
var content = BinaryData.FromString(policy);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<string> ReadPolicy(this WorkspacePolicyFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToString();
}
}

Просмотреть файл

@ -0,0 +1,209 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspacePolicyFragmentsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "policyFragments";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspacePolicyFragmentsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspacePolicyFragmentUri : ResourceUri
{
public required WorkspacePolicyFragmentsUri Parent { get; init; }
public required PolicyFragmentName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspacePolicyFragmentUri From(PolicyFragmentName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspacePolicyFragmentsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspacePolicyFragmentsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "policy fragments";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspacePolicyFragmentsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspacePolicyFragmentsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspacePolicyFragmentsDirectory { Parent = parent }
: Option<WorkspacePolicyFragmentsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspacePolicyFragmentDirectory : ResourceDirectory
{
public required WorkspacePolicyFragmentsDirectory Parent { get; init; }
public required PolicyFragmentName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspacePolicyFragmentDirectory From(PolicyFragmentName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspacePolicyFragmentsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspacePolicyFragmentDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspacePolicyFragmentsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspacePolicyFragmentDirectory
{
Parent = parent,
Name = PolicyFragmentName.From(directory!.Name)
};
}
public sealed record WorkspacePolicyFragmentInformationFile : ResourceFile
{
public required WorkspacePolicyFragmentDirectory Parent { get; init; }
private static string Name { get; } = "policyFragmentInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspacePolicyFragmentInformationFile From(PolicyFragmentName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspacePolicyFragmentDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspacePolicyFragmentInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspacePolicyFragmentDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspacePolicyFragmentInformationFile { Parent = parent }
: Option<WorkspacePolicyFragmentInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspacePolicyFragmentPolicyFile : ResourceFile
{
public required WorkspacePolicyFragmentDirectory Parent { get; init; }
private static string Name { get; } = "policy.xml";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspacePolicyFragmentPolicyFile From(PolicyFragmentName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = new WorkspacePolicyFragmentDirectory
{
Parent = WorkspacePolicyFragmentsDirectory.From(workspaceName, serviceDirectory),
Name = name
}
};
public static Option<WorkspacePolicyFragmentPolicyFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null && file.Name == Name
? from parent in WorkspacePolicyFragmentDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspacePolicyFragmentPolicyFile { Parent = parent }
: Option<WorkspacePolicyFragmentPolicyFile>.None;
}
public sealed record WorkspacePolicyFragmentDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required PolicyFragmentContract Properties { get; init; }
public sealed record PolicyFragmentContract
{
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("format")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Format { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
}
}
public static class WorkspacePolicyFragmentModule
{
public static IAsyncEnumerable<PolicyFragmentName> ListNames(this WorkspacePolicyFragmentsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(PolicyFragmentName.From);
public static IAsyncEnumerable<(PolicyFragmentName Name, WorkspacePolicyFragmentDto Dto)> List(this WorkspacePolicyFragmentsUri workspacePolicyFragmentsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspacePolicyFragmentsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspacePolicyFragmentUri { Parent = workspacePolicyFragmentsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspacePolicyFragmentDto> GetDto(this WorkspacePolicyFragmentUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var contentUri = uri.ToUri().AppendQueryParam("format", "rawxml").ToUri();
var content = await pipeline.GetContent(contentUri, cancellationToken);
return content.ToObjectFromJson<WorkspacePolicyFragmentDto>();
}
public static async ValueTask Delete(this WorkspacePolicyFragmentUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspacePolicyFragmentUri uri, WorkspacePolicyFragmentDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspacePolicyFragmentInformationFile file, WorkspacePolicyFragmentDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspacePolicyFragmentDto> ReadDto(this WorkspacePolicyFragmentInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspacePolicyFragmentDto>();
}
public static async ValueTask WritePolicy(this WorkspacePolicyFragmentPolicyFile file, string policy, CancellationToken cancellationToken)
{
var content = BinaryData.FromString(policy);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,193 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceProductsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "products";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceProductsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceProductUri : ResourceUri
{
public required WorkspaceProductsUri Parent { get; init; }
public required ProductName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceProductUri From(ProductName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceProductsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceProductsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "products";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceProductsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceProductsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceProductsDirectory { Parent = parent }
: Option<WorkspaceProductsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceProductDirectory : ResourceDirectory
{
public required WorkspaceProductsDirectory Parent { get; init; }
public required ProductName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceProductDirectory From(ProductName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceProductsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceProductDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceProductsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceProductDirectory
{
Parent = parent,
Name = ProductName.From(directory!.Name)
};
}
public sealed record WorkspaceProductInformationFile : ResourceFile
{
public required WorkspaceProductDirectory Parent { get; init; }
private static string Name { get; } = "productInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceProductInformationFile From(ProductName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceProductDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceProductInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceProductDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceProductInformationFile { Parent = parent }
: Option<WorkspaceProductInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceProductDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required ProductContract Properties { get; init; }
public record ProductContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("approvalRequired")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? ApprovalRequired { get; init; }
[JsonPropertyName("state")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? State { get; init; }
[JsonPropertyName("subscriptionRequired")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? SubscriptionRequired { get; init; }
[JsonPropertyName("subscriptionsLimit")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int? SubscriptionsLimit { get; init; }
[JsonPropertyName("terms")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Terms { get; init; }
}
}
public static class WorkspaceProductModule
{
public static IAsyncEnumerable<ProductName> ListNames(this WorkspaceProductsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(ProductName.From);
public static IAsyncEnumerable<(ProductName Name, WorkspaceProductDto Dto)> List(this WorkspaceProductsUri workspaceProductsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceProductsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceProductUri { Parent = workspaceProductsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceProductDto> GetDto(this WorkspaceProductUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceProductDto>();
}
public static async ValueTask Delete(this WorkspaceProductUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceProductUri uri, WorkspaceProductDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceProductInformationFile file, WorkspaceProductDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceProductDto> ReadDto(this WorkspaceProductInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceProductDto>();
}
}

Просмотреть файл

@ -0,0 +1,193 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceSubscriptionsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "subscriptions";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceSubscriptionsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceSubscriptionUri : ResourceUri
{
public required WorkspaceSubscriptionsUri Parent { get; init; }
public required SubscriptionName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceSubscriptionUri From(SubscriptionName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceSubscriptionsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceSubscriptionsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "subscriptions";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceSubscriptionsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceSubscriptionsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceSubscriptionsDirectory { Parent = parent }
: Option<WorkspaceSubscriptionsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceSubscriptionDirectory : ResourceDirectory
{
public required WorkspaceSubscriptionsDirectory Parent { get; init; }
public required SubscriptionName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceSubscriptionDirectory From(SubscriptionName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceSubscriptionsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceSubscriptionDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceSubscriptionsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceSubscriptionDirectory
{
Parent = parent,
Name = SubscriptionName.From(directory!.Name)
};
}
public sealed record WorkspaceSubscriptionInformationFile : ResourceFile
{
public required WorkspaceSubscriptionDirectory Parent { get; init; }
private static string Name { get; } = "subscriptionInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceSubscriptionInformationFile From(SubscriptionName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceSubscriptionDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceSubscriptionInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceSubscriptionDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceSubscriptionInformationFile { Parent = parent }
: Option<WorkspaceSubscriptionInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceSubscriptionDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required SubscriptionContract Properties { get; init; }
public sealed record SubscriptionContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("scope")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Scope { get; init; }
[JsonPropertyName("allowTracing")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? AllowTracing { get; init; }
[JsonPropertyName("ownerId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? OwnerId { get; init; }
[JsonPropertyName("primaryKey")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? PrimaryKey { get; init; }
[JsonPropertyName("secondaryKey")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? SecondaryKey { get; init; }
[JsonPropertyName("state")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? State { get; init; }
}
}
public static class WorkspaceSubscriptionModule
{
public static IAsyncEnumerable<SubscriptionName> ListNames(this WorkspaceSubscriptionsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(SubscriptionName.From);
public static IAsyncEnumerable<(SubscriptionName Name, WorkspaceSubscriptionDto Dto)> List(this WorkspaceSubscriptionsUri workspaceSubscriptionsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceSubscriptionsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceSubscriptionUri { Parent = workspaceSubscriptionsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceSubscriptionDto> GetDto(this WorkspaceSubscriptionUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceSubscriptionDto>();
}
public static async ValueTask Delete(this WorkspaceSubscriptionUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceSubscriptionUri uri, WorkspaceSubscriptionDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceSubscriptionInformationFile file, WorkspaceSubscriptionDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceSubscriptionDto> ReadDto(this WorkspaceSubscriptionInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceSubscriptionDto>();
}
}

Просмотреть файл

@ -0,0 +1,169 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceTagsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "tags";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceTagsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceTagUri : ResourceUri
{
public required WorkspaceTagsUri Parent { get; init; }
public required TagName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceTagUri From(TagName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceTagsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceTagsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "tags";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceTagsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceTagsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceTagsDirectory { Parent = parent }
: Option<WorkspaceTagsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceTagDirectory : ResourceDirectory
{
public required WorkspaceTagsDirectory Parent { get; init; }
public required TagName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceTagDirectory From(TagName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceTagsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceTagDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceTagsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceTagDirectory
{
Parent = parent,
Name = TagName.From(directory!.Name)
};
}
public sealed record WorkspaceTagInformationFile : ResourceFile
{
public required WorkspaceTagDirectory Parent { get; init; }
private static string Name { get; } = "tagInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceTagInformationFile From(TagName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceTagDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceTagInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceTagDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceTagInformationFile { Parent = parent }
: Option<WorkspaceTagInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceTagDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required TagContract Properties { get; init; }
public sealed record TagContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
}
}
public static class WorkspaceTagModule
{
public static IAsyncEnumerable<TagName> ListNames(this WorkspaceTagsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(TagName.From);
public static IAsyncEnumerable<(TagName Name, WorkspaceTagDto Dto)> List(this WorkspaceTagsUri workspaceTagsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceTagsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceTagUri { Parent = workspaceTagsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceTagDto> GetDto(this WorkspaceTagUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceTagDto>();
}
public static async ValueTask Delete(this WorkspaceTagUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceTagUri uri, WorkspaceTagDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceTagInformationFile file, WorkspaceTagDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceTagDto> ReadDto(this WorkspaceTagInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceTagDto>();
}
}

Просмотреть файл

@ -0,0 +1,185 @@
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public sealed record WorkspaceVersionSetsUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "apiVersionSets";
protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceVersionSetsUri From(WorkspaceName name, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(name, serviceUri) };
}
public sealed record WorkspaceVersionSetUri : ResourceUri
{
public required WorkspaceVersionSetsUri Parent { get; init; }
public required VersionSetName Name { get; init; }
protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceVersionSetUri From(VersionSetName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceVersionSetsUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceVersionSetsDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "version sets";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceVersionSetsDirectory From(WorkspaceName name, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(name, serviceDirectory) };
public static Option<WorkspaceVersionSetsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
IsDirectoryNameValid(directory)
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceVersionSetsDirectory { Parent = parent }
: Option<WorkspaceVersionSetsDirectory>.None;
internal static bool IsDirectoryNameValid([NotNullWhen(true)] DirectoryInfo? directory) =>
directory?.Name == Name;
}
public sealed record WorkspaceVersionSetDirectory : ResourceDirectory
{
public required WorkspaceVersionSetsDirectory Parent { get; init; }
public required VersionSetName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.ToString());
public static WorkspaceVersionSetDirectory From(VersionSetName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceVersionSetsDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceVersionSetDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
from parent in WorkspaceVersionSetsDirectory.TryParse(directory?.Parent, serviceDirectory)
select new WorkspaceVersionSetDirectory
{
Parent = parent,
Name = VersionSetName.From(directory!.Name)
};
}
public sealed record WorkspaceVersionSetInformationFile : ResourceFile
{
public required WorkspaceVersionSetDirectory Parent { get; init; }
private static string Name { get; } = "versionSetInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceVersionSetInformationFile From(VersionSetName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceVersionSetDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceVersionSetInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
IsFileNameValid(file)
? from parent in WorkspaceVersionSetDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceVersionSetInformationFile { Parent = parent }
: Option<WorkspaceVersionSetInformationFile>.None;
internal static bool IsFileNameValid([NotNullWhen(true)] FileInfo? file) =>
file?.Name == Name;
}
public sealed record WorkspaceVersionSetDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required VersionSetContract Properties { get; init; }
public sealed record VersionSetContract
{
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("versioningScheme")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersioningScheme { get; init; }
[JsonPropertyName("versionQueryName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionQueryName { get; init; }
[JsonPropertyName("versionHeaderName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionHeaderName { get; init; }
}
}
public static class WorkspaceVersionSetModule
{
public static IAsyncEnumerable<VersionSetName> ListNames(this WorkspaceVersionSetsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(VersionSetName.From);
public static IAsyncEnumerable<(VersionSetName Name, WorkspaceVersionSetDto Dto)> List(this WorkspaceVersionSetsUri workspaceVersionSetsUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
workspaceVersionSetsUri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var uri = new WorkspaceVersionSetUri { Parent = workspaceVersionSetsUri, Name = name };
var dto = await uri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceVersionSetDto> GetDto(this WorkspaceVersionSetUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceVersionSetDto>();
}
public static async ValueTask Delete(this WorkspaceVersionSetUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceVersionSetUri uri, WorkspaceVersionSetDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
public static async ValueTask WriteDto(this WorkspaceVersionSetInformationFile file, WorkspaceVersionSetDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask<WorkspaceVersionSetDto> ReadDto(this WorkspaceVersionSetInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceVersionSetDto>();
}
}

Просмотреть файл

@ -10,27 +10,29 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.40.0" />
<PackageReference Include="Azure.Core" Version="1.41.0" />
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.ResourceManager" Version="1.12.0" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.2.0" />
<PackageReference Include="Azure.ResourceManager" Version="1.12.0" />
<PackageReference Include="Flurl" Version="4.0.0" />
<PackageReference Include="LanguageExt.Core" Version="4.4.8" />
<PackageReference Include="LanguageExt.Core" Version="5.0.0-beta-01" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.7.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.15" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<PackageReference Include="Nito.Comparers" Version="6.2.2" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
<PackageReference Include="Polly" Version="8.4.0" />
<PackageReference Include="Polly" Version="8.4.1" />
<PackageReference Include="System.Interactive.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="Yaml2JsonNode" Version="2.1.0" />
<PackageReference Include="YamlDotNet.System.Text.Json" Version="1.4.0" />
<PackageReference Include="Yaml2JsonNode" Version="2.1.1" />
<PackageReference Include="YamlDotNet.System.Text.Json" Version="1.4.2" />
</ItemGroup>
</Project>

Просмотреть файл

@ -4,45 +4,41 @@ using LanguageExt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractApis(CancellationToken cancellationToken);
public delegate ValueTask ExtractApis(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiName Name, ApiDto Dto, Option<(ApiSpecification Specification, BinaryData Contents)> SpecificationOption)> ListApis(CancellationToken cancellationToken);
public delegate bool ShouldExtractApiName(ApiName name);
public delegate bool ShouldExtractApiDto(ApiDto dto);
public delegate ValueTask WriteApiArtifacts(ApiName name, ApiDto dto, Option<(ApiSpecification Specification, BinaryData Contents)> specificationOption, CancellationToken cancellationToken);
public delegate ValueTask WriteApiInformationFile(ApiName name, ApiDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteApiSpecificationFile(ApiName name, ApiSpecification specification, BinaryData contents, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(ApiName Name, ApiDto Dto, Option<(ApiSpecification Specification, BinaryData Contents)> SpecificationOption)> ListApis(CancellationToken cancellationToken);
internal delegate bool ShouldExtractApiName(ApiName name);
internal delegate bool ShouldExtractApiDto(ApiDto dto);
internal delegate ValueTask WriteApiArtifacts(ApiName name, ApiDto dto, Option<(ApiSpecification Specification, BinaryData Contents)> specificationOption, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiInformationFile(ApiName name, ApiDto dto, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiSpecificationFile(ApiName name, ApiSpecification specification, BinaryData contents, CancellationToken cancellationToken);
internal static class ApiServices
internal static class ApiModule
{
public static void ConfigureExtractApis(IServiceCollection services)
public static void ConfigureExtractApis(IHostApplicationBuilder builder)
{
ConfigureListApis(services);
ConfigureShouldExtractApiName(services);
ConfigureShouldExtractApiDto(services);
ConfigureWriteApiArtifacts(services);
ApiPolicyServices.ConfigureExtractApiPolicies(services);
ApiTagServices.ConfigureExtractApiTags(services);
ApiOperationServices.ConfigureExtractApiOperations(services);
ConfigureListApis(builder);
ConfigureShouldExtractApiName(builder);
ConfigureShouldExtractApiDto(builder);
ConfigureWriteApiArtifacts(builder);
ApiPolicyModule.ConfigureExtractApiPolicies(builder);
ApiTagModule.ConfigureExtractApiTags(builder);
ApiOperationModule.ConfigureExtractApiOperations(builder);
services.TryAddSingleton(ExtractApis);
builder.Services.TryAddSingleton(GetExtractApis);
}
private static ExtractApis ExtractApis(IServiceProvider provider)
private static ExtractApis GetExtractApis(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListApis>();
var shouldExtractName = provider.GetRequiredService<ShouldExtractApiName>();
@ -51,8 +47,15 @@ internal static class ApiServices
var extractApiPolicies = provider.GetRequiredService<ExtractApiPolicies>();
var extractApiTags = provider.GetRequiredService<ExtractApiTags>();
var extractApiOperations = provider.GetRequiredService<ExtractApiOperations>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractApis));
logger.LogInformation("Extracting APIs...");
await list(cancellationToken)
.Where(api => shouldExtractName(api.Name))
.Where(api => shouldExtractDto(api.Dto))
@ -62,6 +65,7 @@ internal static class ApiServices
.IterParallel(async group => await group.Iter(async api => await extractApi(api.Name, api.Dto, api.SpecificationOption, cancellationToken),
cancellationToken),
cancellationToken);
};
async ValueTask extractApi(ApiName name, ApiDto dto, Option<(ApiSpecification Specification, BinaryData Contents)> specificationOption, CancellationToken cancellationToken)
{
@ -72,21 +76,20 @@ internal static class ApiServices
}
}
private static void ConfigureListApis(IServiceCollection services)
private static void ConfigureListApis(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
ApiSpecificationModule.ConfigureDefaultApiSpecification(builder);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListApis);
builder.Services.TryAddSingleton(GetListApis);
}
private static ListApis ListApis(IServiceProvider provider)
private static ListApis GetListApis(IServiceProvider provider)
{
var defaultApiSpecification = provider.GetRequiredService<DefaultApiSpecification>();
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
var configuration = provider.GetRequiredService<IConfiguration>();
var defaultApiSpecification = getDefaultApiSpecification(configuration);
return cancellationToken =>
ApisUri.From(serviceUri)
@ -98,52 +101,6 @@ internal static class ApiServices
return (name, dto, specificationContentsOption);
});
static ApiSpecification getDefaultApiSpecification(IConfiguration configuration)
{
var formatOption = configuration.TryGetValue("API_SPECIFICATION_FORMAT")
| configuration.TryGetValue("apiSpecificationFormat");
return formatOption.Map(format => format switch
{
var value when "Wadl".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.Wadl() as ApiSpecification,
var value when "JSON".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
},
var value when "YAML".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
},
var value when "OpenApiV2Json".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V2()
},
var value when "OpenApiV2Yaml".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V2()
},
var value when "OpenApiV3Json".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
},
var value when "OpenApiV3Yaml".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
},
var value => throw new NotSupportedException($"API specification format '{value}' defined in configuration is not supported.")
}).IfNone(() => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
});
}
async ValueTask<Option<(ApiSpecification, BinaryData)>> tryGetSpecificationContents(ApiName name, ApiDto dto, CancellationToken cancellationToken)
{
var specificationOption = tryGetSpecification(dto);
@ -163,20 +120,20 @@ internal static class ApiServices
{
"graphql" => new ApiSpecification.GraphQl(),
"soap" => new ApiSpecification.Wsdl(),
"http" => defaultApiSpecification,
null => defaultApiSpecification,
"http" => defaultApiSpecification.Value,
null => defaultApiSpecification.Value,
_ => Option<ApiSpecification>.None
};
}
public static void ConfigureShouldExtractApiName(IServiceCollection services)
public static void ConfigureShouldExtractApiName(IHostApplicationBuilder builder)
{
CommonServices.ConfigureShouldExtractFactory(services);
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
services.TryAddSingleton(ShouldExtractApiName);
builder.Services.TryAddSingleton(GetShouldExtractApiName);
}
private static ShouldExtractApiName ShouldExtractApiName(IServiceProvider provider)
private static ShouldExtractApiName GetShouldExtractApiName(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
@ -185,31 +142,31 @@ internal static class ApiServices
return name => shouldExtract(name);
}
public static void ConfigureShouldExtractApiDto(IServiceCollection services)
public static void ConfigureShouldExtractApiDto(IHostApplicationBuilder builder)
{
services.TryAddSingleton(ShouldExtractApiDto);
builder.Services.TryAddSingleton(GetShouldExtractApiDto);
}
private static ShouldExtractApiDto ShouldExtractApiDto(IServiceProvider provider)
private static ShouldExtractApiDto GetShouldExtractApiDto(IServiceProvider provider)
{
var shouldExtractVersionSet = provider.GetRequiredService<ShouldExtractVersionSet>();
return dto =>
// Don't extract if its version set should not be extracted
ApiModule.TryGetVersionSetName(dto)
.Map(shouldExtractVersionSet.Invoke)
.IfNone(true);
common.ApiModule.TryGetVersionSetName(dto)
.Map(shouldExtractVersionSet.Invoke)
.IfNone(true);
}
private static void ConfigureWriteApiArtifacts(IServiceCollection services)
private static void ConfigureWriteApiArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteApiInformationFile(services);
ConfigureWriteApiSpecificationFile(services);
ConfigureWriteApiInformationFile(builder);
ConfigureWriteApiSpecificationFile(builder);
services.TryAddSingleton(WriteApiArtifacts);
builder.Services.TryAddSingleton(GetWriteApiArtifacts);
}
private static WriteApiArtifacts WriteApiArtifacts(IServiceProvider provider)
private static WriteApiArtifacts GetWriteApiArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteApiInformationFile>();
var writeSpecificationFile = provider.GetRequiredService<WriteApiSpecificationFile>();
@ -226,19 +183,17 @@ internal static class ApiServices
};
}
private static void ConfigureWriteApiInformationFile(IServiceCollection services)
private static void ConfigureWriteApiInformationFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteApiInformationFile);
builder.Services.TryAddSingleton(GetWriteApiInformationFile);
}
private static WriteApiInformationFile WriteApiInformationFile(IServiceProvider provider)
private static WriteApiInformationFile GetWriteApiInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
@ -249,19 +204,17 @@ internal static class ApiServices
};
}
private static void ConfigureWriteApiSpecificationFile(IServiceCollection services)
private static void ConfigureWriteApiSpecificationFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteApiSpecificationFile);
builder.Services.TryAddSingleton(GetWriteApiSpecificationFile);
}
private static WriteApiSpecificationFile WriteApiSpecificationFile(IServiceProvider provider)
private static WriteApiSpecificationFile GetWriteApiSpecificationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, specification, contents, cancellationToken) =>
{
@ -271,10 +224,4 @@ internal static class ApiServices
await specificationFile.WriteSpecification(contents, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ApiExtractor");
}

Просмотреть файл

@ -2,51 +2,57 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractApiOperations(ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask ExtractApiOperations(ApiName apiName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<ApiOperationName> ListApiOperations(ApiName apiName, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<ApiOperationName> ListApiOperations(ApiName apiName, CancellationToken cancellationToken);
internal static class ApiOperationServices
internal static class ApiOperationModule
{
public static void ConfigureExtractApiOperations(IServiceCollection services)
public static void ConfigureExtractApiOperations(IHostApplicationBuilder builder)
{
ConfigureListApiOperations(services);
ApiOperationPolicyServices.ConfigureExtractApiOperationPolicies(services);
ConfigureListApiOperations(builder);
ApiOperationPolicyModule.ConfigureExtractApiOperationPolicies(builder);
services.TryAddSingleton(ExtractApiOperations);
builder.Services.TryAddSingleton(GetExtractApiOperations);
}
private static ExtractApiOperations ExtractApiOperations(IServiceProvider provider)
private static ExtractApiOperations GetExtractApiOperations(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListApiOperations>();
var extractPolicies = provider.GetRequiredService<ExtractApiOperationPolicies>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (apiName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractApiOperations));
logger.LogInformation("Extracting API operations for {ApiName}...", apiName);
await list(apiName, cancellationToken)
.IterParallel(async name => await extractPolicies(name, apiName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListApiOperations(IServiceCollection services)
private static void ConfigureListApiOperations(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListApiOperations);
builder.Services.TryAddSingleton(GetListApiOperations);
}
private static ListApiOperations ListApiOperations(IServiceProvider provider)
private static ListApiOperations GetListApiOperations(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
@ -55,10 +61,4 @@ internal static class ApiOperationServices
ApiOperationsUri.From(apiName, serviceUri)
.ListNames(pipeline, cancellationToken);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ApiOperationExtractor");
}

Просмотреть файл

@ -2,52 +2,59 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractApiOperationPolicies(ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask ExtractApiOperationPolicies(ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiOperationPolicyName Name, ApiOperationPolicyDto Dto)> ListApiOperationPolicies(ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiOperationPolicyArtifacts(ApiOperationPolicyName name, ApiOperationPolicyDto dto, ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiOperationPolicyFile(ApiOperationPolicyName name, ApiOperationPolicyDto dto, ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(ApiOperationPolicyName Name, ApiOperationPolicyDto Dto)> ListApiOperationPolicies(ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiOperationPolicyArtifacts(ApiOperationPolicyName name, ApiOperationPolicyDto dto, ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiOperationPolicyFile(ApiOperationPolicyName name, ApiOperationPolicyDto dto, ApiOperationName apiOperationName, ApiName apiName, CancellationToken cancellationToken);
internal static class ApiOperationPolicyServices
internal static class ApiOperationPolicyModule
{
public static void ConfigureExtractApiOperationPolicies(IServiceCollection services)
public static void ConfigureExtractApiOperationPolicies(IHostApplicationBuilder builder)
{
ConfigureListApiOperationPolicies(services);
ConfigureWriteApiOperationPolicyArtifacts(services);
ConfigureListApiOperationPolicies(builder);
ConfigureWriteApiOperationPolicyArtifacts(builder);
services.TryAddSingleton(ExtractApiOperationPolicies);
builder.Services.TryAddSingleton(GetExtractApiOperationPolicies);
}
private static ExtractApiOperationPolicies ExtractApiOperationPolicies(IServiceProvider provider)
private static ExtractApiOperationPolicies GetExtractApiOperationPolicies(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListApiOperationPolicies>();
var writeArtifacts = provider.GetRequiredService<WriteApiOperationPolicyArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (operationName, apiName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractApiOperationPolicies));
logger.LogInformation("Extracting policies for operation {ApiOperationName} in API {ApiName}...", operationName, apiName);
await list(operationName, apiName, cancellationToken)
.IterParallel(async policy => await writeArtifacts(policy.Name, policy.Dto, operationName, apiName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListApiOperationPolicies(IServiceCollection services)
private static void ConfigureListApiOperationPolicies(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListApiOperationPolicies);
builder.Services.TryAddSingleton(GetListApiOperationPolicies);
}
private static ListApiOperationPolicies ListApiOperationPolicies(IServiceProvider provider)
private static ListApiOperationPolicies GetListApiOperationPolicies(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
@ -57,14 +64,14 @@ internal static class ApiOperationPolicyServices
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteApiOperationPolicyArtifacts(IServiceCollection services)
private static void ConfigureWriteApiOperationPolicyArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteApiOperationPolicyFile(services);
ConfigureWriteApiOperationPolicyFile(builder);
services.TryAddSingleton(WriteApiOperationPolicyArtifacts);
builder.Services.TryAddSingleton(GetWriteApiOperationPolicyArtifacts);
}
private static WriteApiOperationPolicyArtifacts WriteApiOperationPolicyArtifacts(IServiceProvider provider)
private static WriteApiOperationPolicyArtifacts GetWriteApiOperationPolicyArtifacts(IServiceProvider provider)
{
var writePolicyFile = provider.GetRequiredService<WriteApiOperationPolicyFile>();
@ -72,19 +79,17 @@ internal static class ApiOperationPolicyServices
await writePolicyFile(name, dto, operationName, apiName, cancellationToken);
}
private static void ConfigureWriteApiOperationPolicyFile(IServiceCollection services)
private static void ConfigureWriteApiOperationPolicyFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteApiOperationPolicyFile);
builder.Services.TryAddSingleton(GetWriteApiOperationPolicyFile);
}
private static WriteApiOperationPolicyFile WriteApiOperationPolicyFile(IServiceProvider provider)
private static WriteApiOperationPolicyFile GetWriteApiOperationPolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, operationName, apiName, cancellationToken) =>
{
@ -95,10 +100,4 @@ internal static class ApiOperationPolicyServices
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ApiOperationPolicyExtractor");
}

Просмотреть файл

@ -2,52 +2,59 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractApiPolicies(ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask ExtractApiPolicies(ApiName apiName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiPolicyName Name, ApiPolicyDto Dto)> ListApiPolicies(ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiPolicyArtifacts(ApiPolicyName name, ApiPolicyDto dto, ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiPolicyFile(ApiPolicyName name, ApiPolicyDto dto, ApiName apiName, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(ApiPolicyName Name, ApiPolicyDto Dto)> ListApiPolicies(ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiPolicyArtifacts(ApiPolicyName name, ApiPolicyDto dto, ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiPolicyFile(ApiPolicyName name, ApiPolicyDto dto, ApiName apiName, CancellationToken cancellationToken);
internal static class ApiPolicyServices
internal static class ApiPolicyModule
{
public static void ConfigureExtractApiPolicies(IServiceCollection services)
public static void ConfigureExtractApiPolicies(IHostApplicationBuilder builder)
{
ConfigureListApiPolicies(services);
ConfigureWriteApiPolicyArtifacts(services);
ConfigureListApiPolicies(builder);
ConfigureWriteApiPolicyArtifacts(builder);
services.TryAddSingleton(ExtractApiPolicies);
builder.Services.TryAddSingleton(GetExtractApiPolicies);
}
private static ExtractApiPolicies ExtractApiPolicies(IServiceProvider provider)
private static ExtractApiPolicies GetExtractApiPolicies(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListApiPolicies>();
var writeArtifacts = provider.GetRequiredService<WriteApiPolicyArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (apiName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractApiPolicies));
logger.LogInformation("Extracting policies for API {ApiName}...", apiName);
await list(apiName, cancellationToken)
.IterParallel(async policy => await writeArtifacts(policy.Name, policy.Dto, apiName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListApiPolicies(IServiceCollection services)
private static void ConfigureListApiPolicies(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListApiPolicies);
builder.Services.TryAddSingleton(GetListApiPolicies);
}
private static ListApiPolicies ListApiPolicies(IServiceProvider provider)
private static ListApiPolicies GetListApiPolicies(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
@ -57,14 +64,14 @@ internal static class ApiPolicyServices
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteApiPolicyArtifacts(IServiceCollection services)
private static void ConfigureWriteApiPolicyArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteApiPolicyFile(services);
ConfigureWriteApiPolicyFile(builder);
services.TryAddSingleton(WriteApiPolicyArtifacts);
builder.Services.TryAddSingleton(GetWriteApiPolicyArtifacts);
}
private static WriteApiPolicyArtifacts WriteApiPolicyArtifacts(IServiceProvider provider)
private static WriteApiPolicyArtifacts GetWriteApiPolicyArtifacts(IServiceProvider provider)
{
var writePolicyFile = provider.GetRequiredService<WriteApiPolicyFile>();
@ -72,19 +79,17 @@ internal static class ApiPolicyServices
await writePolicyFile(name, dto, apiName, cancellationToken);
}
private static void ConfigureWriteApiPolicyFile(IServiceCollection services)
private static void ConfigureWriteApiPolicyFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteApiPolicyFile);
builder.Services.TryAddSingleton(GetWriteApiPolicyFile);
}
private static WriteApiPolicyFile WriteApiPolicyFile(IServiceProvider provider)
private static WriteApiPolicyFile GetWriteApiPolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, apiName, cancellationToken) =>
{
@ -95,10 +100,4 @@ internal static class ApiPolicyServices
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ApiPolicyExtractor");
}

Просмотреть файл

@ -0,0 +1,69 @@
using common;
using LanguageExt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using System;
namespace extractor;
internal sealed record DefaultApiSpecification(ApiSpecification Value);
internal static class ApiSpecificationModule
{
public static void ConfigureDefaultApiSpecification(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetDefaultApiSpecification);
}
private static DefaultApiSpecification GetDefaultApiSpecification(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var formatOption = configuration.TryGetValue("API_SPECIFICATION_FORMAT")
| configuration.TryGetValue("apiSpecificationFormat");
var specification = formatOption.Map(format => format switch
{
var value when "Wadl".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.Wadl() as ApiSpecification,
var value when "JSON".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
},
var value when "YAML".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
},
var value when "OpenApiV2Json".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V2()
},
var value when "OpenApiV2Yaml".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V2()
},
var value when "OpenApiV3Json".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
},
var value when "OpenApiV3Yaml".Equals(value, StringComparison.OrdinalIgnoreCase) => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
},
var value => throw new NotSupportedException($"API specification format '{value}' defined in configuration is not supported.")
}).IfNone(() => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
});
return new DefaultApiSpecification(specification);
}
}

Просмотреть файл

@ -2,56 +2,63 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractApiTags(ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask ExtractApiTags(ApiName apiName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(TagName Name, ApiTagDto Dto)> ListApiTags(ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiTagArtifacts(TagName name, ApiTagDto dto, ApiName apiName, CancellationToken cancellationToken);
public delegate ValueTask WriteApiTagInformationFile(TagName name, ApiTagDto dto, ApiName apiName, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(TagName Name, ApiTagDto Dto)> ListApiTags(ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiTagArtifacts(TagName name, ApiTagDto dto, ApiName apiName, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiTagInformationFile(TagName name, ApiTagDto dto, ApiName apiName, CancellationToken cancellationToken);
internal static class ApiTagServices
internal static class ApiTagModule
{
public static void ConfigureExtractApiTags(IServiceCollection services)
public static void ConfigureExtractApiTags(IHostApplicationBuilder builder)
{
ConfigureListApiTags(services);
TagServices.ConfigureShouldExtractTag(services);
ConfigureWriteApiTagArtifacts(services);
ConfigureListApiTags(builder);
TagModule.ConfigureShouldExtractTag(builder);
ConfigureWriteApiTagArtifacts(builder);
services.TryAddSingleton(ExtractApiTags);
builder.Services.TryAddSingleton(GetExtractApiTags);
}
private static ExtractApiTags ExtractApiTags(IServiceProvider provider)
private static ExtractApiTags GetExtractApiTags(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListApiTags>();
var shouldExtractTag = provider.GetRequiredService<ShouldExtractTag>();
var writeArtifacts = provider.GetRequiredService<WriteApiTagArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (apiName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractApiTags));
logger.LogInformation("Extracting tags for API {ApiName}...", apiName);
await list(apiName, cancellationToken)
.Where(tag => shouldExtractTag(tag.Name))
.IterParallel(async tag => await writeArtifacts(tag.Name, tag.Dto, apiName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListApiTags(IServiceCollection services)
private static void ConfigureListApiTags(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListApiTags);
builder.Services.TryAddSingleton(GetListApiTags);
}
private static ListApiTags ListApiTags(IServiceProvider provider)
private static ListApiTags GetListApiTags(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
@ -61,14 +68,14 @@ internal static class ApiTagServices
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteApiTagArtifacts(IServiceCollection services)
private static void ConfigureWriteApiTagArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteApiTagInformationFile(services);
ConfigureWriteApiTagInformationFile(builder);
services.TryAddSingleton(WriteApiTagArtifacts);
builder.Services.TryAddSingleton(GetWriteApiTagArtifacts);
}
private static WriteApiTagArtifacts WriteApiTagArtifacts(IServiceProvider provider)
private static WriteApiTagArtifacts GetWriteApiTagArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteApiTagInformationFile>();
@ -76,19 +83,17 @@ internal static class ApiTagServices
await writeInformationFile(name, dto, apiName, cancellationToken);
}
private static void ConfigureWriteApiTagInformationFile(IServiceCollection services)
private static void ConfigureWriteApiTagInformationFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteApiTagInformationFile);
builder.Services.TryAddSingleton(GetWriteApiTagInformationFile);
}
private static WriteApiTagInformationFile WriteApiTagInformationFile(IServiceProvider provider)
private static WriteApiTagInformationFile GetWriteApiTagInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, apiName, cancellationToken) =>
{
@ -98,10 +103,4 @@ internal static class ApiTagServices
await informationFile.WriteDto(dto, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ApiTagExtractor");
}

Просмотреть файл

@ -1,36 +1,38 @@
using Microsoft.Extensions.DependencyInjection;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.FeatureManagement;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace extractor;
internal delegate ValueTask RunExtractor(CancellationToken cancellationToken);
internal static class AppServices
internal static class AppModule
{
public static void ConfigureRunExtractor(IServiceCollection services)
public static void ConfigureRunApplication(IHostApplicationBuilder builder)
{
NamedValueServices.ConfigureExtractNamedValues(services);
TagServices.ConfigureExtractTags(services);
GatewayServices.ConfigureExtractGateways(services);
VersionSetServices.ConfigureExtractVersionSets(services);
BackendServices.ConfigureExtractBackends(services);
LoggerServices.ConfigureExtractLoggers(services);
DiagnosticServices.ConfigureExtractDiagnostics(services);
PolicyFragmentServices.ConfigureExtractPolicyFragments(services);
ServicePolicyServices.ConfigureExtractServicePolicies(services);
ProductServices.ConfigureExtractProducts(services);
GroupServices.ConfigureExtractGroups(services);
SubscriptionServices.ConfigureExtractSubscriptions(services);
ApiServices.ConfigureExtractApis(services);
NamedValueModule.ConfigureExtractNamedValues(builder);
TagModule.ConfigureExtractTags(builder);
GatewayModule.ConfigureExtractGateways(builder);
VersionSetModule.ConfigureExtractVersionSets(builder);
BackendModule.ConfigureExtractBackends(builder);
LoggerModule.ConfigureExtractLoggers(builder);
DiagnosticModule.ConfigureExtractDiagnostics(builder);
PolicyFragmentModule.ConfigureExtractPolicyFragments(builder);
ServicePolicyModule.ConfigureExtractServicePolicies(builder);
ProductModule.ConfigureExtractProducts(builder);
GroupModule.ConfigureExtractGroups(builder);
SubscriptionModule.ConfigureExtractSubscriptions(builder);
ApiModule.ConfigureExtractApis(builder);
WorkspaceModule.ConfigureExtractWorkspaces(builder);
builder.Services.AddFeatureManagement();
services.TryAddSingleton(RunExtractor);
builder.Services.TryAddSingleton(GetRunApplication);
}
private static RunExtractor RunExtractor(IServiceProvider provider)
private static RunApplication GetRunApplication(IServiceProvider provider)
{
var extractNamedValues = provider.GetRequiredService<ExtractNamedValues>();
var extractTags = provider.GetRequiredService<ExtractTags>();
@ -45,12 +47,15 @@ internal static class AppServices
var extractGroups = provider.GetRequiredService<ExtractGroups>();
var extractSubscriptions = provider.GetRequiredService<ExtractSubscriptions>();
var extractApis = provider.GetRequiredService<ExtractApis>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Extractor");
var extractWorkspaces = provider.GetRequiredService<ExtractWorkspaces>();
var featureManager = provider.GetRequiredService<IFeatureManager>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var activity = activitySource.StartActivity(nameof(RunApplication));
logger.LogInformation("Running extractor...");
await extractNamedValues(cancellationToken);
@ -67,6 +72,11 @@ internal static class AppServices
await extractSubscriptions(cancellationToken);
await extractApis(cancellationToken);
if (await featureManager.IsEnabledAsync("Workspaces"))
{
await extractWorkspaces(cancellationToken);
}
logger.LogInformation("Extractor completed.");
};
}

Просмотреть файл

@ -1,77 +1,84 @@
using Azure.Core.Pipeline;
using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractBackends(CancellationToken cancellationToken);
public delegate ValueTask ExtractBackends(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(BackendName Name, BackendDto Dto)> ListBackends(CancellationToken cancellationToken);
public delegate bool ShouldExtractBackend(BackendName name);
public delegate ValueTask WriteBackendArtifacts(BackendName name, BackendDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteBackendInformationFile(BackendName name, BackendDto dto, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(BackendName Name, BackendDto Dto)> ListBackends(CancellationToken cancellationToken);
internal delegate bool ShouldExtractBackend(BackendName name);
internal delegate ValueTask WriteBackendArtifacts(BackendName name, BackendDto dto, CancellationToken cancellationToken);
internal delegate ValueTask WriteBackendInformationFile(BackendName name, BackendDto dto, CancellationToken cancellationToken);
internal static class BackendServices
internal static class BackendModule
{
public static void ConfigureExtractBackends(IServiceCollection services)
public static void ConfigureExtractBackends(IHostApplicationBuilder builder)
{
ConfigureListBackends(services);
ConfigureShouldExtractBackend(services);
ConfigureWriteBackendArtifacts(services);
ConfigureListBackends(builder);
ConfigureShouldExtractBackend(builder);
ConfigureWriteBackendArtifacts(builder);
services.TryAddSingleton(ExtractBackends);
builder.Services.TryAddSingleton(GetExtractBackends);
}
private static ExtractBackends ExtractBackends(IServiceProvider provider)
private static ExtractBackends GetExtractBackends(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListBackends>();
var shouldExtract = provider.GetRequiredService<ShouldExtractBackend>();
var writeArtifacts = provider.GetRequiredService<WriteBackendArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractBackends));
logger.LogInformation("Extracting backends...");
await list(cancellationToken)
.Where(backend => shouldExtract(backend.Name))
.IterParallel(async backend => await writeArtifacts(backend.Name, backend.Dto, cancellationToken),
.Where(resource => shouldExtract(resource.Name))
.IterParallel(async resource => await writeArtifacts(resource.Name, resource.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListBackends(IServiceCollection services)
private static void ConfigureListBackends(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListBackends);
builder.Services.TryAddSingleton(GetListBackends);
}
private static ListBackends ListBackends(IServiceProvider provider)
private static ListBackends GetListBackends(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
BackendsUri.From(serviceUri)
.List(pipeline, cancellationToken);
{
var backendsUri = BackendsUri.From(serviceUri);
return backendsUri.List(pipeline, cancellationToken);
};
}
private static void ConfigureShouldExtractBackend(IServiceCollection services)
private static void ConfigureShouldExtractBackend(IHostApplicationBuilder builder)
{
CommonServices.ConfigureShouldExtractFactory(services);
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
services.TryAddSingleton(ShouldExtractBackend);
builder.Services.TryAddSingleton(GetShouldExtractBackend);
}
private static ShouldExtractBackend ShouldExtractBackend(IServiceProvider provider)
private static ShouldExtractBackend GetShouldExtractBackend(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
@ -80,14 +87,14 @@ internal static class BackendServices
return name => shouldExtract(name);
}
private static void ConfigureWriteBackendArtifacts(IServiceCollection services)
private static void ConfigureWriteBackendArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteBackendInformationFile(services);
ConfigureWriteBackendInformationFile(builder);
services.TryAddSingleton(WriteBackendArtifacts);
builder.Services.TryAddSingleton(GetWriteBackendArtifacts);
}
private static WriteBackendArtifacts WriteBackendArtifacts(IServiceProvider provider)
private static WriteBackendArtifacts GetWriteBackendArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteBackendInformationFile>();
@ -95,19 +102,17 @@ internal static class BackendServices
await writeInformationFile(name, dto, cancellationToken);
}
private static void ConfigureWriteBackendInformationFile(IServiceCollection services)
private static void ConfigureWriteBackendInformationFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteBackendInformationFile);
builder.Services.TryAddSingleton(GetWriteBackendInformationFile);
}
private static WriteBackendInformationFile WriteBackendInformationFile(IServiceProvider provider)
private static WriteBackendInformationFile GetWriteBackendInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
@ -117,10 +122,4 @@ internal static class BackendServices
await informationFile.WriteDto(dto, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("BackendExtractor");
}

Просмотреть файл

@ -1,193 +0,0 @@
using Azure.Core;
using Azure.Core.Pipeline;
using Azure.Identity;
using Azure.ResourceManager;
using common;
using Flurl;
using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.JsonWebTokens;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace extractor;
internal static class CommonServices
{
public static void Configure(IServiceCollection services)
{
services.AddSingleton(GetActivitySource);
services.AddSingleton(GetAzureEnvironment);
services.AddSingleton(GetTokenCredential);
services.AddSingleton(GetHttpPipeline);
services.AddSingleton(GetManagementServiceName);
services.AddSingleton(GetManagementServiceUri);
services.AddSingleton(GetManagementServiceDirectory);
services.AddSingleton(GetConfigurationJson);
services.AddSingleton<ShouldExtractFactory>();
OpenTelemetryServices.Configure(services);
}
private static ActivitySource GetActivitySource(IServiceProvider provider) =>
new("ApiOps.Extractor");
private static AzureEnvironment GetAzureEnvironment(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
return configuration.TryGetValue("AZURE_CLOUD_ENVIRONMENT").ValueUnsafe() switch
{
null => AzureEnvironment.Public,
"AzureGlobalCloud" or nameof(ArmEnvironment.AzurePublicCloud) => AzureEnvironment.Public,
"AzureChinaCloud" or nameof(ArmEnvironment.AzureChina) => AzureEnvironment.China,
"AzureUSGovernment" or nameof(ArmEnvironment.AzureGovernment) => AzureEnvironment.USGovernment,
"AzureGermanCloud" or nameof(ArmEnvironment.AzureGermany) => AzureEnvironment.Germany,
_ => throw new InvalidOperationException($"AZURE_CLOUD_ENVIRONMENT is invalid. Valid values are {nameof(ArmEnvironment.AzurePublicCloud)}, {nameof(ArmEnvironment.AzureChina)}, {nameof(ArmEnvironment.AzureGovernment)}, {nameof(ArmEnvironment.AzureGermany)}")
};
}
private static TokenCredential GetTokenCredential(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var azureAuthorityHost = provider.GetRequiredService<AzureEnvironment>().AuthorityHost;
return configuration.TryGetValue("AZURE_BEARER_TOKEN")
.Map(GetCredentialFromToken)
.IfNone(() => GetDefaultAzureCredential(azureAuthorityHost));
}
private static TokenCredential GetCredentialFromToken(string token)
{
var jsonWebToken = new JsonWebToken(token);
var expirationDate = new DateTimeOffset(jsonWebToken.ValidTo);
var accessToken = new AccessToken(token, expirationDate);
return DelegatedTokenCredential.Create((context, cancellationToken) => accessToken);
}
private static DefaultAzureCredential GetDefaultAzureCredential(Uri azureAuthorityHost) =>
new(new DefaultAzureCredentialOptions
{
AuthorityHost = azureAuthorityHost
});
public static void ConfigureHttpPipeline(IServiceCollection services)
{
services.TryAddSingleton(GetHttpPipeline);
}
private static HttpPipeline GetHttpPipeline(IServiceProvider provider)
{
var clientOptions = ClientOptions.Default;
clientOptions.RetryPolicy = new CommonRetryPolicy();
var tokenCredential = provider.GetRequiredService<TokenCredential>();
var azureEnvironment = provider.GetRequiredService<AzureEnvironment>();
var bearerAuthenticationPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, azureEnvironment.DefaultScope);
var logger = provider.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(HttpPipeline));
var loggingPolicy = new ILoggerHttpPipelinePolicy(logger);
var version = Assembly.GetExecutingAssembly()?.GetName().Version ?? new Version("-1");
var telemetryPolicy = new TelemetryPolicy(version);
return HttpPipelineBuilder.Build(clientOptions, bearerAuthenticationPolicy, loggingPolicy, telemetryPolicy);
}
private static ManagementServiceName GetManagementServiceName(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var name = configuration.TryGetValue("API_MANAGEMENT_SERVICE_NAME")
.IfNone(() => configuration.GetValue("apimServiceName"));
return ManagementServiceName.From(name);
}
public static void ConfigureManagementServiceUri(IServiceCollection services)
{
services.TryAddSingleton(GetManagementServiceUri);
}
private static ManagementServiceUri GetManagementServiceUri(IServiceProvider provider)
{
var azureEnvironment = provider.GetRequiredService<AzureEnvironment>();
var serviceName = provider.GetRequiredService<ManagementServiceName>();
var configuration = provider.GetRequiredService<IConfiguration>();
var apiVersion = configuration.TryGetValue("ARM_API_VERSION")
.IfNone(() => "2022-08-01");
var uri = azureEnvironment.ManagementEndpoint
.AppendPathSegment("subscriptions")
.AppendPathSegment(configuration.GetValue("AZURE_SUBSCRIPTION_ID"))
.AppendPathSegment("resourceGroups")
.AppendPathSegment(configuration.GetValue("AZURE_RESOURCE_GROUP_NAME"))
.AppendPathSegment("providers/Microsoft.ApiManagement/service")
.AppendPathSegment(serviceName.ToString())
.SetQueryParam("api-version", apiVersion)
.ToUri();
return ManagementServiceUri.From(uri);
}
public static void ConfigureManagementServiceDirectory(IServiceCollection services)
{
services.TryAddSingleton(GetManagementServiceDirectory);
}
private static ManagementServiceDirectory GetManagementServiceDirectory(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var directoryPath = configuration.GetValue("API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH");
var directory = new DirectoryInfo(directoryPath);
return ManagementServiceDirectory.From(directory);
}
public static void ConfigureShouldExtractFactory(IServiceCollection services)
{
ConfigureConfigurationJson(services);
services.TryAddSingleton(GetShouldExtractFactory);
}
private static ShouldExtractFactory GetShouldExtractFactory(IServiceProvider provider)
{
var configurationJson = provider.GetRequiredService<ConfigurationJson>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
return new ShouldExtractFactory(configurationJson, loggerFactory);
}
private static void ConfigureConfigurationJson(IServiceCollection services)
{
services.TryAddSingleton(GetConfigurationJson);
}
private static ConfigurationJson GetConfigurationJson(IServiceProvider provider)
{
var configuration = provider.GetRequiredService<IConfiguration>();
var configurationJson = ConfigurationJson.From(configuration);
return GetConfigurationJsonFromYaml(configuration)
.Map(configurationJson.MergeWith)
.IfNone(configurationJson);
}
private static Option<ConfigurationJson> GetConfigurationJsonFromYaml(IConfiguration configuration) =>
configuration.TryGetValue("CONFIGURATION_YAML_PATH")
.Map(path => new FileInfo(path))
.Where(file => file.Exists)
.Map(file =>
{
using var reader = File.OpenText(file.FullName);
return ConfigurationJson.FromYaml(reader);
});
}

Просмотреть файл

@ -3,58 +3,64 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractDiagnostics(CancellationToken cancellationToken);
public delegate ValueTask ExtractDiagnostics(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(DiagnosticName Name, DiagnosticDto Dto)> ListDiagnostics(CancellationToken cancellationToken);
public delegate bool ShouldExtractDiagnostic(DiagnosticName name, DiagnosticDto dto);
public delegate ValueTask WriteDiagnosticArtifacts(DiagnosticName name, DiagnosticDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteDiagnosticInformationFile(DiagnosticName name, DiagnosticDto dto, CancellationToken cancellationToken);
internal delegate IAsyncEnumerable<(DiagnosticName Name, DiagnosticDto Dto)> ListDiagnostics(CancellationToken cancellationToken);
internal delegate bool ShouldExtractDiagnostic(DiagnosticName name, DiagnosticDto dto);
internal delegate ValueTask WriteDiagnosticArtifacts(DiagnosticName name, DiagnosticDto dto, CancellationToken cancellationToken);
internal delegate ValueTask WriteDiagnosticInformationFile(DiagnosticName name, DiagnosticDto dto, CancellationToken cancellationToken);
internal static class DiagnosticServices
internal static class DiagnosticModule
{
public static void ConfigureExtractDiagnostics(IServiceCollection services)
public static void ConfigureExtractDiagnostics(IHostApplicationBuilder builder)
{
ConfigureListDiagnostics(services);
ConfigureShouldExtractDiagnostic(services);
ConfigureWriteDiagnosticArtifacts(services);
ConfigureListDiagnostics(builder);
ConfigureShouldExtractDiagnostic(builder);
ConfigureWriteDiagnosticArtifacts(builder);
services.TryAddSingleton(ExtractDiagnostics);
builder.Services.TryAddSingleton(GetExtractDiagnostics);
}
private static ExtractDiagnostics ExtractDiagnostics(IServiceProvider provider)
private static ExtractDiagnostics GetExtractDiagnostics(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListDiagnostics>();
var shouldExtract = provider.GetRequiredService<ShouldExtractDiagnostic>();
var writeArtifacts = provider.GetRequiredService<WriteDiagnosticArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractDiagnostics));
logger.LogInformation("Extracting diagnostics...");
await list(cancellationToken)
.Where(diagnostic => shouldExtract(diagnostic.Name, diagnostic.Dto))
.IterParallel(async diagnostic => await writeArtifacts(diagnostic.Name, diagnostic.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListDiagnostics(IServiceCollection services)
private static void ConfigureListDiagnostics(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceUri(services);
CommonServices.ConfigureHttpPipeline(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton(ListDiagnostics);
builder.Services.TryAddSingleton(GetListDiagnostics);
}
private static ListDiagnostics ListDiagnostics(IServiceProvider provider)
private static ListDiagnostics GetListDiagnostics(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
@ -64,15 +70,15 @@ internal static class DiagnosticServices
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractDiagnostic(IServiceCollection services)
private static void ConfigureShouldExtractDiagnostic(IHostApplicationBuilder builder)
{
CommonServices.ConfigureShouldExtractFactory(services);
LoggerServices.ConfigureShouldExtractLogger(services);
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
LoggerModule.ConfigureShouldExtractLogger(builder);
services.TryAddSingleton(ShouldExtractDiagnostic);
builder.Services.TryAddSingleton(GetShouldExtractDiagnostic);
}
private static ShouldExtractDiagnostic ShouldExtractDiagnostic(IServiceProvider provider)
private static ShouldExtractDiagnostic GetShouldExtractDiagnostic(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtractLogger = provider.GetRequiredService<ShouldExtractLogger>();
@ -81,19 +87,19 @@ internal static class DiagnosticServices
return (name, dto) =>
shouldExtractDiagnosticName(name)
&& DiagnosticModule.TryGetLoggerName(dto)
.Map(shouldExtractLogger.Invoke)
.IfNone(true);
&& common.DiagnosticModule.TryGetLoggerName(dto)
.Map(shouldExtractLogger.Invoke)
.IfNone(true);
}
private static void ConfigureWriteDiagnosticArtifacts(IServiceCollection services)
private static void ConfigureWriteDiagnosticArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteDiagnosticInformationFile(services);
ConfigureWriteDiagnosticInformationFile(builder);
services.TryAddSingleton(WriteDiagnosticArtifacts);
builder.Services.TryAddSingleton(GetWriteDiagnosticArtifacts);
}
private static WriteDiagnosticArtifacts WriteDiagnosticArtifacts(IServiceProvider provider)
private static WriteDiagnosticArtifacts GetWriteDiagnosticArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteDiagnosticInformationFile>();
@ -101,19 +107,17 @@ internal static class DiagnosticServices
await writeInformationFile(name, dto, cancellationToken);
}
private static void ConfigureWriteDiagnosticInformationFile(IServiceCollection services)
private static void ConfigureWriteDiagnosticInformationFile(IHostApplicationBuilder builder)
{
CommonServices.ConfigureManagementServiceDirectory(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton(WriteDiagnosticInformationFile);
builder.Services.TryAddSingleton(GetWriteDiagnosticInformationFile);
}
private static WriteDiagnosticInformationFile WriteDiagnosticInformationFile(IServiceProvider provider)
private static WriteDiagnosticInformationFile GetWriteDiagnosticInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
var logger = Common.GetLogger(loggerFactory);
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
@ -123,10 +127,4 @@ internal static class DiagnosticServices
await informationFile.WriteDto(dto, cancellationToken);
};
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("DiagnosticExtractor");
}

Просмотреть файл

@ -3,120 +3,122 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractGateways(CancellationToken cancellationToken);
public delegate ValueTask ExtractGateways(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(GatewayName Name, GatewayDto Dto)> ListGateways(CancellationToken cancellationToken);
public delegate bool ShouldExtractGateway(GatewayName name);
public delegate ValueTask WriteGatewayArtifacts(GatewayName name, GatewayDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteGatewayInformationFile(GatewayName name, GatewayDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(GatewayName Name, GatewayDto Dto)> ListGateways(CancellationToken cancellationToken);
file delegate bool ShouldExtractGateway(GatewayName name);
file delegate ValueTask WriteGatewayArtifacts(GatewayName name, GatewayDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteGatewayInformationFile(GatewayName name, GatewayDto dto, CancellationToken cancellationToken);
file sealed class ExtractGatewaysHandler(ListGateways list,
ShouldExtractGateway shouldExtract,
WriteGatewayArtifacts writeArtifacts,
ExtractGatewayApis extractGatewayApis)
internal static class GatewayModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(gateway => shouldExtract(gateway.Name))
.IterParallel(async gateway => await ExtractGateway(gateway.Name, gateway.Dto, cancellationToken),
cancellationToken);
private async ValueTask ExtractGateway(GatewayName name, GatewayDto dto, CancellationToken cancellationToken)
public static void ConfigureExtractGateways(IHostApplicationBuilder builder)
{
await writeArtifacts(name, dto, cancellationToken);
await extractGatewayApis(name, cancellationToken);
ConfigureListGateways(builder);
ConfigureShouldExtractGateway(builder);
ConfigureWriteGatewayArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractGateways);
}
}
file sealed class ListGatewaysHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(GatewayName, GatewayDto)> Handle(CancellationToken cancellationToken) =>
GatewaysUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractGatewayHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(GatewayName name)
private static ExtractGateways GetExtractGateways(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListGateways>();
var shouldExtract = provider.GetRequiredService<ShouldExtractGateway>();
var writeArtifacts = provider.GetRequiredService<WriteGatewayArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractGateways));
logger.LogInformation("Extracting gateways...");
await list(cancellationToken)
.Where(gateway => shouldExtract(gateway.Name))
.IterParallel(async gateway => await writeArtifacts(gateway.Name, gateway.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListGateways(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListGateways);
}
private static ListGateways GetListGateways(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
GatewaysUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractGateway(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractGateway);
}
private static ShouldExtractGateway GetShouldExtractGateway(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<GatewayName>();
return shouldExtract(name);
}
}
file sealed class WriteGatewayArtifactsHandler(WriteGatewayInformationFile writeInformationFile)
{
public async ValueTask Handle(GatewayName name, GatewayDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteGatewayArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteGatewayInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteGatewayArtifacts);
}
}
file sealed class WriteGatewayInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(GatewayName name, GatewayDto dto, CancellationToken cancellationToken)
private static WriteGatewayArtifacts GetWriteGatewayArtifacts(IServiceProvider provider)
{
var informationFile = GatewayInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteGatewayInformationFile>();
logger.LogInformation("Writing gateway information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class GatewayServices
{
public static void ConfigureExtractGateways(IServiceCollection services)
private static void ConfigureWriteGatewayInformationFile(IHostApplicationBuilder builder)
{
ConfigureListGateways(services);
ConfigureShouldExtractGateway(services);
ConfigureWriteGatewayArtifacts(services);
GatewayApiServices.ConfigureExtractGatewayApis(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractGatewaysHandler>();
services.TryAddSingleton<ExtractGateways>(provider => provider.GetRequiredService<ExtractGatewaysHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteGatewayInformationFile);
}
private static void ConfigureListGateways(IServiceCollection services)
private static WriteGatewayInformationFile GetWriteGatewayInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListGatewaysHandler>();
services.TryAddSingleton<ListGateways>(provider => provider.GetRequiredService<ListGatewaysHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = GatewayInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing gateway information file {GatewayInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
private static void ConfigureShouldExtractGateway(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractGatewayHandler>();
services.TryAddSingleton<ShouldExtractGateway>(provider => provider.GetRequiredService<ShouldExtractGatewayHandler>().Handle);
}
private static void ConfigureWriteGatewayArtifacts(IServiceCollection services)
{
ConfigureWriteGatewayInformationFile(services);
services.TryAddSingleton<WriteGatewayArtifactsHandler>();
services.TryAddSingleton<WriteGatewayArtifacts>(provider => provider.GetRequiredService<WriteGatewayArtifactsHandler>().Handle);
}
private static void ConfigureWriteGatewayInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteGatewayInformationFileHandler>();
services.TryAddSingleton<WriteGatewayInformationFile>(provider => provider.GetRequiredService<WriteGatewayInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("GatewayExtractor");
}

Просмотреть файл

@ -2,93 +2,105 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractGatewayApis(GatewayName gatewayName, CancellationToken cancellationToken);
public delegate ValueTask ExtractGatewayApis(GatewayName gatewayName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiName Name, GatewayApiDto Dto)> ListGatewayApis(GatewayName gatewayName, CancellationToken cancellationToken);
public delegate ValueTask WriteGatewayApiArtifacts(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken);
public delegate ValueTask WriteGatewayApiInformationFile(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(ApiName Name, GatewayApiDto Dto)> ListGatewayApis(GatewayName gatewayName, CancellationToken cancellationToken);
file delegate ValueTask WriteGatewayApiArtifacts(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken);
file delegate ValueTask WriteGatewayApiInformationFile(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken);
file sealed class ExtractGatewayApisHandler(ListGatewayApis list, ShouldExtractApiName shouldExtractApi, WriteGatewayApiArtifacts writeArtifacts)
internal static class GatewayApiModule
{
public async ValueTask Handle(GatewayName gatewayName, CancellationToken cancellationToken) =>
await list(gatewayName, cancellationToken)
.Where(api => shouldExtractApi(api.Name))
.IterParallel(async gatewayapi => await writeArtifacts(gatewayapi.Name, gatewayapi.Dto, gatewayName, cancellationToken),
cancellationToken);
}
file sealed class ListGatewayApisHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(ApiName, GatewayApiDto)> Handle(GatewayName gatewayName, CancellationToken cancellationToken) =>
GatewayApisUri.From(gatewayName, serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteGatewayApiArtifactsHandler(WriteGatewayApiInformationFile writeApiFile)
{
public async ValueTask Handle(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken)
public static void ConfigureExtractGatewayApis(IHostApplicationBuilder builder)
{
await writeApiFile(name, dto, gatewayName, cancellationToken);
}
}
ConfigureListGatewayApis(builder);
ApiModule.ConfigureShouldExtractApiName(builder);
ConfigureWriteGatewayApiArtifacts(builder);
file sealed class WriteGatewayApiInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(ApiName name, GatewayApiDto dto, GatewayName gatewayName, CancellationToken cancellationToken)
{
var informationFile = GatewayApiInformationFile.From(name, gatewayName, serviceDirectory);
logger.LogInformation("Writing gateway api information file {GatewayApiInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
}
}
internal static class GatewayApiServices
{
public static void ConfigureExtractGatewayApis(IServiceCollection services)
{
ConfigureListGatewayApis(services);
ApiServices.ConfigureShouldExtractApiName(services);
ConfigureWriteGatewayApiArtifacts(services);
services.TryAddSingleton<ExtractGatewayApisHandler>();
services.TryAddSingleton<ExtractGatewayApis>(provider => provider.GetRequiredService<ExtractGatewayApisHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractGatewayApis);
}
private static void ConfigureListGatewayApis(IServiceCollection services)
private static ExtractGatewayApis GetExtractGatewayApis(IServiceProvider provider)
{
services.TryAddSingleton<ListGatewayApisHandler>();
services.TryAddSingleton<ListGatewayApis>(provider => provider.GetRequiredService<ListGatewayApisHandler>().Handle);
var list = provider.GetRequiredService<ListGatewayApis>();
var shouldExtractApi = provider.GetRequiredService<ShouldExtractApiName>();
var writeArtifacts = provider.GetRequiredService<WriteGatewayApiArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (gatewayName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractGatewayApis));
logger.LogInformation("Extracting APIs for gateway {GatewayName}...", gatewayName);
await list(gatewayName, cancellationToken)
.Where(api => shouldExtractApi(api.Name))
.IterParallel(async gatewayapi => await writeArtifacts(gatewayapi.Name, gatewayapi.Dto, gatewayName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteGatewayApiArtifacts(IServiceCollection services)
private static void ConfigureListGatewayApis(IHostApplicationBuilder builder)
{
ConfigureWriteGatewayApiInformationFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteGatewayApiArtifactsHandler>();
services.TryAddSingleton<WriteGatewayApiArtifacts>(provider => provider.GetRequiredService<WriteGatewayApiArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListGatewayApis);
}
private static void ConfigureWriteGatewayApiInformationFile(IServiceCollection services)
private static ListGatewayApis GetListGatewayApis(IServiceProvider provider)
{
services.TryAddSingleton<WriteGatewayApiInformationFileHandler>();
services.TryAddSingleton<WriteGatewayApiInformationFile>(provider => provider.GetRequiredService<WriteGatewayApiInformationFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("GatewayApiExtractor");
return (gatewayName, cancellationToken) =>
GatewayApisUri.From(gatewayName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteGatewayApiArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteGatewayApiInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteGatewayApiArtifacts);
}
private static WriteGatewayApiArtifacts GetWriteGatewayApiArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteGatewayApiInformationFile>();
return async (name, dto, gatewayName, cancellationToken) =>
{
await writeInformationFile(name, dto, gatewayName, cancellationToken);
};
}
public static void ConfigureWriteGatewayApiInformationFile(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetWriteGatewayApiInformationFile);
}
private static WriteGatewayApiInformationFile GetWriteGatewayApiInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, gatewayName, cancellationToken) =>
{
var informationFile = GatewayApiInformationFile.From(name, gatewayName, serviceDirectory);
logger.LogInformation("Writing gateway API information file {GatewayApiInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -3,110 +3,124 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractGroups(CancellationToken cancellationToken);
public delegate ValueTask ExtractGroups(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(GroupName Name, GroupDto Dto)> ListGroups(CancellationToken cancellationToken);
public delegate bool ShouldExtractGroup(GroupName name);
public delegate ValueTask WriteGroupArtifacts(GroupName name, GroupDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteGroupInformationFile(GroupName name, GroupDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(GroupName Name, GroupDto Dto)> ListGroups(CancellationToken cancellationToken);
file delegate bool ShouldExtractGroup(GroupName name);
file delegate ValueTask WriteGroupArtifacts(GroupName name, GroupDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteGroupInformationFile(GroupName name, GroupDto dto, CancellationToken cancellationToken);
file sealed class ExtractGroupsHandler(ListGroups list, ShouldExtractGroup shouldExtract, WriteGroupArtifacts writeArtifacts)
internal static class GroupModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(group => shouldExtract(group.Name))
.IterParallel(async group => await writeArtifacts(group.Name, group.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListGroupsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(GroupName, GroupDto)> Handle(CancellationToken cancellationToken) =>
GroupsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractGroupHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(GroupName name)
public static void ConfigureExtractGroups(IHostApplicationBuilder builder)
{
ConfigureListGroups(builder);
ConfigureShouldExtractGroup(builder);
ConfigureWriteGroupArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractGroups);
}
private static ExtractGroups GetExtractGroups(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListGroups>();
var shouldExtract = provider.GetRequiredService<ShouldExtractGroup>();
var writeArtifacts = provider.GetRequiredService<WriteGroupArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractGroups));
logger.LogInformation("Extracting groups...");
await list(cancellationToken)
.Where(group => shouldExtract(group.Name))
.IterParallel(async group => await writeArtifacts(group.Name, group.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListGroups(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListGroups);
}
private static ListGroups GetListGroups(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
GroupsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
public static void ConfigureShouldExtractGroup(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractGroup);
}
private static ShouldExtractGroup GetShouldExtractGroup(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<GroupName>();
return shouldExtract(name);
}
}
file sealed class WriteGroupArtifactsHandler(WriteGroupInformationFile writeInformationFile)
{
public async ValueTask Handle(GroupName name, GroupDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteGroupArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteGroupInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteGroupArtifacts);
}
}
file sealed class WriteGroupInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(GroupName name, GroupDto dto, CancellationToken cancellationToken)
private static WriteGroupArtifacts GetWriteGroupArtifacts(IServiceProvider provider)
{
var informationFile = GroupInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteGroupInformationFile>();
logger.LogInformation("Writing group information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
{
await writeInformationFile(name, dto, cancellationToken);
};
}
}
internal static class GroupServices
{
public static void ConfigureExtractGroups(IServiceCollection services)
private static void ConfigureWriteGroupInformationFile(IHostApplicationBuilder builder)
{
ConfigureListGroups(services);
ConfigureShouldExtractGroup(services);
ConfigureWriteGroupArtifacts(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractGroupsHandler>();
services.TryAddSingleton<ExtractGroups>(provider => provider.GetRequiredService<ExtractGroupsHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteGroupInformationFile);
}
private static void ConfigureListGroups(IServiceCollection services)
private static WriteGroupInformationFile GetWriteGroupInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListGroupsHandler>();
services.TryAddSingleton<ListGroups>(provider => provider.GetRequiredService<ListGroupsHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = GroupInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing group information file {GroupInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
private static void ConfigureShouldExtractGroup(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractGroupHandler>();
services.TryAddSingleton<ShouldExtractGroup>(provider => provider.GetRequiredService<ShouldExtractGroupHandler>().Handle);
}
private static void ConfigureWriteGroupArtifacts(IServiceCollection services)
{
ConfigureWriteGroupInformationFile(services);
services.TryAddSingleton<WriteGroupArtifactsHandler>();
services.TryAddSingleton<WriteGroupArtifacts>(provider => provider.GetRequiredService<WriteGroupArtifactsHandler>().Handle);
}
private static void ConfigureWriteGroupInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteGroupInformationFileHandler>();
services.TryAddSingleton<WriteGroupInformationFile>(provider => provider.GetRequiredService<WriteGroupInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("GroupExtractor");
}

Просмотреть файл

@ -3,110 +3,122 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractLoggers(CancellationToken cancellationToken);
public delegate ValueTask ExtractLoggers(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(LoggerName Name, LoggerDto Dto)> ListLoggers(CancellationToken cancellationToken);
public delegate bool ShouldExtractLogger(LoggerName name);
public delegate ValueTask WriteLoggerArtifacts(LoggerName name, LoggerDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteLoggerInformationFile(LoggerName name, LoggerDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(LoggerName Name, LoggerDto Dto)> ListLoggers(CancellationToken cancellationToken);
internal delegate bool ShouldExtractLogger(LoggerName name);
file delegate ValueTask WriteLoggerArtifacts(LoggerName name, LoggerDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteLoggerInformationFile(LoggerName name, LoggerDto dto, CancellationToken cancellationToken);
file sealed class ExtractLoggersHandler(ListLoggers list, ShouldExtractLogger shouldExtract, WriteLoggerArtifacts writeArtifacts)
internal static class LoggerModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(logger => shouldExtract(logger.Name))
.IterParallel(async logger => await writeArtifacts(logger.Name, logger.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListLoggersHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(LoggerName, LoggerDto)> Handle(CancellationToken cancellationToken) =>
LoggersUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractLoggerHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(LoggerName name)
public static void ConfigureExtractLoggers(IHostApplicationBuilder builder)
{
ConfigureListLoggers(builder);
ConfigureShouldExtractLogger(builder);
ConfigureWriteLoggerArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractLoggers);
}
private static ExtractLoggers GetExtractLoggers(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListLoggers>();
var shouldExtract = provider.GetRequiredService<ShouldExtractLogger>();
var writeArtifacts = provider.GetRequiredService<WriteLoggerArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractLoggers));
logger.LogInformation("Extracting loggers...");
await list(cancellationToken)
.Where(logger => shouldExtract(logger.Name))
.IterParallel(async logger => await writeArtifacts(logger.Name, logger.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListLoggers(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListLoggers);
}
private static ListLoggers GetListLoggers(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
LoggersUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
public static void ConfigureShouldExtractLogger(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractLogger);
}
private static ShouldExtractLogger GetShouldExtractLogger(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<LoggerName>();
return shouldExtract(name);
}
}
file sealed class WriteLoggerArtifactsHandler(WriteLoggerInformationFile writeInformationFile)
{
public async ValueTask Handle(LoggerName name, LoggerDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteLoggerArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteLoggerInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteLoggerArtifacts);
}
}
file sealed class WriteLoggerInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(LoggerName name, LoggerDto dto, CancellationToken cancellationToken)
private static WriteLoggerArtifacts GetWriteLoggerArtifacts(IServiceProvider provider)
{
var informationFile = LoggerInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteLoggerInformationFile>();
logger.LogInformation("Writing logger information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class LoggerServices
{
public static void ConfigureExtractLoggers(IServiceCollection services)
private static void ConfigureWriteLoggerInformationFile(IHostApplicationBuilder builder)
{
ConfigureListLoggers(services);
ConfigureShouldExtractLogger(services);
ConfigureWriteLoggerArtifacts(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractLoggersHandler>();
services.TryAddSingleton<ExtractLoggers>(provider => provider.GetRequiredService<ExtractLoggersHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteLoggerInformationFile);
}
private static void ConfigureListLoggers(IServiceCollection services)
private static WriteLoggerInformationFile GetWriteLoggerInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListLoggersHandler>();
services.TryAddSingleton<ListLoggers>(provider => provider.GetRequiredService<ListLoggersHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = LoggerInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing logger information file {LoggerInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
public static void ConfigureShouldExtractLogger(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractLoggerHandler>();
services.TryAddSingleton<ShouldExtractLogger>(provider => provider.GetRequiredService<ShouldExtractLoggerHandler>().Handle);
}
private static void ConfigureWriteLoggerArtifacts(IServiceCollection services)
{
ConfigureWriteLoggerInformationFile(services);
services.TryAddSingleton<WriteLoggerArtifactsHandler>();
services.TryAddSingleton<WriteLoggerArtifacts>(provider => provider.GetRequiredService<WriteLoggerArtifactsHandler>().Handle);
}
private static void ConfigureWriteLoggerInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteLoggerInformationFileHandler>();
services.TryAddSingleton<WriteLoggerInformationFile>(provider => provider.GetRequiredService<WriteLoggerInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("LoggerExtractor");
}

Просмотреть файл

@ -3,110 +3,122 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractNamedValues(CancellationToken cancellationToken);
public delegate ValueTask ExtractNamedValues(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(NamedValueName Name, NamedValueDto Dto)> ListNamedValues(CancellationToken cancellationToken);
public delegate bool ShouldExtractNamedValue(NamedValueName name);
public delegate ValueTask WriteNamedValueArtifacts(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteNamedValueInformationFile(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(NamedValueName Name, NamedValueDto Dto)> ListNamedValues(CancellationToken cancellationToken);
file delegate bool ShouldExtractNamedValue(NamedValueName name);
file delegate ValueTask WriteNamedValueArtifacts(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteNamedValueInformationFile(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken);
file sealed class ExtractNamedValuesHandler(ListNamedValues list, ShouldExtractNamedValue shouldExtract, WriteNamedValueArtifacts writeArtifacts)
internal static class NamedValueModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(namedvalue => shouldExtract(namedvalue.Name))
.IterParallel(async namedvalue => await writeArtifacts(namedvalue.Name, namedvalue.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListNamedValuesHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(NamedValueName, NamedValueDto)> Handle(CancellationToken cancellationToken) =>
NamedValuesUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractNamedValueHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(NamedValueName name)
public static void ConfigureExtractNamedValues(IHostApplicationBuilder builder)
{
ConfigureListNamedValues(builder);
ConfigureShouldExtractNamedValue(builder);
ConfigureWriteNamedValueArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractNamedValues);
}
private static ExtractNamedValues GetExtractNamedValues(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListNamedValues>();
var shouldExtract = provider.GetRequiredService<ShouldExtractNamedValue>();
var writeArtifacts = provider.GetRequiredService<WriteNamedValueArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractNamedValues));
logger.LogInformation("Extracting named values...");
await list(cancellationToken)
.Where(namedvalue => shouldExtract(namedvalue.Name))
.IterParallel(async namedvalue => await writeArtifacts(namedvalue.Name, namedvalue.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListNamedValues(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListNamedValues);
}
private static ListNamedValues GetListNamedValues(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
NamedValuesUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractNamedValue(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractNamedValue);
}
private static ShouldExtractNamedValue GetShouldExtractNamedValue(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<NamedValueName>();
return shouldExtract(name);
}
}
file sealed class WriteNamedValueArtifactsHandler(WriteNamedValueInformationFile writeInformationFile)
{
public async ValueTask Handle(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteNamedValueArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteNamedValueInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteNamedValueArtifacts);
}
}
file sealed class WriteNamedValueInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(NamedValueName name, NamedValueDto dto, CancellationToken cancellationToken)
private static WriteNamedValueArtifacts GetWriteNamedValueArtifacts(IServiceProvider provider)
{
var informationFile = NamedValueInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteNamedValueInformationFile>();
logger.LogInformation("Writing named value information file {NamedValueInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class NamedValueServices
{
public static void ConfigureExtractNamedValues(IServiceCollection services)
private static void ConfigureWriteNamedValueInformationFile(IHostApplicationBuilder builder)
{
ConfigureListNamedValues(services);
ConfigureShouldExtractNamedValue(services);
ConfigureWriteNamedValueArtifacts(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractNamedValuesHandler>();
services.TryAddSingleton<ExtractNamedValues>(provider => provider.GetRequiredService<ExtractNamedValuesHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteNamedValueInformationFile);
}
private static void ConfigureListNamedValues(IServiceCollection services)
private static WriteNamedValueInformationFile GetWriteNamedValueInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListNamedValuesHandler>();
services.TryAddSingleton<ListNamedValues>(provider => provider.GetRequiredService<ListNamedValuesHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = NamedValueInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing named value information file {NamedValueInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
private static void ConfigureShouldExtractNamedValue(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractNamedValueHandler>();
services.TryAddSingleton<ShouldExtractNamedValue>(provider => provider.GetRequiredService<ShouldExtractNamedValueHandler>().Handle);
}
private static void ConfigureWriteNamedValueArtifacts(IServiceCollection services)
{
ConfigureWriteNamedValueInformationFile(services);
services.TryAddSingleton<WriteNamedValueArtifactsHandler>();
services.TryAddSingleton<WriteNamedValueArtifacts>(provider => provider.GetRequiredService<WriteNamedValueArtifactsHandler>().Handle);
}
private static void ConfigureWriteNamedValueInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteNamedValueInformationFileHandler>();
services.TryAddSingleton<WriteNamedValueInformationFile>(provider => provider.GetRequiredService<WriteNamedValueInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("NamedValueExtractor");
}

Просмотреть файл

@ -3,135 +3,153 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractPolicyFragments(CancellationToken cancellationToken);
public delegate ValueTask ExtractPolicyFragments(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(PolicyFragmentName Name, PolicyFragmentDto Dto)> ListPolicyFragments(CancellationToken cancellationToken);
public delegate bool ShouldExtractPolicyFragment(PolicyFragmentName name);
public delegate ValueTask WritePolicyFragmentArtifacts(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
public delegate ValueTask WritePolicyFragmentInformationFile(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
public delegate ValueTask WritePolicyFragmentPolicyFile(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(PolicyFragmentName Name, PolicyFragmentDto Dto)> ListPolicyFragments(CancellationToken cancellationToken);
file delegate bool ShouldExtractPolicyFragment(PolicyFragmentName name);
file delegate ValueTask WritePolicyFragmentArtifacts(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
file delegate ValueTask WritePolicyFragmentInformationFile(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
file delegate ValueTask WritePolicyFragmentPolicyFile(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken);
file sealed class ExtractPolicyFragmentsHandler(ListPolicyFragments list, ShouldExtractPolicyFragment shouldExtract, WritePolicyFragmentArtifacts writeArtifacts)
internal static class PolicyFragmentModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(policyfragment => shouldExtract(policyfragment.Name))
.IterParallel(async policyfragment => await writeArtifacts(policyfragment.Name, policyfragment.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListPolicyFragmentsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(PolicyFragmentName, PolicyFragmentDto)> Handle(CancellationToken cancellationToken) =>
PolicyFragmentsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractPolicyFragmentHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(PolicyFragmentName name)
public static void ConfigureExtractPolicyFragments(IHostApplicationBuilder builder)
{
ConfigureListPolicyFragments(builder);
ConfigureShouldExtractPolicyFragment(builder);
ConfigureWritePolicyFragmentArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractPolicyFragments);
}
private static ExtractPolicyFragments GetExtractPolicyFragments(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListPolicyFragments>();
var shouldExtract = provider.GetRequiredService<ShouldExtractPolicyFragment>();
var writeArtifacts = provider.GetRequiredService<WritePolicyFragmentArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractPolicyFragments));
logger.LogInformation("Extracting policy fragments...");
await list(cancellationToken)
.Where(policyfragment => shouldExtract(policyfragment.Name))
.IterParallel(async policyfragment => await writeArtifacts(policyfragment.Name, policyfragment.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListPolicyFragments(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListPolicyFragments);
}
private static ListPolicyFragments GetListPolicyFragments(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
PolicyFragmentsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractPolicyFragment(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractPolicyFragment);
}
private static ShouldExtractPolicyFragment GetShouldExtractPolicyFragment(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<PolicyFragmentName>();
return shouldExtract(name);
}
}
file sealed class WritePolicyFragmentArtifactsHandler(WritePolicyFragmentInformationFile writeInformationFile,
WritePolicyFragmentPolicyFile writePolicyFragmentPolicyFile)
{
public async ValueTask Handle(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWritePolicyFragmentArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
await writePolicyFragmentPolicyFile(name, dto, cancellationToken);
ConfigureWritePolicyFragmentInformationFile(builder);
ConfigureWritePolicyFragmentPolicyFile(builder);
builder.Services.TryAddSingleton(GetWritePolicyFragmentArtifacts);
}
}
file sealed class WritePolicyFragmentInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken)
private static WritePolicyFragmentArtifacts GetWritePolicyFragmentArtifacts(IServiceProvider provider)
{
var informationFile = PolicyFragmentInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WritePolicyFragmentInformationFile>();
var writePolicyFragmentPolicyFile = provider.GetRequiredService<WritePolicyFragmentPolicyFile>();
logger.LogInformation("Writing policy fragment information file {PolicyFragmentInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
{
await writeInformationFile(name, dto, cancellationToken);
await writePolicyFragmentPolicyFile(name, dto, cancellationToken);
};
}
}
file sealed class WritePolicyFragmentPolicyFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(PolicyFragmentName name, PolicyFragmentDto dto, CancellationToken cancellationToken)
private static void ConfigureWritePolicyFragmentInformationFile(IHostApplicationBuilder builder)
{
var policyFile = PolicyFragmentPolicyFile.From(name, serviceDirectory);
AzureModule.ConfigureManagementServiceDirectory(builder);
logger.LogInformation("Writing policy fragment policy file {PolicyFragmentPolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
builder.Services.TryAddSingleton(GetWritePolicyFragmentInformationFile);
}
}
internal static class PolicyFragmentServices
{
public static void ConfigureExtractPolicyFragments(IServiceCollection services)
private static WritePolicyFragmentInformationFile GetWritePolicyFragmentInformationFile(IServiceProvider provider)
{
ConfigureListPolicyFragments(services);
ConfigureShouldExtractPolicyFragment(services);
ConfigureWritePolicyFragmentArtifacts(services);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
services.TryAddSingleton<ExtractPolicyFragmentsHandler>();
services.TryAddSingleton<ExtractPolicyFragments>(provider => provider.GetRequiredService<ExtractPolicyFragmentsHandler>().Handle);
return async (name, dto, cancellationToken) =>
{
var informationFile = PolicyFragmentInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing policy fragment information file {PolicyFragmentInformationFile}...", informationFile);
// Remove policy contents from DTO, as these will be written to the policy file
var updatedDto = dto with { Properties = dto.Properties with { Format = null, Value = null } };
await informationFile.WriteDto(updatedDto, cancellationToken);
};
}
private static void ConfigureListPolicyFragments(IServiceCollection services)
private static void ConfigureWritePolicyFragmentPolicyFile(IHostApplicationBuilder builder)
{
services.TryAddSingleton<ListPolicyFragmentsHandler>();
services.TryAddSingleton<ListPolicyFragments>(provider => provider.GetRequiredService<ListPolicyFragmentsHandler>().Handle);
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWritePolicyFragmentPolicyFile);
}
private static void ConfigureShouldExtractPolicyFragment(IServiceCollection services)
private static WritePolicyFragmentPolicyFile GetWritePolicyFragmentPolicyFile(IServiceProvider provider)
{
services.TryAddSingleton<ShouldExtractPolicyFragmentHandler>();
services.TryAddSingleton<ShouldExtractPolicyFragment>(provider => provider.GetRequiredService<ShouldExtractPolicyFragmentHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var policyFile = PolicyFragmentPolicyFile.From(name, serviceDirectory);
logger.LogInformation("Writing policy fragment policy file {PolicyFragmentPolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
};
}
private static void ConfigureWritePolicyFragmentArtifacts(IServiceCollection services)
{
ConfigureWritePolicyFragmentInformationFile(services);
ConfigureWritePolicyFragmentPolicyFile(services);
services.TryAddSingleton<WritePolicyFragmentArtifactsHandler>();
services.TryAddSingleton<WritePolicyFragmentArtifacts>(provider => provider.GetRequiredService<WritePolicyFragmentArtifactsHandler>().Handle);
}
private static void ConfigureWritePolicyFragmentInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WritePolicyFragmentInformationFileHandler>();
services.TryAddSingleton<WritePolicyFragmentInformationFile>(provider => provider.GetRequiredService<WritePolicyFragmentInformationFileHandler>().Handle);
}
private static void ConfigureWritePolicyFragmentPolicyFile(IServiceCollection services)
{
services.TryAddSingleton<WritePolicyFragmentPolicyFileHandler>();
services.TryAddSingleton<WritePolicyFragmentPolicyFile>(provider => provider.GetRequiredService<WritePolicyFragmentPolicyFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("PolicyFragmentExtractor");
}

Просмотреть файл

@ -3,129 +3,139 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractProducts(CancellationToken cancellationToken);
public delegate ValueTask ExtractProducts(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ProductName Name, ProductDto Dto)> ListProducts(CancellationToken cancellationToken);
public delegate bool ShouldExtractProduct(ProductName name);
public delegate ValueTask WriteProductArtifacts(ProductName name, ProductDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteProductInformationFile(ProductName name, ProductDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(ProductName Name, ProductDto Dto)> ListProducts(CancellationToken cancellationToken);
internal delegate bool ShouldExtractProduct(ProductName name);
file delegate ValueTask WriteProductArtifacts(ProductName name, ProductDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteProductInformationFile(ProductName name, ProductDto dto, CancellationToken cancellationToken);
file sealed class ExtractProductsHandler(ListProducts list,
ShouldExtractProduct shouldExtract,
WriteProductArtifacts writeArtifacts,
ExtractProductPolicies extractProductPolicies,
ExtractProductGroups extractProductGroups,
ExtractProductTags extractProductTags,
ExtractProductApis extractProductApis)
internal static class ProductModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(product => shouldExtract(product.Name))
.IterParallel(async product => await ExtractProduct(product.Name, product.Dto, cancellationToken),
cancellationToken);
private async ValueTask ExtractProduct(ProductName name, ProductDto dto, CancellationToken cancellationToken)
public static void ConfigureExtractProducts(IHostApplicationBuilder builder)
{
await writeArtifacts(name, dto, cancellationToken);
await extractProductPolicies(name, cancellationToken);
await extractProductGroups(name, cancellationToken);
await extractProductTags(name, cancellationToken);
await extractProductApis(name, cancellationToken);
ConfigureListProducts(builder);
ConfigureShouldExtractProduct(builder);
ProductPolicyModule.ConfigureExtractProductPolicies(builder);
ProductGroupModule.ConfigureExtractProductGroups(builder);
ProductTagModule.ConfigureExtractProductTags(builder);
ProductApiModule.ConfigureExtractProductApis(builder);
ConfigureWriteProductArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractProducts);
}
}
file sealed class ListProductsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(ProductName, ProductDto)> Handle(CancellationToken cancellationToken) =>
ProductsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractProductHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(ProductName name)
private static ExtractProducts GetExtractProducts(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListProducts>();
var shouldExtract = provider.GetRequiredService<ShouldExtractProduct>();
var writeArtifacts = provider.GetRequiredService<WriteProductArtifacts>();
var extractProductPolicies = provider.GetRequiredService<ExtractProductPolicies>();
var extractProductGroups = provider.GetRequiredService<ExtractProductGroups>();
var extractProductTags = provider.GetRequiredService<ExtractProductTags>();
var extractProductApis = provider.GetRequiredService<ExtractProductApis>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractProducts));
logger.LogInformation("Extracting products...");
await list(cancellationToken)
.Where(product => shouldExtract(product.Name))
.IterParallel(async product => await extractProduct(product.Name, product.Dto, cancellationToken),
cancellationToken);
};
async ValueTask extractProduct(ProductName name, ProductDto dto, CancellationToken cancellationToken)
{
await writeArtifacts(name, dto, cancellationToken);
await extractProductPolicies(name, cancellationToken);
await extractProductGroups(name, cancellationToken);
await extractProductTags(name, cancellationToken);
await extractProductApis(name, cancellationToken);
}
}
private static void ConfigureListProducts(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListProducts);
}
private static ListProducts GetListProducts(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
ProductsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
public static void ConfigureShouldExtractProduct(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractProduct);
}
private static ShouldExtractProduct GetShouldExtractProduct(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<ProductName>();
return shouldExtract(name);
}
}
file sealed class WriteProductArtifactsHandler(WriteProductInformationFile writeInformationFile)
{
public async ValueTask Handle(ProductName name, ProductDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteProductArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteProductInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteProductArtifacts);
}
}
file sealed class WriteProductInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(ProductName name, ProductDto dto, CancellationToken cancellationToken)
private static WriteProductArtifacts GetWriteProductArtifacts(IServiceProvider provider)
{
var informationFile = ProductInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteProductInformationFile>();
logger.LogInformation("Writing product information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class ProductServices
{
public static void ConfigureExtractProducts(IServiceCollection services)
private static void ConfigureWriteProductInformationFile(IHostApplicationBuilder builder)
{
ConfigureListProducts(services);
ConfigureShouldExtractProduct(services);
ConfigureWriteProductArtifacts(services);
ProductPolicyServices.ConfigureExtractProductPolicies(services);
ProductGroupServices.ConfigureExtractProductGroups(services);
ProductTagServices.ConfigureExtractProductTags(services);
ProductApiServices.ConfigureExtractProductApis(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractProductsHandler>();
services.TryAddSingleton<ExtractProducts>(provider => provider.GetRequiredService<ExtractProductsHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteProductInformationFile);
}
private static void ConfigureListProducts(IServiceCollection services)
private static WriteProductInformationFile GetWriteProductInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListProductsHandler>();
services.TryAddSingleton<ListProducts>(provider => provider.GetRequiredService<ListProductsHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = ProductInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing product information file {ProductInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
public static void ConfigureShouldExtractProduct(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractProductHandler>();
services.TryAddSingleton<ShouldExtractProduct>(provider => provider.GetRequiredService<ShouldExtractProductHandler>().Handle);
}
private static void ConfigureWriteProductArtifacts(IServiceCollection services)
{
ConfigureWriteProductInformationFile(services);
services.TryAddSingleton<WriteProductArtifactsHandler>();
services.TryAddSingleton<WriteProductArtifacts>(provider => provider.GetRequiredService<WriteProductArtifactsHandler>().Handle);
}
private static void ConfigureWriteProductInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteProductInformationFileHandler>();
services.TryAddSingleton<WriteProductInformationFile>(provider => provider.GetRequiredService<WriteProductInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ProductExtractor");
}

Просмотреть файл

@ -2,93 +2,105 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractProductApis(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask ExtractProductApis(ProductName productName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiName Name, ProductApiDto Dto)> ListProductApis(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductApiArtifacts(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductApiInformationFile(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(ApiName Name, ProductApiDto Dto)> ListProductApis(ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductApiArtifacts(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductApiInformationFile(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken);
file sealed class ExtractProductApisHandler(ListProductApis list, ShouldExtractApiName shouldExtractApi, WriteProductApiArtifacts writeArtifacts)
internal static class ProductApiModule
{
public async ValueTask Handle(ProductName productName, CancellationToken cancellationToken) =>
await list(productName, cancellationToken)
.Where(api => shouldExtractApi(api.Name))
.IterParallel(async productapi => await writeArtifacts(productapi.Name, productapi.Dto, productName, cancellationToken),
cancellationToken);
}
file sealed class ListProductApisHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(ApiName, ProductApiDto)> Handle(ProductName productName, CancellationToken cancellationToken) =>
ProductApisUri.From(productName, serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteProductApiArtifactsHandler(WriteProductApiInformationFile writeApiFile)
{
public async ValueTask Handle(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken)
public static void ConfigureExtractProductApis(IHostApplicationBuilder builder)
{
await writeApiFile(name, dto, productName, cancellationToken);
}
}
ConfigureListProductApis(builder);
ApiModule.ConfigureShouldExtractApiName(builder);
ConfigureWriteProductApiArtifacts(builder);
file sealed class WriteProductApiInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(ApiName name, ProductApiDto dto, ProductName productName, CancellationToken cancellationToken)
{
var informationFile = ProductApiInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product api information file {ProductApiInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
}
}
internal static class ProductApiServices
{
public static void ConfigureExtractProductApis(IServiceCollection services)
{
ConfigureListProductApis(services);
ConfigureWriteProductApiArtifacts(services);
ApiServices.ConfigureShouldExtractApiName(services);
services.TryAddSingleton<ExtractProductApisHandler>();
services.TryAddSingleton<ExtractProductApis>(provider => provider.GetRequiredService<ExtractProductApisHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractProductApis);
}
private static void ConfigureListProductApis(IServiceCollection services)
private static ExtractProductApis GetExtractProductApis(IServiceProvider provider)
{
services.TryAddSingleton<ListProductApisHandler>();
services.TryAddSingleton<ListProductApis>(provider => provider.GetRequiredService<ListProductApisHandler>().Handle);
var list = provider.GetRequiredService<ListProductApis>();
var shouldExtractApi = provider.GetRequiredService<ShouldExtractApiName>();
var writeArtifacts = provider.GetRequiredService<WriteProductApiArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (productName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractProductApis));
logger.LogInformation("Extracting APIs for product {ProductName}...", productName);
await list(productName, cancellationToken)
.Where(api => shouldExtractApi(api.Name))
.IterParallel(async productapi => await writeArtifacts(productapi.Name, productapi.Dto, productName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteProductApiArtifacts(IServiceCollection services)
private static void ConfigureListProductApis(IHostApplicationBuilder builder)
{
ConfigureWriteProductApiInformationFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteProductApiArtifactsHandler>();
services.TryAddSingleton<WriteProductApiArtifacts>(provider => provider.GetRequiredService<WriteProductApiArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListProductApis);
}
private static void ConfigureWriteProductApiInformationFile(IServiceCollection services)
private static ListProductApis GetListProductApis(IServiceProvider provider)
{
services.TryAddSingleton<WriteProductApiInformationFileHandler>();
services.TryAddSingleton<WriteProductApiInformationFile>(provider => provider.GetRequiredService<WriteProductApiInformationFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ProductApiExtractor");
return (productName, cancellationToken) =>
ProductApisUri.From(productName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteProductApiArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteProductApiInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteProductApiArtifacts);
}
private static WriteProductApiArtifacts GetWriteProductApiArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteProductApiInformationFile>();
return async (name, dto, productName, cancellationToken) =>
{
await writeInformationFile(name, dto, productName, cancellationToken);
};
}
public static void ConfigureWriteProductApiInformationFile(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetWriteProductApiInformationFile);
}
private static WriteProductApiInformationFile GetWriteProductApiInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, productName, cancellationToken) =>
{
var informationFile = ProductApiInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product API information file {ProductApiInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -2,90 +2,105 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractProductGroups(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask ExtractProductGroups(ProductName productName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(GroupName Name, ProductGroupDto Dto)> ListProductGroups(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductGroupArtifacts(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductGroupInformationFile(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(GroupName Name, ProductGroupDto Dto)> ListProductGroups(ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductGroupArtifacts(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductGroupInformationFile(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken);
file sealed class ExtractProductGroupsHandler(ListProductGroups list, WriteProductGroupArtifacts writeArtifacts)
internal static class ProductGroupModule
{
public async ValueTask Handle(ProductName productName, CancellationToken cancellationToken) =>
await list(productName, cancellationToken)
.IterParallel(async productgroup => await writeArtifacts(productgroup.Name, productgroup.Dto, productName, cancellationToken),
cancellationToken);
}
file sealed class ListProductGroupsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(GroupName, ProductGroupDto)> Handle(ProductName productName, CancellationToken cancellationToken) =>
ProductGroupsUri.From(productName, serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteProductGroupArtifactsHandler(WriteProductGroupInformationFile writeGroupFile)
{
public async ValueTask Handle(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken)
public static void ConfigureExtractProductGroups(IHostApplicationBuilder builder)
{
await writeGroupFile(name, dto, productName, cancellationToken);
}
}
ConfigureListProductGroups(builder);
GroupModule.ConfigureShouldExtractGroup(builder);
ConfigureWriteProductGroupArtifacts(builder);
file sealed class WriteProductGroupInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(GroupName name, ProductGroupDto dto, ProductName productName, CancellationToken cancellationToken)
{
var informationFile = ProductGroupInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product group information file {ProductGroupInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
}
}
internal static class ProductGroupServices
{
public static void ConfigureExtractProductGroups(IServiceCollection services)
{
ConfigureListProductGroups(services);
ConfigureWriteProductGroupArtifacts(services);
services.TryAddSingleton<ExtractProductGroupsHandler>();
services.TryAddSingleton<ExtractProductGroups>(provider => provider.GetRequiredService<ExtractProductGroupsHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractProductGroups);
}
private static void ConfigureListProductGroups(IServiceCollection services)
private static ExtractProductGroups GetExtractProductGroups(IServiceProvider provider)
{
services.TryAddSingleton<ListProductGroupsHandler>();
services.TryAddSingleton<ListProductGroups>(provider => provider.GetRequiredService<ListProductGroupsHandler>().Handle);
var list = provider.GetRequiredService<ListProductGroups>();
var shouldExtractGroup = provider.GetRequiredService<ShouldExtractGroup>();
var writeArtifacts = provider.GetRequiredService<WriteProductGroupArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (productName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractProductGroups));
logger.LogInformation("Extracting groups for product {ProductName}...", productName);
await list(productName, cancellationToken)
.Where(group => shouldExtractGroup(group.Name))
.IterParallel(async productgroup => await writeArtifacts(productgroup.Name, productgroup.Dto, productName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteProductGroupArtifacts(IServiceCollection services)
private static void ConfigureListProductGroups(IHostApplicationBuilder builder)
{
ConfigureWriteProductGroupInformationFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteProductGroupArtifactsHandler>();
services.TryAddSingleton<WriteProductGroupArtifacts>(provider => provider.GetRequiredService<WriteProductGroupArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListProductGroups);
}
private static void ConfigureWriteProductGroupInformationFile(IServiceCollection services)
private static ListProductGroups GetListProductGroups(IServiceProvider provider)
{
services.TryAddSingleton<WriteProductGroupInformationFileHandler>();
services.TryAddSingleton<WriteProductGroupInformationFile>(provider => provider.GetRequiredService<WriteProductGroupInformationFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ProductGroupExtractor");
return (productName, cancellationToken) =>
ProductGroupsUri.From(productName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteProductGroupArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteProductGroupInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteProductGroupArtifacts);
}
private static WriteProductGroupArtifacts GetWriteProductGroupArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteProductGroupInformationFile>();
return async (name, dto, productName, cancellationToken) =>
{
await writeInformationFile(name, dto, productName, cancellationToken);
};
}
public static void ConfigureWriteProductGroupInformationFile(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetWriteProductGroupInformationFile);
}
private static WriteProductGroupInformationFile GetWriteProductGroupInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, productName, cancellationToken) =>
{
var informationFile = ProductGroupInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product group information file {ProductGroupInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -2,91 +2,102 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractProductPolicies(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask ExtractProductPolicies(ProductName productName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ProductPolicyName Name, ProductPolicyDto Dto)> ListProductPolicies(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductPolicyArtifacts(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductPolicyFile(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(ProductPolicyName Name, ProductPolicyDto Dto)> ListProductPolicies(ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductPolicyArtifacts(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductPolicyFile(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken);
file sealed class ExtractProductPoliciesHandler(ListProductPolicies list, WriteProductPolicyArtifacts writeArtifacts)
internal static class ProductPolicyModule
{
public async ValueTask Handle(ProductName productName, CancellationToken cancellationToken) =>
await list(productName, cancellationToken)
.IterParallel(async productpolicy => await writeArtifacts(productpolicy.Name, productpolicy.Dto, productName, cancellationToken),
cancellationToken);
}
file sealed class ListProductPoliciesHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(ProductPolicyName, ProductPolicyDto)> Handle(ProductName productName, CancellationToken cancellationToken) =>
ProductPoliciesUri.From(productName, serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteProductPolicyArtifactsHandler(WriteProductPolicyFile writePolicyFile)
{
public async ValueTask Handle(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken)
public static void ConfigureExtractProductPolicies(IHostApplicationBuilder builder)
{
await writePolicyFile(name, dto, productName, cancellationToken);
}
}
ConfigureListProductPolicies(builder);
ConfigureWriteProductPolicyArtifacts(builder);
file sealed class WriteProductPolicyFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(ProductPolicyName name, ProductPolicyDto dto, ProductName productName, CancellationToken cancellationToken)
{
var policyFile = ProductPolicyFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product policy file {ProductPolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
}
}
internal static class ProductPolicyServices
{
public static void ConfigureExtractProductPolicies(IServiceCollection services)
{
ConfigureListProductPolicies(services);
ConfigureWriteProductPolicyArtifacts(services);
services.TryAddSingleton<ExtractProductPoliciesHandler>();
services.TryAddSingleton<ExtractProductPolicies>(provider => provider.GetRequiredService<ExtractProductPoliciesHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractProductPolicies);
}
private static void ConfigureListProductPolicies(IServiceCollection services)
private static ExtractProductPolicies GetExtractProductPolicies(IServiceProvider provider)
{
services.TryAddSingleton<ListProductPoliciesHandler>();
services.TryAddSingleton<ListProductPolicies>(provider => provider.GetRequiredService<ListProductPoliciesHandler>().Handle);
var list = provider.GetRequiredService<ListProductPolicies>();
var writeArtifacts = provider.GetRequiredService<WriteProductPolicyArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (productName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractProductPolicies));
logger.LogInformation("Extracting policies for product {ProductName}...", productName);
await list(productName, cancellationToken)
.IterParallel(async policy => await writeArtifacts(policy.Name, policy.Dto, productName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteProductPolicyArtifacts(IServiceCollection services)
private static void ConfigureListProductPolicies(IHostApplicationBuilder builder)
{
ConfigureWriteProductPolicyFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteProductPolicyArtifactsHandler>();
services.TryAddSingleton<WriteProductPolicyArtifacts>(provider => provider.GetRequiredService<WriteProductPolicyArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListProductPolicies);
}
private static void ConfigureWriteProductPolicyFile(IServiceCollection services)
private static ListProductPolicies GetListProductPolicies(IServiceProvider provider)
{
services.TryAddSingleton<WriteProductPolicyFileHandler>();
services.TryAddSingleton<WriteProductPolicyFile>(provider => provider.GetRequiredService<WriteProductPolicyFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ProductPolicyExtractor");
return (productName, cancellationToken) =>
ProductPoliciesUri.From(productName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteProductPolicyArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteProductPolicyFile(builder);
builder.Services.TryAddSingleton(GetWriteProductPolicyArtifacts);
}
private static WriteProductPolicyArtifacts GetWriteProductPolicyArtifacts(IServiceProvider provider)
{
var writePolicyFile = provider.GetRequiredService<WriteProductPolicyFile>();
return async (name, dto, productName, cancellationToken) =>
await writePolicyFile(name, dto, productName, cancellationToken);
}
private static void ConfigureWriteProductPolicyFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteProductPolicyFile);
}
private static WriteProductPolicyFile GetWriteProductPolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, productName, cancellationToken) =>
{
var policyFile = ProductPolicyFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product policy file {PolicyFile}", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}

Просмотреть файл

@ -2,90 +2,107 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractProductTags(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask ExtractProductTags(ProductName productName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(TagName Name, ProductTagDto Dto)> ListProductTags(ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductTagArtifacts(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken);
public delegate ValueTask WriteProductTagInformationFile(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(TagName Name, ProductTagDto Dto)> ListProductTags(ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductTagArtifacts(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken);
file delegate ValueTask WriteProductTagInformationFile(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken);
file sealed class ExtractProductTagsHandler(ListProductTags list, WriteProductTagArtifacts writeArtifacts)
internal static class ProductTagModule
{
public async ValueTask Handle(ProductName productName, CancellationToken cancellationToken) =>
await list(productName, cancellationToken)
.IterParallel(async producttag => await writeArtifacts(producttag.Name, producttag.Dto, productName, cancellationToken),
cancellationToken);
}
file sealed class ListProductTagsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(TagName, ProductTagDto)> Handle(ProductName productName, CancellationToken cancellationToken) =>
ProductTagsUri.From(productName, serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteProductTagArtifactsHandler(WriteProductTagInformationFile writeTagFile)
{
public async ValueTask Handle(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken)
public static void ConfigureExtractProductTags(IHostApplicationBuilder builder)
{
await writeTagFile(name, dto, productName, cancellationToken);
}
}
ConfigureListProductTags(builder);
TagModule.ConfigureShouldExtractTag(builder);
ConfigureWriteProductTagArtifacts(builder);
file sealed class WriteProductTagInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(TagName name, ProductTagDto dto, ProductName productName, CancellationToken cancellationToken)
{
var informationFile = ProductTagInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product tag information file {ProductTagInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
}
}
internal static class ProductTagServices
{
public static void ConfigureExtractProductTags(IServiceCollection services)
{
ConfigureListProductTags(services);
ConfigureWriteProductTagArtifacts(services);
services.TryAddSingleton<ExtractProductTagsHandler>();
services.TryAddSingleton<ExtractProductTags>(provider => provider.GetRequiredService<ExtractProductTagsHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractProductTags);
}
private static void ConfigureListProductTags(IServiceCollection services)
private static ExtractProductTags GetExtractProductTags(IServiceProvider provider)
{
services.TryAddSingleton<ListProductTagsHandler>();
services.TryAddSingleton<ListProductTags>(provider => provider.GetRequiredService<ListProductTagsHandler>().Handle);
var list = provider.GetRequiredService<ListProductTags>();
var shouldExtractTag = provider.GetRequiredService<ShouldExtractTag>();
var writeArtifacts = provider.GetRequiredService<WriteProductTagArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (productName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractProductTags));
logger.LogInformation("Extracting tags for product {ProductName}...", productName);
await list(productName, cancellationToken)
.Where(tag => shouldExtractTag(tag.Name))
.IterParallel(async producttag => await writeArtifacts(producttag.Name, producttag.Dto, productName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteProductTagArtifacts(IServiceCollection services)
private static void ConfigureListProductTags(IHostApplicationBuilder builder)
{
ConfigureWriteProductTagInformationFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteProductTagArtifactsHandler>();
services.TryAddSingleton<WriteProductTagArtifacts>(provider => provider.GetRequiredService<WriteProductTagArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListProductTags);
}
private static void ConfigureWriteProductTagInformationFile(IServiceCollection services)
private static ListProductTags GetListProductTags(IServiceProvider provider)
{
services.TryAddSingleton<WriteProductTagInformationFileHandler>();
services.TryAddSingleton<WriteProductTagInformationFile>(provider => provider.GetRequiredService<WriteProductTagInformationFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ProductTagExtractor");
return (productName, cancellationToken) =>
ProductTagsUri.From(productName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteProductTagArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteProductTagInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteProductTagArtifacts);
}
private static WriteProductTagArtifacts GetWriteProductTagArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteProductTagInformationFile>();
return async (name, dto, productName, cancellationToken) =>
{
await writeInformationFile(name, dto, productName, cancellationToken);
};
}
public static void ConfigureWriteProductTagInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteProductTagInformationFile);
}
private static WriteProductTagInformationFile GetWriteProductTagInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, productName, cancellationToken) =>
{
var informationFile = ProductTagInformationFile.From(name, productName, serviceDirectory);
logger.LogInformation("Writing product tag information file {ProductTagInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -1,9 +1,4 @@
using common;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace extractor;
@ -12,56 +7,6 @@ public static class Program
{
public static async Task Main(string[] arguments)
{
var host = Host.CreateDefaultBuilder(arguments)
.ConfigureAppConfiguration(ConfigureConfiguration)
.ConfigureServices(ConfigureServices)
.Build();
await RunExtractor(host);
await HostingModule.RunHost(arguments, "extractor", AppModule.ConfigureRunApplication);
}
private static void ConfigureConfiguration(IConfigurationBuilder builder)
{
builder.AddUserSecrets(typeof(Program).Assembly);
var configuration = builder.Build();
configuration.TryGetValue("CONFIGURATION_YAML_PATH")
.Iter(path => builder.AddYamlFile(path));
}
private static void ConfigureServices(IServiceCollection services)
{
CommonServices.Configure(services);
AppServices.ConfigureRunExtractor(services);
}
private static async Task RunExtractor(IHost host)
{
var applicationLifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var cancellationToken = applicationLifetime.ApplicationStopping;
await host.StartAsync(cancellationToken);
try
{
var runExtractor = host.Services.GetRequiredService<RunExtractor>();
await runExtractor(cancellationToken);
}
catch (Exception exception)
{
Environment.ExitCode = -1;
LogException(host, exception);
throw;
}
finally
{
applicationLifetime.StopApplication();
}
}
private static void LogException(IHost host, Exception exception)
{
var logger = host.Services.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(Program));
logger.LogCritical(exception, "Extractor failed with error {ErrorMessage}", exception.Message);
}
}
}

Просмотреть файл

@ -2,91 +2,102 @@
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractServicePolicies(CancellationToken cancellationToken);
public delegate ValueTask ExtractServicePolicies(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ServicePolicyName Name, ServicePolicyDto Dto)> ListServicePolicies(CancellationToken cancellationToken);
public delegate ValueTask WriteServicePolicyArtifacts(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteServicePolicyFile(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(ServicePolicyName Name, ServicePolicyDto Dto)> ListServicePolicies(CancellationToken cancellationToken);
file delegate ValueTask WriteServicePolicyArtifacts(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteServicePolicyFile(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken);
file sealed class ExtractServicePoliciesHandler(ListServicePolicies list, WriteServicePolicyArtifacts writeArtifacts)
internal static class ServicePolicyModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.IterParallel(async servicepolicy => await writeArtifacts(servicepolicy.Name, servicepolicy.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListServicePoliciesHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(ServicePolicyName, ServicePolicyDto)> Handle(CancellationToken cancellationToken) =>
ServicePoliciesUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class WriteServicePolicyArtifactsHandler(WriteServicePolicyFile writePolicyFile)
{
public async ValueTask Handle(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken)
public static void ConfigureExtractServicePolicies(IHostApplicationBuilder builder)
{
await writePolicyFile(name, dto, cancellationToken);
}
}
ConfigureListServicePolicies(builder);
ConfigureWriteServicePolicyArtifacts(builder);
file sealed class WriteServicePolicyFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken)
{
var policyFile = ServicePolicyFile.From(name, serviceDirectory);
logger.LogInformation("Writing service policy file {ServicePolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
}
}
internal static class ServicePolicyServices
{
public static void ConfigureExtractServicePolicies(IServiceCollection services)
{
ConfigureListServicePolicies(services);
ConfigureWriteServicePolicyArtifacts(services);
services.TryAddSingleton<ExtractServicePoliciesHandler>();
services.TryAddSingleton<ExtractServicePolicies>(provider => provider.GetRequiredService<ExtractServicePoliciesHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractServicePolicies);
}
private static void ConfigureListServicePolicies(IServiceCollection services)
private static ExtractServicePolicies GetExtractServicePolicies(IServiceProvider provider)
{
services.TryAddSingleton<ListServicePoliciesHandler>();
services.TryAddSingleton<ListServicePolicies>(provider => provider.GetRequiredService<ListServicePoliciesHandler>().Handle);
var list = provider.GetRequiredService<ListServicePolicies>();
var writeArtifacts = provider.GetRequiredService<WriteServicePolicyArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractServicePolicies));
logger.LogInformation("Extracting service policies...");
await list(cancellationToken)
.IterParallel(async servicepolicy => await writeArtifacts(servicepolicy.Name, servicepolicy.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureWriteServicePolicyArtifacts(IServiceCollection services)
private static void ConfigureListServicePolicies(IHostApplicationBuilder builder)
{
ConfigureWriteServicePolicyFile(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<WriteServicePolicyArtifactsHandler>();
services.TryAddSingleton<WriteServicePolicyArtifacts>(provider => provider.GetRequiredService<WriteServicePolicyArtifactsHandler>().Handle);
builder.Services.TryAddSingleton(GetListServicePolicies);
}
private static void ConfigureWriteServicePolicyFile(IServiceCollection services)
private static ListServicePolicies GetListServicePolicies(IServiceProvider provider)
{
services.TryAddSingleton<WriteServicePolicyFileHandler>();
services.TryAddSingleton<WriteServicePolicyFile>(provider => provider.GetRequiredService<WriteServicePolicyFileHandler>().Handle);
}
}
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("ServicePolicyExtractor");
return cancellationToken =>
ServicePoliciesUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteServicePolicyArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteServicePolicyFile(builder);
builder.Services.TryAddSingleton(GetWriteServicePolicyArtifacts);
}
private static WriteServicePolicyArtifacts GetWriteServicePolicyArtifacts(IServiceProvider provider)
{
var writePolicyFile = provider.GetRequiredService<WriteServicePolicyFile>();
return async (ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken) =>
{
await writePolicyFile(name, dto, cancellationToken);
};
}
private static void ConfigureWriteServicePolicyFile(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetWriteServicePolicyFile);
}
private static WriteServicePolicyFile GetWriteServicePolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (ServicePolicyName name, ServicePolicyDto dto, CancellationToken cancellationToken) =>
{
var policyFile = ServicePolicyFile.From(name, serviceDirectory);
logger.LogInformation("Writing service policy file {ServicePolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}

Просмотреть файл

@ -1,5 +1,8 @@
using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Frozen;
@ -14,7 +17,7 @@ public sealed class ShouldExtractFactory(ConfigurationJson configurationJson, IL
{
private static readonly FrozenDictionary<Type, string> typeSectionNames = GetTypeSectionNames();
private static readonly FrozenDictionary<string, Type> sectionNameTypes = GetSectionNameTypes(typeSectionNames);
private readonly FrozenDictionary<Type, FrozenSet<string>> resourcesToExtract = GetResourcesToExtract(configurationJson, sectionNameTypes);
private readonly FrozenDictionary<Type, FrozenSet<string>> resourcesToExtract = GetResourcesToExtract(configurationJson);
private readonly ILogger logger = loggerFactory.CreateLogger<ShouldExtractFactory>();
private static FrozenDictionary<Type, string> GetTypeSectionNames() =>
@ -32,22 +35,23 @@ public sealed class ShouldExtractFactory(ConfigurationJson configurationJson, IL
[typeof(GroupName)] = "groupNames",
[typeof(SubscriptionName)] = "subscriptionNames",
[typeof(ApiName)] = "apiNames",
[typeof(WorkspaceName)] = "workspaceNames",
}
.ToFrozenDictionary();
private static FrozenDictionary<string, Type> GetSectionNameTypes(FrozenDictionary<Type, string> typeSectionNames) =>
typeSectionNames.ToFrozenDictionary(kvp => kvp.Value, kvp => kvp.Key, StringComparer.OrdinalIgnoreCase);
private static FrozenDictionary<Type, FrozenSet<string>> GetResourcesToExtract(ConfigurationJson configurationJson, FrozenDictionary<string, Type> sectionNameTypes) =>
private static FrozenDictionary<Type, FrozenSet<string>> GetResourcesToExtract(ConfigurationJson configurationJson) =>
configurationJson.Value
// Get configuration sections that are JSON arrays
.ChooseValue(node => node.TryAsJsonArray())
.ChooseValues(node => node.TryAsJsonArray().ToOption())
// Map each JSON array to a set of strings
.MapValue(jsonArray => jsonArray.Choose(node => node.TryAsString())
.Where(value => string.IsNullOrWhiteSpace(value) is false)
.ToFrozenSet(StringComparer.OrdinalIgnoreCase))
.Select(kvp => kvp.MapValue(jsonArray => jsonArray.PickStrings()
.Where(value => string.IsNullOrWhiteSpace(value) is false)
.ToFrozenSet(StringComparer.OrdinalIgnoreCase)))
// Map each configuration section to a resource name type
.ChooseKey(sectionNameTypes.Find)
.ChooseKeys(sectionNameTypes.Find)
.ToFrozenDictionary();
public static string GetConfigurationSectionName<T>() =>
@ -75,9 +79,27 @@ public sealed class ShouldExtractFactory(ConfigurationJson configurationJson, IL
if (shouldExtract is false)
{
logger.LogWarning("{ResourceType} {ResourceName} is not in configuration and will be skipped.", typeof(TName).Name, name);
logger.LogWarning("{ResourceType} {ResourceName} is not in configuration and will be not be extracted.", typeof(TName).Name, name);
}
return shouldExtract;
}
}
internal static class ShouldExtractModule
{
public static void ConfigureShouldExtractFactory(IHostApplicationBuilder builder)
{
ConfigurationModule.ConfigureConfigurationJson(builder);
builder.Services.TryAddSingleton(GetShouldExtractFactory);
}
private static ShouldExtractFactory GetShouldExtractFactory(IServiceProvider provider)
{
var configurationJson = provider.GetRequiredService<ConfigurationJson>();
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
return new ShouldExtractFactory(configurationJson, loggerFactory);
}
}

Просмотреть файл

@ -3,122 +3,140 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractSubscriptions(CancellationToken cancellationToken);
public delegate ValueTask ExtractSubscriptions(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(SubscriptionName Name, SubscriptionDto Dto)> ListSubscriptions(CancellationToken cancellationToken);
public delegate bool ShouldExtractSubscription(SubscriptionName name, SubscriptionDto dto);
public delegate ValueTask WriteSubscriptionArtifacts(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteSubscriptionInformationFile(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(SubscriptionName Name, SubscriptionDto Dto)> ListSubscriptions(CancellationToken cancellationToken);
file delegate bool ShouldExtractSubscription(SubscriptionName name, SubscriptionDto dto);
file delegate ValueTask WriteSubscriptionArtifacts(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteSubscriptionInformationFile(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken);
file sealed class ExtractSubscriptionsHandler(ListSubscriptions list, ShouldExtractSubscription shouldExtractSubscription, WriteSubscriptionArtifacts writeArtifacts)
internal static class SubscriptionModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(subscription => shouldExtractSubscription(subscription.Name, subscription.Dto))
.IterParallel(async subscription => await writeArtifacts(subscription.Name, subscription.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListSubscriptionsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(SubscriptionName, SubscriptionDto)> Handle(CancellationToken cancellationToken) =>
SubscriptionsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractSubscriptionHandler(ShouldExtractFactory shouldExtractFactory, ShouldExtractApiName shouldExtractApi, ShouldExtractProduct shouldExtractProduct)
{
public bool Handle(SubscriptionName name, SubscriptionDto dto) =>
// Don't extract the master subscription
name != SubscriptionName.From("master")
// Check name from configuration override
&& shouldExtractFactory.Create<SubscriptionName>().Invoke(name)
// Don't extract subscription if its API should not be extracted
&& SubscriptionModule.TryGetApiName(dto)
.Map(shouldExtractApi.Invoke)
.IfNone(true)
// Don't extract subscription if its product should not be extracted
&& SubscriptionModule.TryGetProductName(dto)
.Map(shouldExtractProduct.Invoke)
.IfNone(true);
}
file sealed class WriteSubscriptionArtifactsHandler(WriteSubscriptionInformationFile writeInformationFile)
{
public async ValueTask Handle(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken)
public static void ConfigureExtractSubscriptions(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
}
}
ConfigureListSubscriptions(builder);
ConfigureShouldExtractSubscription(builder);
ConfigureWriteSubscriptionArtifacts(builder);
file sealed class WriteSubscriptionInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(SubscriptionName name, SubscriptionDto dto, CancellationToken cancellationToken)
{
var informationFile = SubscriptionInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing subscription information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
}
}
internal static class SubscriptionServices
{
public static void ConfigureExtractSubscriptions(IServiceCollection services)
{
ConfigureListSubscriptions(services);
ConfigureShouldExtractSubscription(services);
ConfigureWriteSubscriptionArtifacts(services);
services.TryAddSingleton<ExtractSubscriptionsHandler>();
services.TryAddSingleton<ExtractSubscriptions>(provider => provider.GetRequiredService<ExtractSubscriptionsHandler>().Handle);
builder.Services.TryAddSingleton(GetExtractSubscriptions);
}
private static void ConfigureListSubscriptions(IServiceCollection services)
private static ExtractSubscriptions GetExtractSubscriptions(IServiceProvider provider)
{
services.TryAddSingleton<ListSubscriptionsHandler>();
services.TryAddSingleton<ListSubscriptions>(provider => provider.GetRequiredService<ListSubscriptionsHandler>().Handle);
var list = provider.GetRequiredService<ListSubscriptions>();
var shouldExtract = provider.GetRequiredService<ShouldExtractSubscription>();
var writeArtifacts = provider.GetRequiredService<WriteSubscriptionArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractSubscriptions));
logger.LogInformation("Extracting subscriptions...");
await list(cancellationToken)
.Where(subscription => shouldExtract(subscription.Name, subscription.Dto))
.IterParallel(async subscription => await writeArtifacts(subscription.Name, subscription.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureShouldExtractSubscription(IServiceCollection services)
private static void ConfigureListSubscriptions(IHostApplicationBuilder builder)
{
ApiServices.ConfigureShouldExtractApiName(services);
ProductServices.ConfigureShouldExtractProduct(services);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
services.TryAddSingleton<ShouldExtractSubscriptionHandler>();
services.TryAddSingleton<ShouldExtractSubscription>(provider => provider.GetRequiredService<ShouldExtractSubscriptionHandler>().Handle);
builder.Services.TryAddSingleton(GetListSubscriptions);
}
private static void ConfigureWriteSubscriptionArtifacts(IServiceCollection services)
private static ListSubscriptions GetListSubscriptions(IServiceProvider provider)
{
ConfigureWriteSubscriptionInformationFile(services);
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
services.TryAddSingleton<WriteSubscriptionArtifactsHandler>();
services.TryAddSingleton<WriteSubscriptionArtifacts>(provider => provider.GetRequiredService<WriteSubscriptionArtifactsHandler>().Handle);
return cancellationToken =>
SubscriptionsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteSubscriptionInformationFile(IServiceCollection services)
private static void ConfigureShouldExtractSubscription(IHostApplicationBuilder builder)
{
services.TryAddSingleton<WriteSubscriptionInformationFileHandler>();
services.TryAddSingleton<WriteSubscriptionInformationFile>(provider => provider.GetRequiredService<WriteSubscriptionInformationFileHandler>().Handle);
}
}
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
ApiModule.ConfigureShouldExtractApiName(builder);
ProductModule.ConfigureShouldExtractProduct(builder);
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("SubscriptionExtractor");
builder.Services.TryAddSingleton(GetShouldExtractSubscription);
}
private static ShouldExtractSubscription GetShouldExtractSubscription(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtractApi = provider.GetRequiredService<ShouldExtractApiName>();
var shouldExtractProduct = provider.GetRequiredService<ShouldExtractProduct>();
var shouldExtractSubscriptionName = shouldExtractFactory.Create<SubscriptionName>();
return (name, dto) =>
// Don't extract the master subscription
name != SubscriptionName.From("master")
// Check name from configuration override
&& shouldExtractSubscriptionName(name)
// Don't extract subscription if its API should not be extracted
&& common.SubscriptionModule.TryGetApiName(dto)
.Map(shouldExtractApi.Invoke)
.IfNone(true)
// Don't extract subscription if its product should not be extracted
&& common.SubscriptionModule.TryGetProductName(dto)
.Map(shouldExtractProduct.Invoke)
.IfNone(true);
}
private static void ConfigureWriteSubscriptionArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteSubscriptionInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteSubscriptionArtifacts);
}
private static WriteSubscriptionArtifacts GetWriteSubscriptionArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteSubscriptionInformationFile>();
return async (name, dto, cancellationToken) =>
{
await writeInformationFile(name, dto, cancellationToken);
};
}
private static void ConfigureWriteSubscriptionInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteSubscriptionInformationFile);
}
private static WriteSubscriptionInformationFile GetWriteSubscriptionInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = SubscriptionInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing subscription information file {SubscriptionInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -3,110 +3,122 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractTags(CancellationToken cancellationToken);
public delegate ValueTask ExtractTags(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(TagName Name, TagDto Dto)> ListTags(CancellationToken cancellationToken);
public delegate bool ShouldExtractTag(TagName name);
public delegate ValueTask WriteTagArtifacts(TagName name, TagDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteTagInformationFile(TagName name, TagDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(TagName Name, TagDto Dto)> ListTags(CancellationToken cancellationToken);
internal delegate bool ShouldExtractTag(TagName name);
file delegate ValueTask WriteTagArtifacts(TagName name, TagDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteTagInformationFile(TagName name, TagDto dto, CancellationToken cancellationToken);
file sealed class ExtractTagsHandler(ListTags list, ShouldExtractTag shouldExtract, WriteTagArtifacts writeArtifacts)
internal static class TagModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(tag => shouldExtract(tag.Name))
.IterParallel(async tag => await writeArtifacts(tag.Name, tag.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListTagsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(TagName, TagDto)> Handle(CancellationToken cancellationToken) =>
TagsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractTagHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(TagName name)
public static void ConfigureExtractTags(IHostApplicationBuilder builder)
{
ConfigureListTags(builder);
ConfigureShouldExtractTag(builder);
ConfigureWriteTagArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractTags);
}
private static ExtractTags GetExtractTags(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListTags>();
var shouldExtract = provider.GetRequiredService<ShouldExtractTag>();
var writeArtifacts = provider.GetRequiredService<WriteTagArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractTags));
logger.LogInformation("Extracting tags...");
await list(cancellationToken)
.Where(tag => shouldExtract(tag.Name))
.IterParallel(async tag => await writeArtifacts(tag.Name, tag.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListTags(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListTags);
}
private static ListTags GetListTags(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
TagsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
public static void ConfigureShouldExtractTag(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractTag);
}
private static ShouldExtractTag GetShouldExtractTag(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<TagName>();
return shouldExtract(name);
}
}
file sealed class WriteTagArtifactsHandler(WriteTagInformationFile writeInformationFile)
{
public async ValueTask Handle(TagName name, TagDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteTagArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteTagInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteTagArtifacts);
}
}
file sealed class WriteTagInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(TagName name, TagDto dto, CancellationToken cancellationToken)
private static WriteTagArtifacts GetWriteTagArtifacts(IServiceProvider provider)
{
var informationFile = TagInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteTagInformationFile>();
logger.LogInformation("Writing tag information file {InformationFile}", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class TagServices
{
public static void ConfigureExtractTags(IServiceCollection services)
private static void ConfigureWriteTagInformationFile(IHostApplicationBuilder builder)
{
ConfigureListTags(services);
ConfigureShouldExtractTag(services);
ConfigureWriteTagArtifacts(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractTagsHandler>();
services.TryAddSingleton<ExtractTags>(provider => provider.GetRequiredService<ExtractTagsHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteTagInformationFile);
}
private static void ConfigureListTags(IServiceCollection services)
private static WriteTagInformationFile GetWriteTagInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListTagsHandler>();
services.TryAddSingleton<ListTags>(provider => provider.GetRequiredService<ListTagsHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = TagInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing tag information file {TagInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
public static void ConfigureShouldExtractTag(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractTagHandler>();
services.TryAddSingleton<ShouldExtractTag>(provider => provider.GetRequiredService<ShouldExtractTagHandler>().Handle);
}
private static void ConfigureWriteTagArtifacts(IServiceCollection services)
{
ConfigureWriteTagInformationFile(services);
services.TryAddSingleton<WriteTagArtifactsHandler>();
services.TryAddSingleton<WriteTagArtifacts>(provider => provider.GetRequiredService<WriteTagArtifactsHandler>().Handle);
}
private static void ConfigureWriteTagInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteTagInformationFileHandler>();
services.TryAddSingleton<WriteTagInformationFile>(provider => provider.GetRequiredService<WriteTagInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("TagExtractor");
}

Просмотреть файл

@ -3,110 +3,122 @@ using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
internal delegate ValueTask ExtractVersionSets(CancellationToken cancellationToken);
public delegate ValueTask ExtractVersionSets(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(VersionSetName Name, VersionSetDto Dto)> ListVersionSets(CancellationToken cancellationToken);
public delegate bool ShouldExtractVersionSet(VersionSetName name);
public delegate ValueTask WriteVersionSetArtifacts(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteVersionSetInformationFile(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken);
file delegate IAsyncEnumerable<(VersionSetName Name, VersionSetDto Dto)> ListVersionSets(CancellationToken cancellationToken);
internal delegate bool ShouldExtractVersionSet(VersionSetName name);
file delegate ValueTask WriteVersionSetArtifacts(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken);
file delegate ValueTask WriteVersionSetInformationFile(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken);
file sealed class ExtractVersionSetsHandler(ListVersionSets list, ShouldExtractVersionSet shouldExtract, WriteVersionSetArtifacts writeArtifacts)
internal static class VersionSetModule
{
public async ValueTask Handle(CancellationToken cancellationToken) =>
await list(cancellationToken)
.Where(versionset => shouldExtract(versionset.Name))
.IterParallel(async versionset => await writeArtifacts(versionset.Name, versionset.Dto, cancellationToken),
cancellationToken);
}
file sealed class ListVersionSetsHandler(ManagementServiceUri serviceUri, HttpPipeline pipeline)
{
public IAsyncEnumerable<(VersionSetName, VersionSetDto)> Handle(CancellationToken cancellationToken) =>
VersionSetsUri.From(serviceUri).List(pipeline, cancellationToken);
}
file sealed class ShouldExtractVersionSetHandler(ShouldExtractFactory shouldExtractFactory)
{
public bool Handle(VersionSetName name)
public static void ConfigureExtractVersionSets(IHostApplicationBuilder builder)
{
ConfigureListVersionSets(builder);
ConfigureShouldExtractVersionSet(builder);
ConfigureWriteVersionSetArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractVersionSets);
}
private static ExtractVersionSets GetExtractVersionSets(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListVersionSets>();
var shouldExtract = provider.GetRequiredService<ShouldExtractVersionSet>();
var writeArtifacts = provider.GetRequiredService<WriteVersionSetArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractVersionSets));
logger.LogInformation("Extracting version sets...");
await list(cancellationToken)
.Where(versionset => shouldExtract(versionset.Name))
.IterParallel(async versionset => await writeArtifacts(versionset.Name, versionset.Dto, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListVersionSets(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListVersionSets);
}
private static ListVersionSets GetListVersionSets(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
VersionSetsUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractVersionSet(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractVersionSet);
}
private static ShouldExtractVersionSet GetShouldExtractVersionSet(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<VersionSetName>();
return shouldExtract(name);
}
}
file sealed class WriteVersionSetArtifactsHandler(WriteVersionSetInformationFile writeInformationFile)
{
public async ValueTask Handle(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken)
return name => shouldExtract(name);
}
private static void ConfigureWriteVersionSetArtifacts(IHostApplicationBuilder builder)
{
await writeInformationFile(name, dto, cancellationToken);
ConfigureWriteVersionSetInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteVersionSetArtifacts);
}
}
file sealed class WriteVersionSetInformationFileHandler(ILoggerFactory loggerFactory, ManagementServiceDirectory serviceDirectory)
{
private readonly ILogger logger = Common.GetLogger(loggerFactory);
public async ValueTask Handle(VersionSetName name, VersionSetDto dto, CancellationToken cancellationToken)
private static WriteVersionSetArtifacts GetWriteVersionSetArtifacts(IServiceProvider provider)
{
var informationFile = VersionSetInformationFile.From(name, serviceDirectory);
var writeInformationFile = provider.GetRequiredService<WriteVersionSetInformationFile>();
logger.LogInformation("Writing version set information file {VersionSetInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
}
internal static class VersionSetServices
{
public static void ConfigureExtractVersionSets(IServiceCollection services)
private static void ConfigureWriteVersionSetInformationFile(IHostApplicationBuilder builder)
{
ConfigureListVersionSets(services);
ConfigureShouldExtractVersionSet(services);
ConfigureWriteVersionSetArtifacts(services);
AzureModule.ConfigureManagementServiceDirectory(builder);
services.TryAddSingleton<ExtractVersionSetsHandler>();
services.TryAddSingleton<ExtractVersionSets>(provider => provider.GetRequiredService<ExtractVersionSetsHandler>().Handle);
builder.Services.TryAddSingleton(GetWriteVersionSetInformationFile);
}
private static void ConfigureListVersionSets(IServiceCollection services)
private static WriteVersionSetInformationFile GetWriteVersionSetInformationFile(IServiceProvider provider)
{
services.TryAddSingleton<ListVersionSetsHandler>();
services.TryAddSingleton<ListVersionSets>(provider => provider.GetRequiredService<ListVersionSetsHandler>().Handle);
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = VersionSetInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing version set information file {VersionSetInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
public static void ConfigureShouldExtractVersionSet(IServiceCollection services)
{
services.TryAddSingleton<ShouldExtractVersionSetHandler>();
services.TryAddSingleton<ShouldExtractVersionSet>(provider => provider.GetRequiredService<ShouldExtractVersionSetHandler>().Handle);
}
private static void ConfigureWriteVersionSetArtifacts(IServiceCollection services)
{
ConfigureWriteVersionSetInformationFile(services);
services.TryAddSingleton<WriteVersionSetArtifactsHandler>();
services.TryAddSingleton<WriteVersionSetArtifacts>(provider => provider.GetRequiredService<WriteVersionSetArtifactsHandler>().Handle);
}
private static void ConfigureWriteVersionSetInformationFile(IServiceCollection services)
{
services.TryAddSingleton<WriteVersionSetInformationFileHandler>();
services.TryAddSingleton<WriteVersionSetInformationFile>(provider => provider.GetRequiredService<WriteVersionSetInformationFileHandler>().Handle);
}
}
file static class Common
{
public static ILogger GetLogger(ILoggerFactory loggerFactory) =>
loggerFactory.CreateLogger("VersionSetExtractor");
}

Просмотреть файл

@ -0,0 +1,165 @@
using Azure.Core.Pipeline;
using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaces(CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(WorkspaceName Name, WorkspaceDto Dto)> ListWorkspaces(CancellationToken cancellationToken);
public delegate bool ShouldExtractWorkspace(WorkspaceName name);
public delegate ValueTask WriteWorkspaceArtifacts(WorkspaceName name, WorkspaceDto dto, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceInformationFile(WorkspaceName name, WorkspaceDto dto, CancellationToken cancellationToken);
internal static class WorkspaceModule
{
public static void ConfigureExtractWorkspaces(IHostApplicationBuilder builder)
{
ConfigureListWorkspaces(builder);
ConfigureShouldExtractWorkspace(builder);
ConfigureWriteWorkspaceArtifacts(builder);
WorkspaceNamedValueModule.ConfigureExtractWorkspaceNamedValues(builder);
WorkspaceBackendModule.ConfigureExtractWorkspaceBackends(builder);
WorkspaceTagModule.ConfigureExtractWorkspaceTags(builder);
WorkspaceVersionSetModule.ConfigureExtractWorkspaceVersionSets(builder);
WorkspaceLoggerModule.ConfigureExtractWorkspaceLoggers(builder);
WorkspaceDiagnosticModule.ConfigureExtractWorkspaceDiagnostics(builder);
WorkspacePolicyFragmentModule.ConfigureExtractWorkspacePolicyFragments(builder);
WorkspacePolicyModule.ConfigureExtractWorkspacePolicies(builder);
WorkspaceProductModule.ConfigureExtractWorkspaceProducts(builder);
WorkspaceGroupModule.ConfigureExtractWorkspaceGroups(builder);
WorkspaceApiModule.ConfigureExtractWorkspaceApis(builder);
WorkspaceSubscriptionModule.ConfigureExtractWorkspaceSubscriptions(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaces);
}
private static ExtractWorkspaces GetExtractWorkspaces(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaces>();
var shouldExtract = provider.GetRequiredService<ShouldExtractWorkspace>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceArtifacts>();
var extractWorkspaceNamedValues = provider.GetRequiredService<ExtractWorkspaceNamedValues>();
var extractWorkspaceBackends = provider.GetRequiredService<ExtractWorkspaceBackends>();
var extractWorkspaceTags = provider.GetRequiredService<ExtractWorkspaceTags>();
var extractWorkspaceVersionSets = provider.GetRequiredService<ExtractWorkspaceVersionSets>();
var extractWorkspaceLoggers = provider.GetRequiredService<ExtractWorkspaceLoggers>();
var extractWorkspaceDiagnostics = provider.GetRequiredService<ExtractWorkspaceDiagnostics>();
var extractWorkspacePolicyFragments = provider.GetRequiredService<ExtractWorkspacePolicyFragments>();
var extractWorkspacePolicies = provider.GetRequiredService<ExtractWorkspacePolicies>();
var extractWorkspaceProducts = provider.GetRequiredService<ExtractWorkspaceProducts>();
var extractWorkspaceGroups = provider.GetRequiredService<ExtractWorkspaceGroups>();
var extractWorkspaceApis = provider.GetRequiredService<ExtractWorkspaceApis>();
var extractWorkspaceSubscriptions = provider.GetRequiredService<ExtractWorkspaceSubscriptions>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaces));
logger.LogInformation("Extracting workspaces...");
await list(cancellationToken)
.Where(workspace => shouldExtract(workspace.Name))
.IterParallel(async workspace => await extractWorkspace(workspace.Name, workspace.Dto, cancellationToken),
cancellationToken);
};
async ValueTask extractWorkspace(WorkspaceName name, WorkspaceDto dto, CancellationToken cancellationToken)
{
//await writeArtifacts(name, dto, cancellationToken); // TODO: Revisit support for writing workspace artifacts
await extractWorkspaceNamedValues(name, cancellationToken);
await extractWorkspaceBackends(name, cancellationToken);
await extractWorkspaceTags(name, cancellationToken);
await extractWorkspaceVersionSets(name, cancellationToken);
await extractWorkspaceLoggers(name, cancellationToken);
await extractWorkspaceDiagnostics(name, cancellationToken);
await extractWorkspacePolicyFragments(name, cancellationToken);
await extractWorkspacePolicies(name, cancellationToken);
await extractWorkspaceProducts(name, cancellationToken);
await extractWorkspaceGroups(name, cancellationToken);
await extractWorkspaceApis(name, cancellationToken);
await extractWorkspaceSubscriptions(name, cancellationToken);
}
}
private static void ConfigureListWorkspaces(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaces);
}
private static ListWorkspaces GetListWorkspaces(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return cancellationToken =>
WorkspacesUri.From(serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureShouldExtractWorkspace(IHostApplicationBuilder builder)
{
ShouldExtractModule.ConfigureShouldExtractFactory(builder);
builder.Services.TryAddSingleton(GetShouldExtractWorkspace);
}
private static ShouldExtractWorkspace GetShouldExtractWorkspace(IServiceProvider provider)
{
var shouldExtractFactory = provider.GetRequiredService<ShouldExtractFactory>();
var shouldExtract = shouldExtractFactory.Create<WorkspaceName>();
return name => shouldExtract(name);
}
private static void ConfigureWriteWorkspaceArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceArtifacts);
}
private static WriteWorkspaceArtifacts GetWriteWorkspaceArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceInformationFile>();
return async (name, dto, cancellationToken) =>
await writeInformationFile(name, dto, cancellationToken);
}
private static void ConfigureWriteWorkspaceInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceInformationFile);
}
private static WriteWorkspaceInformationFile GetWriteWorkspaceInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, cancellationToken) =>
{
var informationFile = WorkspaceInformationFile.From(name, serviceDirectory);
logger.LogInformation("Writing workspace information file {WorkspaceInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,180 @@
using Azure.Core.Pipeline;
using common;
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceApis(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ApiName Name, WorkspaceApiDto Dto, Option<(ApiSpecification Specification, BinaryData Contents)> SpecificationOption)> ListWorkspaceApis(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceApiArtifacts(ApiName name, WorkspaceApiDto dto, Option<(ApiSpecification Specification, BinaryData Contents)> specificationOption, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceApiInformationFile(ApiName name, WorkspaceApiDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceApiSpecificationFile(ApiName name, ApiSpecification specification, BinaryData contents, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceApiModule
{
public static void ConfigureExtractWorkspaceApis(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceApis(builder);
ConfigureWriteWorkspaceApiArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceApis);
}
private static ExtractWorkspaceApis GetExtractWorkspaceApis(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceApis>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceApiArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceApis));
logger.LogInformation("Extracting APIs for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
// Group APIs by version set (https://github.com/Azure/apiops/issues/316).
// We'll process each group in parallel, but each API within a group sequentially.
.GroupBy(api => api.Dto.Properties.ApiVersionSetId ?? string.Empty)
.IterParallel(async group => await group.Iter(async api => await extractApi(api.Name, api.Dto, api.SpecificationOption, workspaceName, cancellationToken),
cancellationToken),
cancellationToken);
};
async ValueTask extractApi(ApiName name, WorkspaceApiDto dto, Option<(ApiSpecification Specification, BinaryData Contents)> specificationOption, WorkspaceName workspaceName, CancellationToken cancellationToken)
{
await writeArtifacts(name, dto, specificationOption, workspaceName, cancellationToken);
}
}
private static void ConfigureListWorkspaceApis(IHostApplicationBuilder builder)
{
ApiSpecificationModule.ConfigureDefaultApiSpecification(builder);
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceApis);
}
private static ListWorkspaceApis GetListWorkspaceApis(IServiceProvider provider)
{
var defaultApiSpecification = provider.GetRequiredService<DefaultApiSpecification>();
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
{
var workspaceApisUri = WorkspaceApisUri.From(workspaceName, serviceUri);
return workspaceApisUri.List(pipeline, cancellationToken)
.SelectAwait(async api =>
{
var (name, dto) = api;
var specificationContentsOption = await tryGetSpecificationContents(name, dto, workspaceName, cancellationToken);
return (name, dto, specificationContentsOption);
});
};
async ValueTask<Option<(ApiSpecification, BinaryData)>> tryGetSpecificationContents(ApiName name, WorkspaceApiDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken)
{
var specificationOption = tryGetSpecification(dto);
return await specificationOption.BindTask(async specification =>
{
var uri = WorkspaceApiUri.From(name, workspaceName, serviceUri);
var contentsOption = await uri.TryGetSpecificationContents(specification, pipeline, cancellationToken);
return from contents in contentsOption
select (specification, contents);
});
}
Option<ApiSpecification> tryGetSpecification(WorkspaceApiDto dto) =>
(dto.Properties.Type ?? dto.Properties.ApiType) switch
{
"graphql" => new ApiSpecification.GraphQl(),
"soap" => new ApiSpecification.Wsdl(),
"http" => defaultApiSpecification.Value,
null => defaultApiSpecification.Value,
_ => Option<ApiSpecification>.None
};
}
private static void ConfigureWriteWorkspaceApiArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceApiInformationFile(builder);
ConfigureWriteWorkspaceApiSpecificationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceApiArtifacts);
}
private static WriteWorkspaceApiArtifacts GetWriteWorkspaceApiArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceApiInformationFile>();
var writeSpecificationFile = provider.GetRequiredService<WriteWorkspaceApiSpecificationFile>();
return async (name, dto, specificationContentsOption, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
await specificationContentsOption.IterTask(async x =>
{
var (specification, contents) = x;
await writeSpecificationFile(name, specification, contents, workspaceName, cancellationToken);
});
};
}
private static void ConfigureWriteWorkspaceApiInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceApiInformationFile);
}
private static WriteWorkspaceApiInformationFile GetWriteWorkspaceApiInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceApiInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace API information file {WorkspaceApiInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceApiSpecificationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceApiSpecificationFile);
}
private static WriteWorkspaceApiSpecificationFile GetWriteWorkspaceApiSpecificationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, specification, contents, workspaceName, cancellationToken) =>
{
var specificationFile = WorkspaceApiSpecificationFile.From(specification, name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace API specification file {WorkspaceApiSpecificationFile}...", specificationFile);
await specificationFile.WriteSpecification(contents, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceBackends(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(BackendName Name, WorkspaceBackendDto Dto)> ListWorkspaceBackends(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceBackendArtifacts(BackendName name, WorkspaceBackendDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceBackendInformationFile(BackendName name, WorkspaceBackendDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceBackendModule
{
public static void ConfigureExtractWorkspaceBackends(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceBackends(builder);
ConfigureWriteWorkspaceBackendArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceBackends);
}
private static ExtractWorkspaceBackends GetExtractWorkspaceBackends(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceBackends>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceBackendArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceBackends));
logger.LogInformation("Extracting backends for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async resource => await writeArtifacts(resource.Name, resource.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceBackends(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceBackends);
}
private static ListWorkspaceBackends GetListWorkspaceBackends(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
{
var workspaceBackendsUri = WorkspaceBackendsUri.From(workspaceName, serviceUri);
return workspaceBackendsUri.List(pipeline, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceBackendArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceBackendInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceBackendArtifacts);
}
private static WriteWorkspaceBackendArtifacts GetWriteWorkspaceBackendArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceBackendInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
await writeInformationFile(name, dto, workspaceName, cancellationToken);
}
private static void ConfigureWriteWorkspaceBackendInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceBackendInformationFile);
}
private static WriteWorkspaceBackendInformationFile GetWriteWorkspaceBackendInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceBackendInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace backend information file {WorkspaceBackendInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceDiagnostics(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(DiagnosticName Name, WorkspaceDiagnosticDto Dto)> ListWorkspaceDiagnostics(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceDiagnosticArtifacts(DiagnosticName name, WorkspaceDiagnosticDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceDiagnosticInformationFile(DiagnosticName name, WorkspaceDiagnosticDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceDiagnosticModule
{
public static void ConfigureExtractWorkspaceDiagnostics(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceDiagnostics(builder);
ConfigureWriteWorkspaceDiagnosticArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceDiagnostics);
}
private static ExtractWorkspaceDiagnostics GetExtractWorkspaceDiagnostics(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceDiagnostics>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceDiagnosticArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceDiagnostics));
logger.LogInformation("Extracting diagnostics for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async diagnostic => await writeArtifacts(diagnostic.Name, diagnostic.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceDiagnostics(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceDiagnostics);
}
private static ListWorkspaceDiagnostics GetListWorkspaceDiagnostics(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceDiagnosticsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceDiagnosticArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceDiagnosticInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceDiagnosticArtifacts);
}
private static WriteWorkspaceDiagnosticArtifacts GetWriteWorkspaceDiagnosticArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceDiagnosticInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceDiagnosticInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceDiagnosticInformationFile);
}
private static WriteWorkspaceDiagnosticInformationFile GetWriteWorkspaceDiagnosticInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceDiagnosticInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace diagnostic information file {WorkspaceDiagnosticInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceGroups(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(GroupName Name, WorkspaceGroupDto Dto)> ListWorkspaceGroups(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceGroupArtifacts(GroupName name, WorkspaceGroupDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceGroupInformationFile(GroupName name, WorkspaceGroupDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceGroupModule
{
public static void ConfigureExtractWorkspaceGroups(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceGroups(builder);
ConfigureWriteWorkspaceGroupArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceGroups);
}
private static ExtractWorkspaceGroups GetExtractWorkspaceGroups(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceGroups>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceGroupArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceGroups));
logger.LogInformation("Extracting groups for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async group => await writeArtifacts(group.Name, group.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceGroups(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceGroups);
}
private static ListWorkspaceGroups GetListWorkspaceGroups(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceGroupsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceGroupArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceGroupInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceGroupArtifacts);
}
private static WriteWorkspaceGroupArtifacts GetWriteWorkspaceGroupArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceGroupInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceGroupInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceGroupInformationFile);
}
private static WriteWorkspaceGroupInformationFile GetWriteWorkspaceGroupInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceGroupInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace group information file {WorkspaceGroupInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceLoggers(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(LoggerName Name, WorkspaceLoggerDto Dto)> ListWorkspaceLoggers(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceLoggerArtifacts(LoggerName name, WorkspaceLoggerDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceLoggerInformationFile(LoggerName name, WorkspaceLoggerDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceLoggerModule
{
public static void ConfigureExtractWorkspaceLoggers(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceLoggers(builder);
ConfigureWriteWorkspaceLoggerArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceLoggers);
}
private static ExtractWorkspaceLoggers GetExtractWorkspaceLoggers(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceLoggers>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceLoggerArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceLoggers));
logger.LogInformation("Extracting loggers for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async logger => await writeArtifacts(logger.Name, logger.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceLoggers(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceLoggers);
}
private static ListWorkspaceLoggers GetListWorkspaceLoggers(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceLoggersUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceLoggerArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceLoggerInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceLoggerArtifacts);
}
private static WriteWorkspaceLoggerArtifacts GetWriteWorkspaceLoggerArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceLoggerInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceLoggerInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceLoggerInformationFile);
}
private static WriteWorkspaceLoggerInformationFile GetWriteWorkspaceLoggerInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceLoggerInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace logger information file {WorkspaceLoggerInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceNamedValues(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(NamedValueName Name, WorkspaceNamedValueDto Dto)> ListWorkspaceNamedValues(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceNamedValueArtifacts(NamedValueName name, WorkspaceNamedValueDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceNamedValueInformationFile(NamedValueName name, WorkspaceNamedValueDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceNamedValueModule
{
public static void ConfigureExtractWorkspaceNamedValues(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceNamedValues(builder);
ConfigureWriteWorkspaceNamedValueArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceNamedValues);
}
private static ExtractWorkspaceNamedValues GetExtractWorkspaceNamedValues(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceNamedValues>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceNamedValueArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceNamedValues));
logger.LogInformation("Extracting named values for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async namedValue => await writeArtifacts(namedValue.Name, namedValue.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceNamedValues(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceNamedValues);
}
private static ListWorkspaceNamedValues GetListWorkspaceNamedValues(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceNamedValuesUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceNamedValueArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceNamedValueInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceNamedValueArtifacts);
}
private static WriteWorkspaceNamedValueArtifacts GetWriteWorkspaceNamedValueArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceNamedValueInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceNamedValueInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceNamedValueInformationFile);
}
private static WriteWorkspaceNamedValueInformationFile GetWriteWorkspaceNamedValueInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceNamedValueInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace named value information file {WorkspaceNamedValueInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,103 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspacePolicies(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(WorkspacePolicyName Name, WorkspacePolicyDto Dto)> ListWorkspacePolicies(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspacePolicyArtifacts(WorkspacePolicyName name, WorkspacePolicyDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspacePolicyFile(WorkspacePolicyName name, WorkspacePolicyDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspacePolicyModule
{
public static void ConfigureExtractWorkspacePolicies(IHostApplicationBuilder builder)
{
ConfigureListWorkspacePolicies(builder);
ConfigureWriteWorkspacePolicyArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspacePolicies);
}
private static ExtractWorkspacePolicies GetExtractWorkspacePolicies(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspacePolicies>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspacePolicyArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspacePolicies));
logger.LogInformation("Extracting policies for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async policy => await writeArtifacts(policy.Name, policy.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspacePolicies(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspacePolicies);
}
private static ListWorkspacePolicies GetListWorkspacePolicies(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspacePoliciesUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspacePolicyArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspacePolicyFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspacePolicyArtifacts);
}
private static WriteWorkspacePolicyArtifacts GetWriteWorkspacePolicyArtifacts(IServiceProvider provider)
{
var writePolicyFile = provider.GetRequiredService<WriteWorkspacePolicyFile>();
return async (name, dto, workspaceName, cancellationToken) =>
await writePolicyFile(name, dto, workspaceName, cancellationToken);
}
private static void ConfigureWriteWorkspacePolicyFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspacePolicyFile);
}
private static WriteWorkspacePolicyFile GetWriteWorkspacePolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var policyFile = WorkspacePolicyFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace policy file {PolicyFile}", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,134 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspacePolicyFragments(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(PolicyFragmentName Name, WorkspacePolicyFragmentDto Dto)> ListWorkspacePolicyFragments(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspacePolicyFragmentArtifacts(PolicyFragmentName name, WorkspacePolicyFragmentDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspacePolicyFragmentInformationFile(PolicyFragmentName name, WorkspacePolicyFragmentDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspacePolicyFragmentPolicyFile(PolicyFragmentName name, WorkspacePolicyFragmentDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspacePolicyFragmentModule
{
public static void ConfigureExtractWorkspacePolicyFragments(IHostApplicationBuilder builder)
{
ConfigureListWorkspacePolicyFragments(builder);
ConfigureWriteWorkspacePolicyFragmentArtifacts(builder);
ConfigureWriteWorkspacePolicyFragmentArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspacePolicyFragments);
}
private static ExtractWorkspacePolicyFragments GetExtractWorkspacePolicyFragments(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspacePolicyFragments>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspacePolicyFragmentArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspacePolicyFragments));
logger.LogInformation("Extracting policy fragments for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async policyFragment => await writeArtifacts(policyFragment.Name, policyFragment.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspacePolicyFragments(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspacePolicyFragments);
}
private static ListWorkspacePolicyFragments GetListWorkspacePolicyFragments(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspacePolicyFragmentsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspacePolicyFragmentArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspacePolicyFragmentInformationFile(builder);
ConfigureWriteWorkspacePolicyFragmentPolicyFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspacePolicyFragmentArtifacts);
}
private static WriteWorkspacePolicyFragmentArtifacts GetWriteWorkspacePolicyFragmentArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspacePolicyFragmentInformationFile>();
var writePolicyFile = provider.GetRequiredService<WriteWorkspacePolicyFragmentPolicyFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
await writePolicyFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspacePolicyFragmentInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspacePolicyFragmentInformationFile);
}
private static WriteWorkspacePolicyFragmentInformationFile GetWriteWorkspacePolicyFragmentInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspacePolicyFragmentInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace policy fragment information file {WorkspacePolicyFragmentInformationFile}...", informationFile);
// Remove policy contents from DTO, as these will be written to the policy file
var updatedDto = dto with { Properties = dto.Properties with { Format = null, Value = null } };
await informationFile.WriteDto(updatedDto, cancellationToken);
};
}
private static void ConfigureWriteWorkspacePolicyFragmentPolicyFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspacePolicyFragmentPolicyFile);
}
private static WriteWorkspacePolicyFragmentPolicyFile GetWriteWorkspacePolicyFragmentPolicyFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var policyFile = WorkspacePolicyFragmentPolicyFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace policy fragment policy file {WorkspacePolicyFragmentPolicyFile}...", policyFile);
var policy = dto.Properties.Value ?? string.Empty;
await policyFile.WritePolicy(policy, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceProducts(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(ProductName Name, WorkspaceProductDto Dto)> ListWorkspaceProducts(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceProductArtifacts(ProductName name, WorkspaceProductDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceProductInformationFile(ProductName name, WorkspaceProductDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceProductModule
{
public static void ConfigureExtractWorkspaceProducts(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceProducts(builder);
ConfigureWriteWorkspaceProductArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceProducts);
}
private static ExtractWorkspaceProducts GetExtractWorkspaceProducts(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceProducts>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceProductArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceProducts));
logger.LogInformation("Extracting products for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async product => await writeArtifacts(product.Name, product.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceProducts(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceProducts);
}
private static ListWorkspaceProducts GetListWorkspaceProducts(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceProductsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceProductArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceProductInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceProductArtifacts);
}
private static WriteWorkspaceProductArtifacts GetWriteWorkspaceProductArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceProductInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceProductInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceProductInformationFile);
}
private static WriteWorkspaceProductInformationFile GetWriteWorkspaceProductInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceProductInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace product information file {WorkspaceProductInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceSubscriptions(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(SubscriptionName Name, WorkspaceSubscriptionDto Dto)> ListWorkspaceSubscriptions(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceSubscriptionArtifacts(SubscriptionName name, WorkspaceSubscriptionDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceSubscriptionInformationFile(SubscriptionName name, WorkspaceSubscriptionDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceSubscriptionModule
{
public static void ConfigureExtractWorkspaceSubscriptions(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceSubscriptions(builder);
ConfigureWriteWorkspaceSubscriptionArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceSubscriptions);
}
private static ExtractWorkspaceSubscriptions GetExtractWorkspaceSubscriptions(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceSubscriptions>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceSubscriptionArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceSubscriptions));
logger.LogInformation("Extracting subscriptions for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async subscription => await writeArtifacts(subscription.Name, subscription.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceSubscriptions(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceSubscriptions);
}
private static ListWorkspaceSubscriptions GetListWorkspaceSubscriptions(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceSubscriptionsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceSubscriptionArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceSubscriptionInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceSubscriptionArtifacts);
}
private static WriteWorkspaceSubscriptionArtifacts GetWriteWorkspaceSubscriptionArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceSubscriptionInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceSubscriptionInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceSubscriptionInformationFile);
}
private static WriteWorkspaceSubscriptionInformationFile GetWriteWorkspaceSubscriptionInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceSubscriptionInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace subscription information file {WorkspaceSubscriptionInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceTags(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(TagName Name, WorkspaceTagDto Dto)> ListWorkspaceTags(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceTagArtifacts(TagName name, WorkspaceTagDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceTagInformationFile(TagName name, WorkspaceTagDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceTagModule
{
public static void ConfigureExtractWorkspaceTags(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceTags(builder);
ConfigureWriteWorkspaceTagArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceTags);
}
private static ExtractWorkspaceTags GetExtractWorkspaceTags(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceTags>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceTagArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceTags));
logger.LogInformation("Extracting tags for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async tag => await writeArtifacts(tag.Name, tag.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceTags(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceTags);
}
private static ListWorkspaceTags GetListWorkspaceTags(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceTagsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceTagArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceTagInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceTagArtifacts);
}
private static WriteWorkspaceTagArtifacts GetWriteWorkspaceTagArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceTagInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceTagInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceTagInformationFile);
}
private static WriteWorkspaceTagInformationFile GetWriteWorkspaceTagInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceTagInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace tag information file {WorkspaceTagInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -0,0 +1,104 @@
using Azure.Core.Pipeline;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace extractor;
public delegate ValueTask ExtractWorkspaceVersionSets(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate IAsyncEnumerable<(VersionSetName Name, WorkspaceVersionSetDto Dto)> ListWorkspaceVersionSets(WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceVersionSetArtifacts(VersionSetName name, WorkspaceVersionSetDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
public delegate ValueTask WriteWorkspaceVersionSetInformationFile(VersionSetName name, WorkspaceVersionSetDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken);
internal static class WorkspaceVersionSetModule
{
public static void ConfigureExtractWorkspaceVersionSets(IHostApplicationBuilder builder)
{
ConfigureListWorkspaceVersionSets(builder);
ConfigureWriteWorkspaceVersionSetArtifacts(builder);
builder.Services.TryAddSingleton(GetExtractWorkspaceVersionSets);
}
private static ExtractWorkspaceVersionSets GetExtractWorkspaceVersionSets(IServiceProvider provider)
{
var list = provider.GetRequiredService<ListWorkspaceVersionSets>();
var writeArtifacts = provider.GetRequiredService<WriteWorkspaceVersionSetArtifacts>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (workspaceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ExtractWorkspaceVersionSets));
logger.LogInformation("Extracting version sets for workspace {WorkspaceName}...", workspaceName);
await list(workspaceName, cancellationToken)
.IterParallel(async versionSet => await writeArtifacts(versionSet.Name, versionSet.Dto, workspaceName, cancellationToken),
cancellationToken);
};
}
private static void ConfigureListWorkspaceVersionSets(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetListWorkspaceVersionSets);
}
private static ListWorkspaceVersionSets GetListWorkspaceVersionSets(IServiceProvider provider)
{
var serviceUri = provider.GetRequiredService<ManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
return (workspaceName, cancellationToken) =>
WorkspaceVersionSetsUri.From(workspaceName, serviceUri)
.List(pipeline, cancellationToken);
}
private static void ConfigureWriteWorkspaceVersionSetArtifacts(IHostApplicationBuilder builder)
{
ConfigureWriteWorkspaceVersionSetInformationFile(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceVersionSetArtifacts);
}
private static WriteWorkspaceVersionSetArtifacts GetWriteWorkspaceVersionSetArtifacts(IServiceProvider provider)
{
var writeInformationFile = provider.GetRequiredService<WriteWorkspaceVersionSetInformationFile>();
return async (name, dto, workspaceName, cancellationToken) =>
{
await writeInformationFile(name, dto, workspaceName, cancellationToken);
};
}
private static void ConfigureWriteWorkspaceVersionSetInformationFile(IHostApplicationBuilder builder)
{
AzureModule.ConfigureManagementServiceDirectory(builder);
builder.Services.TryAddSingleton(GetWriteWorkspaceVersionSetInformationFile);
}
private static WriteWorkspaceVersionSetInformationFile GetWriteWorkspaceVersionSetInformationFile(IServiceProvider provider)
{
var serviceDirectory = provider.GetRequiredService<ManagementServiceDirectory>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, dto, workspaceName, cancellationToken) =>
{
var informationFile = WorkspaceVersionSetInformationFile.From(name, workspaceName, serviceDirectory);
logger.LogInformation("Writing workspace version set information file {WorkspaceVersionSetInformationFile}...", informationFile);
await informationFile.WriteDto(dto, cancellationToken);
};
}
}

Просмотреть файл

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>8-all</AnalysisLevel>
<WarningsNotAsErrors>CA1708,CA1724,CA1812,CA1848,CA2007,CA1034,CA1062</WarningsNotAsErrors>
@ -13,8 +12,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<PackageReference Include="Microsoft.FeatureManagement" Version="3.5.0" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -8,12 +8,12 @@ using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using publisher;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@ -21,496 +21,526 @@ using System.Threading.Tasks;
namespace integration.tests;
internal delegate ValueTask DeleteAllApis(ManagementServiceName serviceName, CancellationToken cancellationToken);
public delegate ValueTask DeleteAllApis(ManagementServiceName serviceName, CancellationToken cancellationToken);
public delegate ValueTask PutApiModels(IEnumerable<ApiModel> models, ManagementServiceName serviceName, CancellationToken cancellationToken);
public delegate ValueTask ValidateExtractedApis(Option<FrozenSet<ApiName>> apiNamesOption, Option<ApiSpecification> defaultApiSpecification, Option<FrozenSet<VersionSetName>> versionSetNamesOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
public delegate ValueTask<FrozenDictionary<ApiName, ApiDto>> GetApimApis(ManagementServiceName serviceName, CancellationToken cancellationToken);
public delegate ValueTask<Option<BinaryData>> TryGetApimGraphQlSchema(ApiName name, ManagementServiceName serviceName, CancellationToken cancellationToken);
public delegate ValueTask<FrozenDictionary<ApiName, ApiDto>> GetFileApis(ManagementServiceDirectory serviceDirectory, Option<CommitId> commitIdOption, CancellationToken cancellationToken);
public delegate ValueTask WriteApiModels(IEnumerable<ApiModel> models, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
public delegate ValueTask ValidatePublishedApis(IDictionary<ApiName, ApiDto> overrides, Option<CommitId> commitIdOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
internal delegate ValueTask PutApiModels(IEnumerable<ApiModel> models, ManagementServiceName serviceName, CancellationToken cancellationToken);
internal delegate ValueTask ValidateExtractedApis(Option<FrozenSet<ApiName>> apiNamesOption, Option<ApiSpecification> defaultApiSpecification, Option<FrozenSet<VersionSetName>> versionSetNamesOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
file delegate ValueTask<FrozenDictionary<ApiName, ApiDto>> GetApimApis(ManagementServiceName serviceName, CancellationToken cancellationToken);
file delegate ValueTask<Option<BinaryData>> TryGetApimGraphQlSchema(ApiName name, ManagementServiceName serviceName, CancellationToken cancellationToken);
file delegate ValueTask<FrozenDictionary<ApiName, ApiDto>> GetFileApis(ManagementServiceDirectory serviceDirectory, Option<CommitId> commitIdOption, CancellationToken cancellationToken);
internal delegate ValueTask WriteApiModels(IEnumerable<ApiModel> models, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
internal delegate ValueTask ValidatePublishedApis(IDictionary<ApiName, ApiDto> overrides, Option<CommitId> commitIdOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken);
file sealed class DeleteAllApisHandler(ILogger<DeleteAllApis> logger, GetManagementServiceUri getServiceUri, HttpPipeline pipeline, ActivitySource activitySource)
public static class ApiModule
{
public async ValueTask Handle(ManagementServiceName serviceName, CancellationToken cancellationToken)
public static void ConfigureDeleteAllApis(IHostApplicationBuilder builder)
{
using var _ = activitySource.StartActivity(nameof(DeleteAllApis));
ManagementServiceModule.ConfigureGetManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
logger.LogInformation("Deleting all APIs in {ServiceName}...", serviceName);
var serviceUri = getServiceUri(serviceName);
await ApisUri.From(serviceUri).DeleteAll(pipeline, cancellationToken);
builder.Services.TryAddSingleton(GetDeleteAllApis);
}
}
file sealed class PutApiModelsHandler(ILogger<PutApiModels> logger, GetManagementServiceUri getServiceUri, HttpPipeline pipeline, ActivitySource activitySource)
{
public async ValueTask Handle(IEnumerable<ApiModel> models, ManagementServiceName serviceName, CancellationToken cancellationToken)
private static DeleteAllApis GetDeleteAllApis(IServiceProvider provider)
{
using var _ = activitySource.StartActivity(nameof(PutApiModels));
var getServiceUri = provider.GetRequiredService<GetManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
logger.LogInformation("Putting API models in {ServiceName}...", serviceName);
await models.IterParallel(async model =>
return async (serviceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(DeleteAllApis));
logger.LogInformation("Deleting all APIs in {ServiceName}...", serviceName);
var serviceUri = getServiceUri(serviceName);
async ValueTask putRevision(ApiRevision revision) => await Put(model.Name, model.Type, model.Path, model.Version, revision, serviceUri, cancellationToken);
// Put first revision to make sure it's the current revision.
await model.Revisions.HeadOrNone().IterTask(putRevision);
// Put other revisions
await model.Revisions.Skip(1).IterParallel(putRevision, cancellationToken);
}, cancellationToken);
}
private async ValueTask Put(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision, ManagementServiceUri serviceUri, CancellationToken cancellationToken)
{
var rootName = ApiName.GetRootName(name);
var dto = GetDto(rootName, type, path, version, revision);
var revisionedName = ApiName.GetRevisionedName(rootName, revision.Number);
var uri = ApiUri.From(revisionedName, serviceUri);
await uri.PutDto(dto, pipeline, cancellationToken);
if (type is ApiType.GraphQl)
{
await revision.Specification.IterTask(async specification => await uri.PutGraphQlSchema(specification, pipeline, cancellationToken));
}
await ApiPolicy.Put(revision.Policies, revisionedName, serviceUri, pipeline, cancellationToken);
await ApiTag.Put(revision.Tags, revisionedName, serviceUri, pipeline, cancellationToken);
}
private static ApiDto GetDto(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision) =>
new ApiDto()
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
{
// APIM sets the description to null when it imports for SOAP APIs.
DisplayName = name.ToString(),
Path = path,
ApiType = type switch
{
ApiType.Http => null,
ApiType.Soap => "soap",
ApiType.GraphQl => null,
ApiType.WebSocket => null,
_ => throw new NotSupportedException()
},
Type = type switch
{
ApiType.Http => "http",
ApiType.Soap => "soap",
ApiType.GraphQl => "graphql",
ApiType.WebSocket => "websocket",
_ => throw new NotSupportedException()
},
Protocols = type switch
{
ApiType.Http => ["http", "https"],
ApiType.Soap => ["http", "https"],
ApiType.GraphQl => ["http", "https"],
ApiType.WebSocket => ["ws", "wss"],
_ => throw new NotSupportedException()
},
ServiceUrl = revision.ServiceUri.ValueUnsafe()?.ToString(),
ApiRevisionDescription = revision.Description.ValueUnsafe(),
ApiRevision = $"{revision.Number.ToInt()}",
ApiVersion = version.Map(version => version.Version).ValueUnsafe(),
ApiVersionSetId = version.Map(version => $"/apiVersionSets/{version.VersionSetName}").ValueUnsafe()
}
await ApisUri.From(serviceUri)
.DeleteAll(pipeline, cancellationToken);
};
}
file sealed class ValidateExtractedApisHandler(ILogger<ValidateExtractedApis> logger, GetApimApis getApimResources, TryGetApimGraphQlSchema tryGetApimGraphQlSchema, GetFileApis getFileResources, ActivitySource activitySource)
{
public async ValueTask Handle(Option<FrozenSet<ApiName>> apiNamesOption, Option<ApiSpecification> defaultApiSpecification, Option<FrozenSet<VersionSetName>> versionSetNamesOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(ValidateExtractedApis));
logger.LogInformation("Validating extracted APIs in {ServiceName}...", serviceName);
var expected = await GetExpectedResources(apiNamesOption, versionSetNamesOption, serviceName, cancellationToken);
await ValidateExtractedInformationFiles(expected, serviceDirectory, cancellationToken);
await ValidateExtractedSpecificationFiles(expected, defaultApiSpecification, serviceName, serviceDirectory, cancellationToken);
}
private async ValueTask<ImmutableDictionary<ApiName, ApiDto>> GetExpectedResources(Option<FrozenSet<ApiName>> apiNamesOption, Option<FrozenSet<VersionSetName>> versionSetNamesOption, ManagementServiceName serviceName, CancellationToken cancellationToken)
public static void ConfigurePutApiModels(IHostApplicationBuilder builder)
{
var apimResources = await getApimResources(serviceName, cancellationToken);
ManagementServiceModule.ConfigureGetManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
return apimResources.WhereKey(name => ExtractorOptions.ShouldExtract(name, apiNamesOption))
.WhereValue(dto => ApiModule.TryGetVersionSetName(dto)
.Map(name => ExtractorOptions.ShouldExtract(name, versionSetNamesOption))
.IfNone(true));
builder.Services.TryAddSingleton(GetPutApiModels);
}
private async ValueTask ValidateExtractedInformationFiles(IDictionary<ApiName, ApiDto> expectedResources, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
private static PutApiModels GetPutApiModels(IServiceProvider provider)
{
var fileResources = await getFileResources(serviceDirectory, Prelude.None, cancellationToken);
var getServiceUri = provider.GetRequiredService<GetManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
var expected = expectedResources.MapValue(NormalizeDto);
var actual = fileResources.MapValue(NormalizeDto);
actual.Should().BeEquivalentTo(expected);
}
private static string NormalizeDto(ApiDto dto) =>
new
return async (models, serviceName, cancellationToken) =>
{
DisplayName = dto.Properties.DisplayName ?? string.Empty,
Path = dto.Properties.Path ?? string.Empty,
RevisionDescription = dto.Properties.ApiRevisionDescription ?? string.Empty,
Revision = dto.Properties.ApiRevision ?? string.Empty,
ServiceUrl = Uri.TryCreate(dto.Properties.ServiceUrl, UriKind.Absolute, out var uri)
? uri.RemovePath().ToString()
: string.Empty
}.ToString()!;
using var _ = activitySource.StartActivity(nameof(PutApiModels));
private async ValueTask ValidateExtractedSpecificationFiles(IDictionary<ApiName, ApiDto> expectedResources, Option<ApiSpecification> defaultApiSpecification, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var expected = await expectedResources.ToAsyncEnumerable()
.Choose(async kvp =>
{
var name = kvp.Key;
return from specification in await GetExpectedApiSpecification(name, kvp.Value, defaultApiSpecification, serviceName, cancellationToken)
// Skip XML specification files. Sometimes they get extracted, other times they fail.
where specification is not (ApiSpecification.Wsdl or ApiSpecification.Wadl)
select (name, specification);
})
.ToFrozenDictionary(cancellationToken);
logger.LogInformation("Putting API models in {ServiceName}...", serviceName);
var actual = await ApiModule.ListSpecificationFiles(serviceDirectory, cancellationToken)
.Select(file => (file.Parent.Name, file.Specification))
// Skip XML specification files. Sometimes they get extracted, other times they fail.
.Where(file => file.Specification is not (ApiSpecification.Wsdl or ApiSpecification.Wadl))
.ToFrozenDictionary(cancellationToken);
await models.IterParallel(async model =>
{
async ValueTask putRevision(ApiRevision revision) => await put(model.Name, model.Type, model.Path, model.Version, revision, serviceName, cancellationToken);
actual.Should().BeEquivalentTo(expected);
}
// Put first revision to make sure it's the current revision.
await model.Revisions.HeadOrNone().IterTask(putRevision);
private async ValueTask<Option<ApiSpecification>> GetExpectedApiSpecification(ApiName name, ApiDto dto, Option<ApiSpecification> defaultApiSpecification, ManagementServiceName serviceName, CancellationToken cancellationToken)
{
switch (dto.Properties.ApiType ?? dto.Properties.Type)
// Put other revisions
await model.Revisions.Skip(1).IterParallel(putRevision, cancellationToken);
}, cancellationToken);
};
async ValueTask put(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision, ManagementServiceName serviceName, CancellationToken cancellationToken)
{
case "graphql":
var specificationContents = await tryGetApimGraphQlSchema(name, serviceName, cancellationToken);
return specificationContents.Map(contents => new ApiSpecification.GraphQl() as ApiSpecification);
case "soap":
return new ApiSpecification.Wsdl();
case "websocket":
return Option<ApiSpecification>.None;
default:
#pragma warning disable CA1849 // Call async methods when in an async method
return defaultApiSpecification.IfNone(() => new ApiSpecification.OpenApi
var rootName = ApiName.GetRootName(name);
var dto = getDto(rootName, type, path, version, revision);
var revisionedName = ApiName.GetRevisionedName(rootName, revision.Number);
var serviceUri = getServiceUri(serviceName);
var uri = ApiUri.From(revisionedName, serviceUri);
await uri.PutDto(dto, pipeline, cancellationToken);
if (type is ApiType.GraphQl)
{
await revision.Specification.IterTask(async specification => await uri.PutGraphQlSchema(BinaryData.FromString(specification), pipeline, cancellationToken));
}
await ApiPolicyModule.Put(revision.Policies, revisionedName, serviceUri, pipeline, cancellationToken);
await ApiTagModule.Put(revision.Tags, revisionedName, serviceUri, pipeline, cancellationToken);
}
static ApiDto getDto(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision) =>
new ApiDto()
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
});
#pragma warning restore CA1849 // Call async methods when in an async method
// APIM sets the description to null when it imports for SOAP APIs.
DisplayName = name.ToString(),
Path = path,
ApiType = type switch
{
ApiType.Http => null,
ApiType.Soap => "soap",
ApiType.GraphQl => null,
ApiType.WebSocket => null,
_ => throw new NotSupportedException()
},
Type = type switch
{
ApiType.Http => "http",
ApiType.Soap => "soap",
ApiType.GraphQl => "graphql",
ApiType.WebSocket => "websocket",
_ => throw new NotSupportedException()
},
Protocols = type switch
{
ApiType.Http => ["http", "https"],
ApiType.Soap => ["http", "https"],
ApiType.GraphQl => ["http", "https"],
ApiType.WebSocket => ["ws", "wss"],
_ => throw new NotSupportedException()
},
ServiceUrl = revision.ServiceUri.ValueUnsafe()?.ToString(),
ApiRevisionDescription = revision.Description.ValueUnsafe(),
ApiRevision = $"{revision.Number.ToInt()}",
ApiVersion = version.Map(version => version.Version).ValueUnsafe(),
ApiVersionSetId = version.Map(version => $"/apiVersionSets/{version.VersionSetName}").ValueUnsafe()
}
};
}
public static void ConfigureValidateExtractedApis(IHostApplicationBuilder builder)
{
ConfigureGetApimApis(builder);
ConfigureTryGetApimGraphQlSchema(builder);
ConfigureGetFileApis(builder);
builder.Services.TryAddSingleton(GetValidateExtractedApis);
}
private static ValidateExtractedApis GetValidateExtractedApis(IServiceProvider provider)
{
var getApimResources = provider.GetRequiredService<GetApimApis>();
var tryGetApimGraphQlSchema = provider.GetRequiredService<TryGetApimGraphQlSchema>();
var getFileResources = provider.GetRequiredService<GetFileApis>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (apiNamesOption, defaultApiSpecification, versionSetNamesOption, serviceName, serviceDirectory, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(ValidateExtractedApis));
logger.LogInformation("Validating extracted APIs in {ServiceName}...", serviceName);
var expected = await getExpectedResources(apiNamesOption, versionSetNamesOption, serviceName, cancellationToken);
await validateExtractedInformationFiles(expected, serviceDirectory, cancellationToken);
await validateExtractedSpecificationFiles(expected, defaultApiSpecification, serviceName, serviceDirectory, cancellationToken);
};
async ValueTask<FrozenDictionary<ApiName, ApiDto>> getExpectedResources(Option<FrozenSet<ApiName>> apiNamesOption, Option<FrozenSet<VersionSetName>> versionSetNamesOption, ManagementServiceName serviceName, CancellationToken cancellationToken)
{
var apimResources = await getApimResources(serviceName, cancellationToken);
return apimResources.WhereKey(name => ExtractorOptions.ShouldExtract(name, apiNamesOption))
.WhereValue(dto => common.ApiModule.TryGetVersionSetName(dto)
.Map(name => ExtractorOptions.ShouldExtract(name, versionSetNamesOption))
.IfNone(true))
.ToFrozenDictionary();
}
async ValueTask validateExtractedInformationFiles(IDictionary<ApiName, ApiDto> expectedResources, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var fileResources = await getFileResources(serviceDirectory, Prelude.None, cancellationToken);
var expected = expectedResources.MapValue(normalizeDto)
.ToFrozenDictionary();
var actual = fileResources.MapValue(normalizeDto)
.ToFrozenDictionary();
actual.Should().BeEquivalentTo(expected);
}
static string normalizeDto(ApiDto dto) =>
new
{
DisplayName = dto.Properties.DisplayName ?? string.Empty,
Path = dto.Properties.Path ?? string.Empty,
RevisionDescription = dto.Properties.ApiRevisionDescription ?? string.Empty,
Revision = dto.Properties.ApiRevision ?? string.Empty,
ServiceUrl = Uri.TryCreate(dto.Properties.ServiceUrl, UriKind.Absolute, out var uri)
? uri.RemovePath().ToString()
: string.Empty
}.ToString()!;
async ValueTask validateExtractedSpecificationFiles(IDictionary<ApiName, ApiDto> expectedResources, Option<ApiSpecification> defaultApiSpecification, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var expected = await expectedResources.ToAsyncEnumerable()
.Choose(async kvp =>
{
var name = kvp.Key;
return from specification in await getExpectedApiSpecification(name, kvp.Value, defaultApiSpecification, serviceName, cancellationToken)
// Skip XML specification files. Sometimes they get extracted, other times they fail.
where specification is not (ApiSpecification.Wsdl or ApiSpecification.Wadl)
select (name, specification);
})
.ToFrozenDictionary(cancellationToken);
var actual = await common.ApiModule.ListSpecificationFiles(serviceDirectory)
.Select(file => (file.Parent.Name, file.Specification))
// Skip XML specification files. Sometimes they get extracted, other times they fail.
.Where(file => file.Specification is not (ApiSpecification.Wsdl or ApiSpecification.Wadl))
.ToFrozenDictionary(cancellationToken);
actual.Should().BeEquivalentTo(expected);
}
async ValueTask<Option<ApiSpecification>> getExpectedApiSpecification(ApiName name, ApiDto dto, Option<ApiSpecification> defaultApiSpecification, ManagementServiceName serviceName, CancellationToken cancellationToken)
{
switch (dto.Properties.ApiType ?? dto.Properties.Type)
{
case "graphql":
var specificationContents = await tryGetApimGraphQlSchema(name, serviceName, cancellationToken);
return specificationContents.Map(contents => new ApiSpecification.GraphQl() as ApiSpecification);
case "soap":
return new ApiSpecification.Wsdl();
case "websocket":
return Option<ApiSpecification>.None;
default:
return defaultApiSpecification.IfNone(() => new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Yaml(),
Version = new OpenApiVersion.V3()
});
}
}
}
}
file sealed class GetApimApisHandler(ILogger<GetApimApis> logger, GetManagementServiceUri getServiceUri, HttpPipeline pipeline, ActivitySource activitySource)
{
public async ValueTask<FrozenDictionary<ApiName, ApiDto>> Handle(ManagementServiceName serviceName, CancellationToken cancellationToken)
public static void ConfigureGetApimApis(IHostApplicationBuilder builder)
{
using var _ = activitySource.StartActivity(nameof(GetApimApis));
ManagementServiceModule.ConfigureGetManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
logger.LogInformation("Getting APIs from {ServiceName}...", serviceName);
var serviceUri = getServiceUri(serviceName);
var uri = ApisUri.From(serviceUri);
return await uri.List(pipeline, cancellationToken)
.ToFrozenDictionary(cancellationToken);
}
}
file sealed class TryGetApimGraphQlSchemaHandler(ILogger<TryGetApimGraphQlSchema> logger, GetManagementServiceUri getServiceUri, HttpPipeline pipeline, ActivitySource activitySource)
{
public async ValueTask<Option<BinaryData>> Handle(ApiName name, ManagementServiceName serviceName, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(TryGetApimGraphQlSchema));
logger.LogInformation("Getting GraphQL schema for {ApiName} from {ServiceName}...", name, serviceName);
var serviceUri = getServiceUri(serviceName);
var uri = ApiUri.From(name, serviceUri);
return await uri.TryGetGraphQlSchema(pipeline, cancellationToken);
}
}
file sealed class GetFileApisHandler(ILogger<GetFileApis> logger, ActivitySource activitySource)
{
public async ValueTask<FrozenDictionary<ApiName, ApiDto>> Handle(ManagementServiceDirectory serviceDirectory, Option<CommitId> commitIdOption, CancellationToken cancellationToken) =>
await commitIdOption.Map(commitId => GetWithCommit(serviceDirectory, commitId, cancellationToken))
.IfNone(() => GetWithoutCommit(serviceDirectory, cancellationToken));
private async ValueTask<FrozenDictionary<ApiName, ApiDto>> GetWithCommit(ManagementServiceDirectory serviceDirectory, CommitId commitId, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(GetFileApis));
logger.LogInformation("Getting apis from {ServiceDirectory} as of commit {CommitId}...", serviceDirectory, commitId);
return await Git.GetExistingFilesInCommit(serviceDirectory.ToDirectoryInfo(), commitId)
.ToAsyncEnumerable()
.Choose(file => ApiInformationFile.TryParse(file, serviceDirectory))
.Choose(async file => await TryGetCommitResource(commitId, serviceDirectory, file, cancellationToken))
.ToFrozenDictionary(cancellationToken);
builder.Services.TryAddSingleton(GetGetApimApis);
}
private static async ValueTask<Option<(ApiName name, ApiDto dto)>> TryGetCommitResource(CommitId commitId, ManagementServiceDirectory serviceDirectory, ApiInformationFile file, CancellationToken cancellationToken)
private static GetApimApis GetGetApimApis(IServiceProvider provider)
{
var name = file.Parent.Name;
var contentsOption = Git.TryGetFileContentsInCommit(serviceDirectory.ToDirectoryInfo(), file.ToFileInfo(), commitId);
var getServiceUri = provider.GetRequiredService<GetManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return await contentsOption.MapTask(async contents =>
return async (serviceName, cancellationToken) =>
{
using (contents)
using var _ = activitySource.StartActivity(nameof(GetApimApis));
logger.LogInformation("Getting APIs from {ServiceName}...", serviceName);
var serviceUri = getServiceUri(serviceName);
return await ApisUri.From(serviceUri)
.List(pipeline, cancellationToken)
.ToFrozenDictionary(cancellationToken);
};
}
public static void ConfigureTryGetApimGraphQlSchema(IHostApplicationBuilder builder)
{
ManagementServiceModule.ConfigureGetManagementServiceUri(builder);
AzureModule.ConfigureHttpPipeline(builder);
builder.Services.TryAddSingleton(GetTryGetApimGraphQlSchema);
}
private static TryGetApimGraphQlSchema GetTryGetApimGraphQlSchema(IServiceProvider provider)
{
var getServiceUri = provider.GetRequiredService<GetManagementServiceUri>();
var pipeline = provider.GetRequiredService<HttpPipeline>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (name, serviceName, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(TryGetApimGraphQlSchema));
logger.LogInformation("Getting GraphQL schema for {ApiName} from {ServiceName}...", name, serviceName);
var serviceUri = getServiceUri(serviceName);
return await ApiUri.From(name, serviceUri)
.TryGetGraphQlSchema(pipeline, cancellationToken);
};
}
public static void ConfigureGetFileApis(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetGetFileApis);
}
private static GetFileApis GetGetFileApis(IServiceProvider provider)
{
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (serviceDirectory, commitIdOption, cancellationToken) =>
{
using var _ = activitySource.StartActivity(nameof(GetFileApis));
return await commitIdOption.Map(commitId => getWithCommit(serviceDirectory, commitId, cancellationToken))
.IfNone(() => getWithoutCommit(serviceDirectory, cancellationToken));
};
async ValueTask<FrozenDictionary<ApiName, ApiDto>> getWithCommit(ManagementServiceDirectory serviceDirectory, CommitId commitId, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(GetFileApis));
logger.LogInformation("Getting apis from {ServiceDirectory} as of commit {CommitId}...", serviceDirectory, commitId);
return await Git.GetExistingFilesInCommit(serviceDirectory.ToDirectoryInfo(), commitId)
.ToAsyncEnumerable()
.Choose(file => ApiInformationFile.TryParse(file, serviceDirectory))
.Choose(async file => await tryGetCommitResource(commitId, serviceDirectory, file, cancellationToken))
.ToFrozenDictionary(cancellationToken);
}
static async ValueTask<Option<(ApiName name, ApiDto dto)>> tryGetCommitResource(CommitId commitId, ManagementServiceDirectory serviceDirectory, ApiInformationFile file, CancellationToken cancellationToken)
{
var name = file.Parent.Name;
var contentsOption = Git.TryGetFileContentsInCommit(serviceDirectory.ToDirectoryInfo(), file.ToFileInfo(), commitId);
return await contentsOption.MapTask(async contents =>
{
var data = await BinaryData.FromStreamAsync(contents, cancellationToken);
var dto = data.ToObjectFromJson<ApiDto>();
return (name, dto);
}
});
}
using (contents)
{
var data = await BinaryData.FromStreamAsync(contents, cancellationToken);
var dto = data.ToObjectFromJson<ApiDto>();
return (name, dto);
}
});
}
private async ValueTask<FrozenDictionary<ApiName, ApiDto>> GetWithoutCommit(ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(GetFileApis));
logger.LogInformation("Getting apis from {ServiceDirectory}...", serviceDirectory);
return await ApiModule.ListInformationFiles(serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Parent.Name,
await file.ReadDto(cancellationToken)))
.ToFrozenDictionary(cancellationToken);
}
}
file sealed class WriteApiModelsHandler(ILogger<WriteApiModels> logger, ActivitySource activitySource)
{
public async ValueTask Handle(IEnumerable<ApiModel> models, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(WriteApiModels));
logger.LogInformation("Writing api models to {ServiceDirectory}...", serviceDirectory);
await models.IterParallel(async model =>
async ValueTask<FrozenDictionary<ApiName, ApiDto>> getWithoutCommit(ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
await WriteRevisionArtifacts(model, serviceDirectory, cancellationToken);
}, cancellationToken);
logger.LogInformation("Getting APIs from {ServiceDirectory}...", serviceDirectory);
return await common.ApiModule.ListInformationFiles(serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Parent.Name,
await file.ReadDto(cancellationToken)))
.ToFrozenDictionary(cancellationToken);
}
}
public static async ValueTask WriteArtifacts(IEnumerable<ApiModel> models, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await models.IterParallel(async model =>
public static void ConfigureWriteApiModels(IHostApplicationBuilder builder)
{
builder.Services.TryAddSingleton(GetWriteApiModels);
}
private static WriteApiModels GetWriteApiModels(IServiceProvider provider)
{
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (models, serviceDirectory, cancellationToken) =>
{
await WriteRevisionArtifacts(model, serviceDirectory, cancellationToken);
}, cancellationToken);
using var _ = activitySource.StartActivity(nameof(WriteApiModels));
public static async ValueTask WriteRevisionArtifacts(ApiModel model, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await model.Revisions
.Select((revision, index) => (revision, index))
.IterParallel(async x =>
{
var (name, type, path, version, (revision, index)) = (model.Name, model.Type, model.Path, model.Version, x);
logger.LogInformation("Writing api models to {ServiceDirectory}...", serviceDirectory);
await WriteInformationFile(name, type, path, version, revision, index, serviceDirectory, cancellationToken);
await WriteSpecificationFile(name, type, revision, index, serviceDirectory, cancellationToken);
}, cancellationToken);
await models.IterParallel(async model =>
{
await writeRevisionArtifacts(model, serviceDirectory, cancellationToken);
}, cancellationToken);
};
private static async ValueTask WriteInformationFile(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision, int index, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var apiName = GetApiName(name, revision, index);
var informationFile = ApiInformationFile.From(apiName, serviceDirectory);
var rootApiName = ApiName.GetRootName(name);
var dto = GetDto(rootApiName, type, path, version, revision);
static async ValueTask writeRevisionArtifacts(ApiModel model, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await model.Revisions
.Select((revision, index) => (revision, index))
.IterParallel(async x =>
{
var (name, type, path, version, (revision, index)) = (model.Name, model.Type, model.Path, model.Version, x);
await informationFile.WriteDto(dto, cancellationToken);
}
await writeInformationFile(name, type, path, version, revision, index, serviceDirectory, cancellationToken);
await writeSpecificationFile(name, type, revision, index, serviceDirectory, cancellationToken);
}, cancellationToken);
private static ApiName GetApiName(ApiName name, ApiRevision revision, int index)
{
var rootApiName = ApiName.GetRootName(name);
static async ValueTask writeInformationFile(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision, int index, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var apiName = getApiName(name, revision, index);
var informationFile = ApiInformationFile.From(apiName, serviceDirectory);
var rootApiName = ApiName.GetRootName(name);
var dto = getDto(rootApiName, type, path, version, revision);
return index == 0 ? rootApiName : ApiName.GetRevisionedName(rootApiName, revision.Number);
}
await informationFile.WriteDto(dto, cancellationToken);
}
private static async ValueTask WriteSpecificationFile(ApiName name, ApiType type, ApiRevision revision, int index, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var specificationOption = from contents in revision.Specification
from specification in type switch
{
ApiType.Http => Option<ApiSpecification>.Some(new ApiSpecification.OpenApi
static ApiName getApiName(ApiName name, ApiRevision revision, int index)
{
var rootApiName = ApiName.GetRootName(name);
return index == 0 ? rootApiName : ApiName.GetRevisionedName(rootApiName, revision.Number);
}
static async ValueTask writeSpecificationFile(ApiName name, ApiType type, ApiRevision revision, int index, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
var specificationOption = from contents in revision.Specification
from specification in type switch
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
}),
ApiType.GraphQl => new ApiSpecification.GraphQl(),
ApiType.Soap => new ApiSpecification.Wsdl(),
_ => Option<ApiSpecification>.None
}
select (specification, contents);
ApiType.Http => Option<ApiSpecification>.Some(new ApiSpecification.OpenApi
{
Format = new OpenApiFormat.Json(),
Version = new OpenApiVersion.V3()
}),
ApiType.GraphQl => new ApiSpecification.GraphQl(),
ApiType.Soap => new ApiSpecification.Wsdl(),
_ => Option<ApiSpecification>.None
}
select (specification, contents);
await specificationOption.IterTask(async x =>
{
var (specification, contents) = x;
var apiName = GetApiName(name, revision, index);
var specificationFile = ApiSpecificationFile.From(specification, apiName, serviceDirectory);
await specificationFile.WriteSpecification(BinaryData.FromString(contents), cancellationToken);
});
}
private static ApiDto GetDto(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision) =>
new ApiDto()
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
await specificationOption.IterTask(async x =>
{
// APIM sets the description to null when it imports for SOAP APIs.
DisplayName = name.ToString(),
Path = path,
ApiType = type switch
var (specification, contents) = x;
var apiName = getApiName(name, revision, index);
var specificationFile = ApiSpecificationFile.From(specification, apiName, serviceDirectory);
await specificationFile.WriteSpecification(BinaryData.FromString(contents), cancellationToken);
});
}
static ApiDto getDto(ApiName name, ApiType type, string path, Option<ApiVersion> version, ApiRevision revision) =>
new ApiDto()
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
{
ApiType.Http => null,
ApiType.Soap => "soap",
ApiType.GraphQl => null,
ApiType.WebSocket => null,
_ => throw new NotSupportedException()
},
Type = type switch
{
ApiType.Http => "http",
ApiType.Soap => "soap",
ApiType.GraphQl => "graphql",
ApiType.WebSocket => "websocket",
_ => throw new NotSupportedException()
},
Protocols = type switch
{
ApiType.Http => ["http", "https"],
ApiType.Soap => ["http", "https"],
ApiType.GraphQl => ["http", "https"],
ApiType.WebSocket => ["ws", "wss"],
_ => throw new NotSupportedException()
},
ServiceUrl = revision.ServiceUri.ValueUnsafe()?.ToString(),
ApiRevisionDescription = revision.Description.ValueUnsafe(),
ApiRevision = $"{revision.Number.ToInt()}",
ApiVersion = version.Map(version => version.Version).ValueUnsafe(),
ApiVersionSetId = version.Map(version => $"/apiVersionSets/{version.VersionSetName}").ValueUnsafe()
}
};
}
file sealed class ValidatePublishedApisHandler(ILogger<ValidatePublishedApis> logger, GetFileApis getFileResources, GetApimApis getApimResources, ActivitySource activitySource)
{
public async ValueTask Handle(IDictionary<ApiName, ApiDto> overrides, Option<CommitId> commitIdOption, ManagementServiceName serviceName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(ValidatePublishedApis));
logger.LogInformation("Validating published apis in {ServiceDirectory}...", serviceDirectory);
var apimResources = await getApimResources(serviceName, cancellationToken);
var fileResources = await getFileResources(serviceDirectory, commitIdOption, cancellationToken);
var expected = PublisherOptions.Override(fileResources, overrides)
.MapValue(NormalizeDto);
var actual = apimResources.MapValue(NormalizeDto);
actual.Should().BeEquivalentTo(expected);
// APIM sets the description to null when it imports for SOAP APIs.
DisplayName = name.ToString(),
Path = path,
ApiType = type switch
{
ApiType.Http => null,
ApiType.Soap => "soap",
ApiType.GraphQl => null,
ApiType.WebSocket => null,
_ => throw new NotSupportedException()
},
Type = type switch
{
ApiType.Http => "http",
ApiType.Soap => "soap",
ApiType.GraphQl => "graphql",
ApiType.WebSocket => "websocket",
_ => throw new NotSupportedException()
},
Protocols = type switch
{
ApiType.Http => ["http", "https"],
ApiType.Soap => ["http", "https"],
ApiType.GraphQl => ["http", "https"],
ApiType.WebSocket => ["ws", "wss"],
_ => throw new NotSupportedException()
},
ServiceUrl = revision.ServiceUri.ValueUnsafe()?.ToString(),
ApiRevisionDescription = revision.Description.ValueUnsafe(),
ApiRevision = $"{revision.Number.ToInt()}",
ApiVersion = version.Map(version => version.Version).ValueUnsafe(),
ApiVersionSetId = version.Map(version => $"/apiVersionSets/{version.VersionSetName}").ValueUnsafe()
}
};
}
private static string NormalizeDto(ApiDto dto) =>
new
public static void ConfigureValidatePublishedApis(IHostApplicationBuilder builder)
{
ConfigureGetFileApis(builder);
ConfigureGetApimApis(builder);
builder.Services.TryAddSingleton(GetValidatePublishedApis);
}
private static ValidatePublishedApis GetValidatePublishedApis(IServiceProvider provider)
{
var getFileResources = provider.GetRequiredService<GetFileApis>();
var getApimResources = provider.GetRequiredService<GetApimApis>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var logger = provider.GetRequiredService<ILogger>();
return async (overrides, commitIdOption, serviceName, serviceDirectory, cancellationToken) =>
{
DisplayName = dto.Properties.DisplayName ?? string.Empty,
Path = dto.Properties.Path ?? string.Empty,
RevisionDescription = dto.Properties.ApiRevisionDescription ?? string.Empty,
Revision = dto.Properties.ApiRevision ?? string.Empty,
// Disabling this check because there are too many edge cases //TODO - Investigate
//ServiceUrl = Uri.TryCreate(dto.Properties.ServiceUrl, UriKind.Absolute, out var uri)
// ? uri.RemovePath().ToString()
// : string.Empty
}.ToString()!;
}
using var _ = activitySource.StartActivity(nameof(ValidatePublishedApis));
internal static class ApiServices
{
public static void ConfigureDeleteAllApis(IServiceCollection services)
{
ManagementServices.ConfigureGetManagementServiceUri(services);
logger.LogInformation("Validating published apis in {ServiceDirectory}...", serviceDirectory);
services.TryAddSingleton<DeleteAllApisHandler>();
services.TryAddSingleton<DeleteAllApis>(provider => provider.GetRequiredService<DeleteAllApisHandler>().Handle);
var apimResources = await getApimResources(serviceName, cancellationToken);
var fileResources = await getFileResources(serviceDirectory, commitIdOption, cancellationToken);
var expected = PublisherOptions.Override(fileResources, overrides)
.MapValue(normalizeDto)
.ToFrozenDictionary();
var actual = apimResources.MapValue(normalizeDto)
.ToFrozenDictionary();
actual.Should().BeEquivalentTo(expected);
};
static string normalizeDto(ApiDto dto) =>
new
{
DisplayName = dto.Properties.DisplayName ?? string.Empty,
Path = dto.Properties.Path ?? string.Empty,
RevisionDescription = dto.Properties.ApiRevisionDescription ?? string.Empty,
Revision = dto.Properties.ApiRevision ?? string.Empty,
// Disabling this check because there are too many edge cases //TODO - Investigate
//ServiceUrl = Uri.TryCreate(dto.Properties.ServiceUrl, UriKind.Absolute, out var uri)
// ? uri.RemovePath().ToString()
// : string.Empty
}.ToString()!;
}
public static void ConfigurePutApiModels(IServiceCollection services)
{
ManagementServices.ConfigureGetManagementServiceUri(services);
services.TryAddSingleton<PutApiModelsHandler>();
services.TryAddSingleton<PutApiModels>(provider => provider.GetRequiredService<PutApiModelsHandler>().Handle);
}
public static void ConfigureValidateExtractedApis(IServiceCollection services)
{
ConfigureGetApimApis(services);
ConfigureTryGetApimGraphQlSchema(services);
ConfigureGetFileApis(services);
services.TryAddSingleton<ValidateExtractedApisHandler>();
services.TryAddSingleton<ValidateExtractedApis>(provider => provider.GetRequiredService<ValidateExtractedApisHandler>().Handle);
}
private static void ConfigureGetApimApis(IServiceCollection services)
{
ManagementServices.ConfigureGetManagementServiceUri(services);
services.TryAddSingleton<GetApimApisHandler>();
services.TryAddSingleton<GetApimApis>(provider => provider.GetRequiredService<GetApimApisHandler>().Handle);
}
private static void ConfigureTryGetApimGraphQlSchema(IServiceCollection services)
{
ManagementServices.ConfigureGetManagementServiceUri(services);
services.TryAddSingleton<TryGetApimGraphQlSchemaHandler>();
services.TryAddSingleton<TryGetApimGraphQlSchema>(provider => provider.GetRequiredService<TryGetApimGraphQlSchemaHandler>().Handle);
}
private static void ConfigureGetFileApis(IServiceCollection services)
{
services.TryAddSingleton<GetFileApisHandler>();
services.TryAddSingleton<GetFileApis>(provider => provider.GetRequiredService<GetFileApisHandler>().Handle);
}
public static void ConfigureWriteApiModels(IServiceCollection services)
{
services.TryAddSingleton<WriteApiModelsHandler>();
services.TryAddSingleton<WriteApiModels>(provider => provider.GetRequiredService<WriteApiModelsHandler>().Handle);
}
public static void ConfigureValidatePublishedApis(IServiceCollection services)
{
ConfigureGetFileApis(services);
ConfigureGetApimApis(services);
services.TryAddSingleton<ValidatePublishedApisHandler>();
services.TryAddSingleton<ValidatePublishedApis>(provider => provider.GetRequiredService<ValidatePublishedApisHandler>().Handle);
}
}
internal static class Api
{
public static Gen<ApiModel> GenerateUpdate(ApiModel original) =>
from revisions in GenerateRevisionUpdates(original.Revisions, original.Type, original.Name)
select original with
@ -523,7 +553,7 @@ internal static class Api
var newGen = ApiRevision.GenerateSet(type, name);
var updateGen = (ApiRevision revision) => GenerateRevisionUpdate(revision, type);
return Fixture.GenerateNewSet(revisions, newGen, updateGen);
return Generator.GenerateNewSet(revisions, newGen, updateGen);
}
private static Gen<ApiRevision> GenerateRevisionUpdate(ApiRevision revision, ApiType type) =>
@ -609,4 +639,4 @@ internal static class Api
ApiVersionSetId = version.Map(version => $"/apiVersionSets/{version.VersionSetName}").ValueUnsafe()
}
};
}
}

Просмотреть файл

@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace integration.tests;
internal static class ApiPolicy
internal static class ApiPolicyModule
{
public static Gen<ApiPolicyModel> GenerateUpdate(ApiPolicyModel original) =>
from content in ApiPolicyModel.GenerateContent()
@ -79,17 +79,17 @@ internal static class ApiPolicy
}
private static async ValueTask<FrozenDictionary<ApiPolicyName, ApiPolicyDto>> GetFileResources(ApiName apiName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await ApiPolicyModule.ListPolicyFiles(apiName, serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Name,
new ApiPolicyDto
{
Properties = new ApiPolicyDto.ApiPolicyContract
{
Value = await file.ReadPolicy(cancellationToken)
}
}))
.ToFrozenDictionary(cancellationToken);
await common.ApiPolicyModule.ListPolicyFiles(apiName, serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Name,
new ApiPolicyDto
{
Properties = new ApiPolicyDto.ApiPolicyContract
{
Value = await file.ReadPolicy(cancellationToken)
}
}))
.ToFrozenDictionary(cancellationToken);
private static string NormalizeDto(ApiPolicyDto dto) =>
new

Просмотреть файл

@ -13,7 +13,7 @@ using System.Threading.Tasks;
namespace integration.tests;
internal static class ApiTag
internal static class ApiTagModule
{
public static async ValueTask Put(IEnumerable<ApiTagModel> models, ApiName apiName, ManagementServiceUri serviceUri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await models.IterParallel(async model =>
@ -60,11 +60,11 @@ internal static class ApiTag
}
private static async ValueTask<FrozenDictionary<TagName, ApiTagDto>> GetFileResources(ApiName apiName, ManagementServiceDirectory serviceDirectory, CancellationToken cancellationToken) =>
await ApiTagModule.ListInformationFiles(apiName, serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Parent.Name,
await file.ReadDto(cancellationToken)))
.ToFrozenDictionary(cancellationToken);
await common.ApiTagModule.ListInformationFiles(apiName, serviceDirectory)
.ToAsyncEnumerable()
.SelectAwait(async file => (file.Parent.Name,
await file.ReadDto(cancellationToken)))
.ToFrozenDictionary(cancellationToken);
private static string NormalizeDto(ApiTagDto dto) =>
nameof(ApiTagDto);

Просмотреть файл

@ -1,30 +1,43 @@
using Microsoft.Extensions.DependencyInjection;
using common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace integration.tests;
internal delegate ValueTask RunApplication(CancellationToken cancellationToken);
file sealed class RunApplicationHandler(ActivitySource activitySource, RunTests runTests)
internal static class AppModule
{
public async ValueTask Handle(CancellationToken cancellationToken)
public static void ConfigureRunApplication(IHostApplicationBuilder builder)
{
using var _ = activitySource.StartActivity(nameof(RunApplication));
TestModule.ConfigureTestExtractor(builder);
TestModule.ConfigureTestExtractThenPublish(builder);
TestModule.ConfigureTestPublisher(builder);
TestModule.ConfigureCleanUpTests(builder);
//TestModule.ConfigureTestWorkspaces(builder);
await runTests(cancellationToken);
builder.Services.TryAddSingleton(GetRunApplication);
}
}
internal static class AppServices
{
public static void ConfigureRunApplication(IServiceCollection services)
private static RunApplication GetRunApplication(IServiceProvider provider)
{
TestServices.ConfigureRunTests(services);
var testExtractor = provider.GetRequiredService<TestExtractor>();
var testExtractThenPublish = provider.GetRequiredService<TestExtractThenPublish>();
var testPublisher = provider.GetRequiredService<TestPublisher>();
//var testWorkspaces = provider.GetRequiredService<TestWorkspaces>();
var cleanUpTests = provider.GetRequiredService<CleanUpTests>();
var activitySource = provider.GetRequiredService<ActivitySource>();
services.TryAddSingleton<RunApplicationHandler>();
services.TryAddSingleton<RunApplication>(provider => provider.GetRequiredService<RunApplicationHandler>().Handle);
return async cancellationToken =>
{
using var _ = activitySource.StartActivity(nameof(RunApplication));
await testExtractor(cancellationToken);
await testExtractThenPublish(cancellationToken);
await testPublisher(cancellationToken);
await cleanUpTests(cancellationToken);
//await testWorkspaces(cancellationToken);
};
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше