Skip to content

Commit bf6daaa

Browse files
author
Wael Kdouh
committed
Merge remote-tracking branch 'colin/feature-graphql'
2 parents 7603af8 + cd8cb53 commit bf6daaa

File tree

6 files changed

+159
-7
lines changed

6 files changed

+159
-7
lines changed

tools/code/common/Api.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ private ApiInformationFile(ApiDirectory apiDirectory) : base(apiDirectory.Path.A
132132

133133
public static class Api
134134
{
135-
private static readonly JsonSerializerOptions serializerOptions = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
135+
private static readonly JsonSerializerOptions serializerOptions = new()
136+
{
137+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
138+
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
139+
};
136140

137141
internal static Uri GetUri(ServiceProviderUri serviceProviderUri, ServiceName serviceName, ApiName apiName) =>
138142
Service.GetUri(serviceProviderUri, serviceName)

tools/code/common/ApiSchema.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.IO;
3+
using System.Text.Json;
4+
using System.Text.Json.Nodes;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace common;
9+
10+
public sealed record GraphQLSchemaFile : FileRecord
11+
{
12+
public ApiDirectory ApiDirectory { get; }
13+
public const string FileName = "schema.graphql";
14+
15+
private GraphQLSchemaFile(ApiDirectory apiDirectory)
16+
: base(apiDirectory.Path.Append(FileName))
17+
{
18+
ApiDirectory = apiDirectory;
19+
}
20+
21+
public static GraphQLSchemaFile From(ApiDirectory apiDirectory) => new(apiDirectory);
22+
23+
public static GraphQLSchemaFile? TryFrom(ServiceDirectory serviceDirectory, FileInfo file)
24+
{
25+
var apiDirectory = ApiDirectory.TryFrom(serviceDirectory, file.Directory);
26+
27+
return (apiDirectory, file.Name) switch
28+
{
29+
(not null, FileName) => new GraphQLSchemaFile(apiDirectory),
30+
_ => null
31+
};
32+
}
33+
}
34+
35+
public sealed record ApiSchemaName : NonEmptyString
36+
{
37+
private ApiSchemaName(string value) : base(value)
38+
{
39+
}
40+
41+
public static ApiSchemaName From(string value) => new(value);
42+
43+
public static ApiSchemaName GraphQLSchemaName() => new("graphql");
44+
}
45+
46+
47+
public static class ApiSchema
48+
{
49+
50+
internal static Uri GetUri(ServiceProviderUri serviceProviderUri, ServiceName serviceName, ApiName apiName, ApiSchemaName schemaName)
51+
{
52+
return Api.GetUri(serviceProviderUri, serviceName, apiName)
53+
.AppendPath("schemas")
54+
.AppendPath(schemaName);
55+
}
56+
57+
public static async ValueTask<string?> TryGetGraphQLSchemaContent(Func<Uri, CancellationToken, ValueTask<JsonObject?>> tryGetResource, ServiceProviderUri serviceProviderUri, ServiceName serviceName, ApiName apiName, CancellationToken cancellationToken)
58+
{
59+
var uri = GetUri(serviceProviderUri,
60+
serviceName,
61+
apiName,
62+
ApiSchemaName.GraphQLSchemaName());
63+
64+
var json = await tryGetResource(uri, cancellationToken);
65+
66+
return json?.GetJsonObjectProperty("properties")
67+
.GetJsonObjectProperty("document")
68+
.GetStringProperty("value");
69+
}
70+
71+
public static async ValueTask PutGraphQL(Func<Uri, JsonObject, CancellationToken, ValueTask> putResource, ServiceProviderUri serviceProviderUri, ServiceName serviceName, ApiName apiName, string schemaText, CancellationToken cancellationToken)
72+
{
73+
var json = new JsonObject()
74+
{
75+
["properties"] = new JsonObject
76+
{
77+
["contentType"] = "application/vnd.ms-azure-apim.graphql.schema",
78+
["document"] = new JsonObject()
79+
{
80+
["value"] = schemaText
81+
}
82+
}
83+
};
84+
85+
var uri = GetUri(serviceProviderUri, serviceName, apiName, ApiSchemaName.GraphQLSchemaName());
86+
await putResource(uri, json, cancellationToken);
87+
}
88+
}

tools/code/common/Models/Api.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public sealed record ApiCreateOrUpdateProperties([property: JsonPropertyName("pa
6666
public string? TermsOfServiceUrl { get; init; }
6767

6868
[JsonPropertyName("type")]
69-
public string? Type { get; init; }
69+
public ApiType? Type { get; init; }
7070

7171
[JsonPropertyName("value")]
7272
public string? Value { get; init; }
@@ -162,3 +162,10 @@ public sealed record ApiCreateOrUpdatePropertiesWsdlSelector
162162
public string? WsdlServiceName { get; init; }
163163
}
164164
}
165+
public enum ApiType
166+
{
167+
Graphql,
168+
Http,
169+
Soap,
170+
Websocket
171+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace common.Models;

tools/code/extractor/Extractor.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ private async ValueTask ExportApi(common.Models.Api api, CancellationToken cance
448448
{
449449
await ExportApiInformation(api, cancellationToken);
450450
await ExportApiPolicy(api, cancellationToken);
451-
await ExportApiSpecification(api, cancellationToken);
451+
await ExportApiContractFile(api, cancellationToken);
452452
await ExportApiDiagnostics(api, cancellationToken);
453453
await ExportApiOperations(api, cancellationToken);
454454
}
@@ -489,7 +489,7 @@ private async ValueTask ExportApiPolicy(common.Models.Api api, CancellationToken
489489
}
490490
}
491491

492-
private async ValueTask ExportApiSpecification(common.Models.Api api, CancellationToken cancellationToken)
492+
private ValueTask ExportApiContractFile(common.Models.Api api, CancellationToken cancellationToken)
493493
{
494494
logger.LogInformation("Exporting specification for api {apiName}...", api.Name);
495495

@@ -498,14 +498,36 @@ private async ValueTask ExportApiSpecification(common.Models.Api api, Cancellati
498498
var apiVersion = ApiVersion.From(api.Properties.ApiVersion);
499499
var apiRevision = ApiRevision.From(api.Properties.ApiRevision);
500500
var apiDirectory = ApiDirectory.From(apisDirectory, apiDisplayName, apiVersion, apiRevision);
501-
var file = ApiSpecificationFile.From(apiDirectory, apiSpecification);
502501

503502
var apiName = ApiName.From(api.Name);
504-
var downloader = nonAuthenticatedHttpClient.GetSuccessfulResponseStream;
505-
using var specificationStream = await ApiSpecification.Get(getResource, downloader, serviceProviderUri, serviceName, apiName, apiSpecification, cancellationToken);
503+
504+
return api.Properties.Type switch
505+
{
506+
common.Models.ApiType.Graphql => ExportApiGraphQLSchema(apiDirectory, apiName, cancellationToken),
507+
_ => ExportApiSpecification(apiDirectory, apiName, cancellationToken)
508+
};
509+
}
510+
511+
private async ValueTask ExportApiSpecification(ApiDirectory apiDirectory, ApiName apiName, CancellationToken cancellationToken)
512+
{
513+
var file = ApiSpecificationFile.From(apiDirectory, apiSpecification);
514+
515+
Func<Uri, CancellationToken, ValueTask<System.IO.Stream>> downloader = nonAuthenticatedHttpClient.GetSuccessfulResponseStream;
516+
using System.IO.Stream specificationStream = await ApiSpecification.Get(getResource, downloader, serviceProviderUri, serviceName, apiName, apiSpecification, cancellationToken);
506517
await file.OverwriteWithStream(specificationStream, cancellationToken);
507518
}
508519

520+
private async ValueTask ExportApiGraphQLSchema(ApiDirectory apiDirectory, ApiName apiName, CancellationToken cancellationToken)
521+
{
522+
var schemaText = await ApiSchema.TryGetGraphQLSchemaContent(tryGetResource, serviceProviderUri, serviceName, apiName, cancellationToken);
523+
524+
if (schemaText is not null)
525+
{
526+
GraphQLSchemaFile file = GraphQLSchemaFile.From(apiDirectory);
527+
await file.OverwriteWithText(schemaText, cancellationToken);
528+
}
529+
}
530+
509531
private async ValueTask ExportApiDiagnostics(common.Models.Api api, CancellationToken cancellationToken)
510532
{
511533
var apiName = ApiName.From(api.Name);

tools/code/publisher/Publisher.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ private async ValueTask PutFiles(IReadOnlyCollection<FileRecord> files, Cancella
322322
var apiVersionSetInformationFiles = files.Choose(file => file as ApiVersionSetInformationFile).ToList();
323323
var apiInformationFiles = files.Choose(file => file as ApiInformationFile).ToList();
324324
var apiSpecificationFiles = files.Choose(file => file as ApiSpecificationFile).ToList();
325+
var apiGraphQLFiles = files.Choose(file => file as GraphQLSchemaFile).ToList();
325326
var apiDiagnosticInformationFiles = files.Choose(file => file as ApiDiagnosticInformationFile).ToList();
326327
var apiPolicyFiles = files.Choose(file => file as ApiPolicyFile).ToList();
327328
var apiOperationPolicyFiles = files.Choose(file => file as ApiOperationPolicyFile).ToList();
@@ -337,6 +338,7 @@ private async ValueTask PutFiles(IReadOnlyCollection<FileRecord> files, Cancella
337338
await PutProductPolicyFiles(productPolicyFiles, cancellationToken);
338339
await PutApiVersionSetInformationFiles(apiVersionSetInformationFiles, cancellationToken);
339340
await PutApiInformationAndSpecificationFiles(apiInformationFiles, apiSpecificationFiles, cancellationToken);
341+
await PutApiGraphQLSchemaFiles(apiGraphQLFiles, cancellationToken);
340342
await PutApiPolicyFiles(apiPolicyFiles, cancellationToken);
341343
await PutApiDiagnosticInformationFiles(apiDiagnosticInformationFiles, cancellationToken);
342344
await PutApiOperationPolicyFiles(apiOperationPolicyFiles, cancellationToken);
@@ -359,6 +361,7 @@ private async ValueTask PutFiles(IReadOnlyCollection<FileRecord> files, Cancella
359361
?? DiagnosticInformationFile.TryFrom(serviceDirectory, file) as FileRecord
360362
?? ApiVersionSetInformationFile.TryFrom(serviceDirectory, file) as FileRecord
361363
?? ApiInformationFile.TryFrom(serviceDirectory, file) as FileRecord
364+
?? GraphQLSchemaFile.TryFrom(serviceDirectory, file) as FileRecord
362365
?? ApiSpecificationFile.TryFrom(serviceDirectory, file) as FileRecord
363366
?? ApiDiagnosticInformationFile.TryFrom(serviceDirectory, file) as FileRecord
364367
?? ApiPolicyFile.TryFrom(serviceDirectory, file) as FileRecord
@@ -959,6 +962,18 @@ private async ValueTask PutApiInformationAndSpecificationFiles(IReadOnlyCollecti
959962
if (splitCurrentRevisions.TryGetValue("Current", out var currentRevisions)) await Parallel.ForEachAsync(currentRevisions, cancellationToken, (filePair, cancellationToken) => PutApi(filePair.Api, filePair.SpecificationFile, cancellationToken));
960963
if (splitCurrentRevisions.TryGetValue("NonCurrentRevisions", out var nonCurrentRevisions)) await Parallel.ForEachAsync(nonCurrentRevisions, cancellationToken, (filePair, cancellationToken) => PutApi(filePair.Api, filePair.SpecificationFile, cancellationToken));
961964
}
965+
966+
private async ValueTask PutApiGraphQLSchemaFiles(IReadOnlyCollection<GraphQLSchemaFile> schemaFiles, CancellationToken cancellationToken)
967+
{
968+
var apiSchemaPair = schemaFiles.Select(schemaFile =>
969+
{
970+
var apiJson = ApiInformationFile.From(schemaFile.ApiDirectory).ReadAsJsonObject();
971+
var api = Api.Deserialize(apiJson);
972+
return (Api: api, SchemaFile: schemaFile);
973+
});
974+
975+
await Parallel.ForEachAsync(apiSchemaPair, cancellationToken, (apiSchemaPair, cancellationToken) => PutApiGraphQLSchema(apiSchemaPair.Api, apiSchemaPair.SchemaFile, cancellationToken));
976+
}
962977

963978
private async ValueTask PutPolicyFragments(IReadOnlyCollection<PolicyFragmentInformationFile> informationFiles, IReadOnlyCollection<PolicyFragmentPolicyFile> policyFiles, CancellationToken cancellationToken)
964979
{
@@ -1047,6 +1062,15 @@ private async ValueTask PutApi(common.Models.Api api, ApiSpecificationFile? spec
10471062
await Api.Put(putResource, serviceProviderUri, serviceName, api, cancellationToken);
10481063
}
10491064

1065+
private async ValueTask PutApiGraphQLSchema(common.Models.Api api, GraphQLSchemaFile schemaFile, CancellationToken cancellationToken)
1066+
{
1067+
logger.LogInformation("Putting API GraphQL Schema for {api}...", api.Name);
1068+
var apiName = ApiName.From(api.Name);
1069+
var schemaText = await schemaFile.ReadAsText(cancellationToken) ?? throw new InvalidOperationException($"Could not read GraphQL schema file for API {api.Name}.");
1070+
1071+
await ApiSchema.PutGraphQL(putResource, serviceProviderUri, serviceName, apiName, schemaText, cancellationToken);
1072+
}
1073+
10501074
private async ValueTask DeleteApis(IReadOnlyCollection<ApiInformationFile> files, CancellationToken cancellationToken)
10511075
{
10521076
await Parallel.ForEachAsync(files, cancellationToken, DeleteApi);

0 commit comments

Comments
 (0)