Skip to content

Commit 530b357

Browse files
Introduces MSGraph sqlite db. Closes #304 (#324)
* Introduces MSGraph sqlite db. Closes #304 * Moves comment to the right place
1 parent be44186 commit 530b357

15 files changed

+279
-2223431
lines changed

m365-developer-proxy-abstractions/ILogger.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public enum LogLevel {
3636
Error
3737
}
3838

39-
public interface ILogger {
39+
public interface ILogger: ICloneable {
4040
public LogLevel LogLevel { get; set; }
4141

4242
public void LogRequest(string[] message, MessageType messageType, LoggingContext? context = null);

m365-developer-proxy-abstractions/ProxyUtils.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using System.Text.RegularExpressions;
5+
using Microsoft.Data.Sqlite;
56
using Titanium.Web.Proxy.Http;
67
using Titanium.Web.Proxy.Models;
78

@@ -22,6 +23,23 @@ public static class ProxyUtils {
2223
private static readonly Regex deprecationRegex = new Regex("^[a-z]+_v2$", RegexOptions.IgnoreCase);
2324
private static readonly Regex functionCallRegex = new Regex(@"^[a-z]+\(.*\)$", RegexOptions.IgnoreCase);
2425

26+
// v1 refers to v1 of the db schema, not the graph version
27+
public static string MsGraphDbFilePath => Path.Combine(AppFolder!, "msgraph-openapi-v1.db");
28+
private static SqliteConnection? _msGraphDbConnection;
29+
public static SqliteConnection MsGraphDbConnection {
30+
get {
31+
if (_msGraphDbConnection is null) {
32+
_msGraphDbConnection = new SqliteConnection($"Data Source={MsGraphDbFilePath}");
33+
_msGraphDbConnection.Open();
34+
}
35+
36+
return _msGraphDbConnection;
37+
}
38+
}
39+
40+
// doesn't end with a path separator
41+
public static string? AppFolder => Path.GetDirectoryName(AppContext.BaseDirectory);
42+
2543
public static bool IsGraphRequest(Request request) => IsGraphUrl(request.RequestUri);
2644

2745
public static bool IsGraphUrl(Uri uri) =>
@@ -76,9 +94,7 @@ public static string ReplacePathTokens(string? path) {
7694
return path ?? string.Empty;
7795
}
7896

79-
// doesn't end with a path separator
80-
var appFolder = Path.GetDirectoryName(AppContext.BaseDirectory);
81-
return path.Replace("~appFolder", appFolder, StringComparison.OrdinalIgnoreCase);
97+
return path.Replace("~appFolder", AppFolder, StringComparison.OrdinalIgnoreCase);
8298
}
8399

84100
// from: https://github.com/microsoftgraph/microsoft-graph-explorer-v4/blob/db86b903f36ef1b882996d46aee52cd49ed4444b/src/app/utils/query-url-sanitization.ts

m365-developer-proxy-abstractions/m365-developer-proxy-abstractions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
1213
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
1314
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
1415
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />

m365-developer-proxy-plugins/Guidance/GraphSelectGuidancePlugin.cs

Lines changed: 12 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using System.Text.RegularExpressions;
54
using Microsoft.Extensions.Configuration;
6-
using Microsoft.OpenApi.Models;
7-
using Microsoft.OpenApi.Readers;
85
using Microsoft365.DeveloperProxy.Abstractions;
96
using Titanium.Web.Proxy.Http;
107

@@ -13,94 +10,17 @@ namespace Microsoft365.DeveloperProxy.Plugins.Guidance;
1310
public class GraphSelectGuidancePlugin : BaseProxyPlugin
1411
{
1512
public override string Name => nameof(GraphSelectGuidancePlugin);
16-
private readonly Dictionary<string, OpenApiDocument> _openApiDocuments = new();
1713

1814
public override void Register(IPluginEvents pluginEvents,
1915
IProxyContext context,
2016
ISet<UrlToWatch> urlsToWatch,
2117
IConfigurationSection? configSection = null)
2218
{
23-
var proxyFolder = Path.GetDirectoryName(context.Configuration.ConfigFile);
24-
var stopwatch = new System.Diagnostics.Stopwatch();
25-
stopwatch.Start();
26-
LoadOpenAPIFiles(proxyFolder!);
27-
stopwatch.Stop();
28-
UpdateOpenAPIGraphFilesIfNecessary(proxyFolder!);
29-
3019
base.Register(pluginEvents, context, urlsToWatch, configSection);
3120

3221
pluginEvents.AfterResponse += AfterResponse;
3322
}
3423

35-
private async Task UpdateOpenAPIGraphFilesIfNecessary(string proxyFolder)
36-
{
37-
_logger?.LogDebug("Checking for updated OpenAPI files...");
38-
39-
var modified = false;
40-
var versions = new[] { "v1.0", "beta" };
41-
foreach (var version in versions)
42-
{
43-
try
44-
{
45-
var file = new FileInfo(Path.Combine(proxyFolder, "plugins", $"graph-{version.Replace(".", "_")}-openapi.yaml"));
46-
_logger?.LogDebug($"Checking for updated OpenAPI file for {file}...");
47-
if (file.LastWriteTime.Date == DateTime.Now.Date)
48-
{
49-
_logger?.LogDebug($"File {file} already updated today");
50-
// file already updated today
51-
continue;
52-
}
53-
54-
var url = $"https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/{version}/openapi.yaml";
55-
_logger?.LogDebug($"Downloading OpenAPI file from {url}...");
56-
57-
var client = new HttpClient();
58-
var response = await client.GetStringAsync(url);
59-
File.WriteAllText(file.FullName, response);
60-
61-
_logger?.LogDebug($"Downloaded OpenAPI file from {url} to {file}");
62-
modified = true;
63-
}
64-
catch (Exception ex)
65-
{
66-
_logger?.LogError(ex.Message);
67-
}
68-
}
69-
70-
if (modified)
71-
{
72-
LoadOpenAPIFiles(proxyFolder);
73-
}
74-
}
75-
76-
private async void LoadOpenAPIFiles(string proxyFolder)
77-
{
78-
_logger?.LogDebug("Loading OpenAPI files...");
79-
80-
var versions = new[] { "v1.0", "beta" };
81-
foreach (var version in versions)
82-
{
83-
var file = new FileInfo(Path.Combine(proxyFolder, "plugins", $"graph-{version.Replace(".", "_")}-openapi.yaml"));
84-
_logger?.LogDebug($"Loading OpenAPI file for {file}...");
85-
86-
if (!file.Exists)
87-
{
88-
_logger?.LogDebug($"File {file} does not exist");
89-
continue;
90-
}
91-
92-
try {
93-
var openApiDocument = await new OpenApiStreamReader().ReadAsync(file.OpenRead());
94-
_openApiDocuments[version] = openApiDocument.OpenApiDocument;
95-
96-
_logger?.LogDebug($"Added OpenAPI file {file} for {version}");
97-
}
98-
catch (Exception ex) {
99-
_logger?.LogDebug($"Error loading OpenAPI file {file}: {ex.Message}");
100-
}
101-
}
102-
}
103-
10424
private async Task AfterResponse(object? sender, ProxyResponseArgs e)
10525
{
10626
Request request = e.Session.HttpClient.Request;
@@ -134,25 +54,23 @@ private bool EndpointSupportsSelect(string graphVersion, string relativeUrl)
13454
{
13555
var fallback = relativeUrl.Contains("$value", StringComparison.OrdinalIgnoreCase);
13656

137-
if (!_openApiDocuments.ContainsKey(graphVersion))
57+
try
13858
{
139-
return fallback;
59+
var dbConnection = ProxyUtils.MsGraphDbConnection;
60+
// lookup information from the database
61+
var selectEndpoint = dbConnection.CreateCommand();
62+
selectEndpoint.CommandText = "SELECT hasSelect FROM endpoints WHERE path = @path AND graphVersion = @graphVersion";
63+
selectEndpoint.Parameters.AddWithValue("@path", relativeUrl);
64+
selectEndpoint.Parameters.AddWithValue("@graphVersion", graphVersion);
65+
var result = selectEndpoint.ExecuteScalar();
66+
var hasSelect = result != null && Convert.ToInt32(result) == 1;
67+
return hasSelect;
14068
}
141-
142-
var relativeUrlPattern = Regex.Replace(relativeUrl, @"{[^}]+}", @"{[a-zA-Z-]+}");
143-
var relativeUrlRegex = new Regex($"^{relativeUrlPattern}$");
144-
145-
var openApiDocument = _openApiDocuments[graphVersion];
146-
var pathString = openApiDocument.Paths.Keys.FirstOrDefault(k => relativeUrlRegex.IsMatch(k));
147-
if (pathString is null ||
148-
!openApiDocument.Paths[pathString].Operations.ContainsKey(OperationType.Get))
69+
catch (Exception ex)
14970
{
71+
_logger?.LogError(ex.Message);
15072
return fallback;
15173
}
152-
153-
var operation = openApiDocument.Paths[pathString].Operations[OperationType.Get];
154-
var parameters = operation.Parameters;
155-
return parameters.Any(p => p.Name == "$select");
15674
}
15775

15876
private static string GetSelectParameterGuidanceUrl() => "https://aka.ms/m365/proxy/guidance/select";

0 commit comments

Comments
 (0)