Skip to content

Commit

Permalink
Merge pull request #74 from microsoftgraph/refactor
Browse files Browse the repository at this point in the history
Removed obsolete GetRootParseNode
  • Loading branch information
jasonjoh authored Sep 3, 2024
2 parents 20d8070 + a9dc7ad commit feab41a
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 72 deletions.
12 changes: 6 additions & 6 deletions src/Extensions/IssueExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,26 @@ public static async Task<ExternalItem> ToExternalItem(
return new ExternalItem
{
Id = issue.Number.ToString(),
Acl = new()
{
Acl =
[
new()
{
Type = AclType.Everyone,
Value = "everyone",
AccessType = AccessType.Grant,
},
},
],
Properties = issue.ToProperties(events),
Activities = new()
{
Activities =
[
new()
{
OdataType = "#microsoft.graph.externalConnectors.externalActivity",
Type = ExternalActivityType.Created,
StartDateTime = issue.CreatedAt,
PerformedBy = await connectorService.GetIdentityForGitHubUserAsync(issue.User.Login),
},
},
],
};
}

Expand Down
12 changes: 6 additions & 6 deletions src/Extensions/RepositoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,26 @@ public static async Task<ExternalItem> ToExternalItem(
return new ExternalItem
{
Id = repository.Id.ToString(),
Acl = new()
{
Acl =
[
new()
{
Type = AclType.Everyone,
Value = "everyone",
AccessType = AccessType.Grant,
},
},
],
Properties = repository.ToProperties(events),
Activities = new()
{
Activities =
[
new()
{
OdataType = "#microsoft.graph.externalConnectors.externalActivity",
Type = ExternalActivityType.Created,
StartDateTime = repository.CreatedAt,
PerformedBy = await connectorService.GetIdentityForGitHubUserAsync(repository.Owner.Login),
},
},
],
};
}

Expand Down
49 changes: 22 additions & 27 deletions src/Middleware/PollForSchemaStatusHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,46 @@ namespace GitHubConnector.Middleware;
/// <summary>
/// Middleware handler for asynchronous creation of connector schema.
/// </summary>
public partial class PollForSchemaStatusHandler : DelegatingHandler
/// <remarks>
/// Initializes a new instance of the <see cref="PollForSchemaStatusHandler"/> class.
/// </remarks>
/// <param name="delay">The number of milliseconds to wait between poll requests.</param>
public partial class PollForSchemaStatusHandler(int delay = 60000) : DelegatingHandler
{
// Match URL like:
// /external/connections/{connection-id}/schema
private static readonly Regex PostSchemaRegex =
new("\\/external\\/connections\\/[0-9a-zA-Z]+\\/schema", RegexOptions.IgnoreCase);

// Match URL like:
// /external/connections/{connection-id}/operations/{operation-id}
private static readonly Regex GetOperationRegex =
new("\\/external\\/connections\\/[0-9a-zA-Z]+\\/operations\\/.*", RegexOptions.IgnoreCase);

/// <summary>
/// Initializes a new instance of the <see cref="PollForSchemaStatusHandler"/> class.
/// </summary>
/// <param name="delay">The number of milliseconds to wait between poll requests.</param>
public PollForSchemaStatusHandler(int delay = 60000)
{
Delay = delay;
}

/// <summary>
/// Gets the delay setting in milliseconds.
/// </summary>
public int Delay { get; private set; }
public int Delay { get; private set; } = delay;

/// <inheritdoc/>
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Patch &&
PostSchemaRegex.IsMatch(request.RequestUri?.AbsolutePath ?? string.Empty))
PostSchemaRegex().IsMatch(request.RequestUri?.AbsolutePath ?? string.Empty))
{
return HandlePatchSchemaRequestAsync(request, cancellationToken);
}

if (request.Method == HttpMethod.Get &&
GetOperationRegex.IsMatch(request.RequestUri?.AbsolutePath ?? string.Empty))
GetOperationRegex().IsMatch(request.RequestUri?.AbsolutePath ?? string.Empty))
{
return HandleGetOperationStatusRequestAsync(request, cancellationToken);
}

return base.SendAsync(request, cancellationToken);
}

// Match URL like:
// /external/connections/{connection-id}/operations/{operation-id}
[GeneratedRegex("\\/external\\/connections\\/[0-9a-zA-Z]+\\/operations\\/.*", RegexOptions.IgnoreCase)]
private static partial Regex GetOperationRegex();

// Match URL like:
// /external/connections/{connection-id}/schema
[GeneratedRegex("\\/external\\/connections\\/[0-9a-zA-Z]+\\/schema", RegexOptions.IgnoreCase)]
private static partial Regex PostSchemaRegex();

private async Task<HttpResponseMessage> HandlePatchSchemaRequestAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Expand All @@ -69,7 +64,7 @@ private async Task<HttpResponseMessage> HandlePatchSchemaRequestAsync(
if (location is not null)
{
Console.WriteLine($"Waiting {Delay}ms to poll {location.AbsoluteUri}");
await Task.Delay(Delay);
await Task.Delay(Delay, cancellationToken);

request.RequestUri = location;
request.Method = HttpMethod.Get;
Expand All @@ -96,17 +91,17 @@ private async Task<HttpResponseMessage> HandleGetOperationStatusRequestAsync(
if (response.IsSuccessStatusCode)
{
// Use Graph SDK's parsers to deserialize the body
var body = await response.Content.ReadAsStringAsync();
var body = await response.Content.ReadAsStringAsync(cancellationToken);
using var responseBody = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(body));
var parseNode = ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNode("application/json", responseBody);
var parseNode = await ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNodeAsync("application/json", responseBody, cancellationToken);
var operation = parseNode.GetObjectValue(ConnectionOperation.CreateFromDiscriminatorValue) ??
throw new ServiceException("Could not get operation from API.");

if (operation.Status == ConnectionOperationStatus.Inprogress)
{
// Schema registration is in progress, poll again
Console.WriteLine($"Waiting {Delay}ms to poll {request.RequestUri?.AbsoluteUri}");
await Task.Delay(Delay);
await Task.Delay(Delay, cancellationToken);
return await SendAsync(request, cancellationToken);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static async Task ListenForSimplifiedAdminAsync(
Console.WriteLine("Received post");

// Parse the POST body
var changeNotifications = m365AppConfigService.DeserializePostBody(request.InputStream);
var changeNotifications = await M365AppConfigService.DeserializePostBody(request.InputStream);

// Ensure the body deserialized into the expected form
// and that the validation tokens are valid.
Expand Down
38 changes: 15 additions & 23 deletions src/Services/M365AppConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,26 @@ namespace GitHubConnector.Services;
/// <summary>
/// Contains methods for listening for configuration changes from a Microsoft 365 app.
/// </summary>
public class M365AppConfigService
/// <remarks>
/// Initializes a new instance of the <see cref="M365AppConfigService"/> class.
/// </remarks>
/// <param name="settings">The application settings.</param>
public class M365AppConfigService(AppSettings settings)
{
private readonly HttpListener listener;
private readonly int port;
private readonly string tenantId;
private readonly string clientId;
private readonly HttpListener listener = new();
private readonly int port = settings.PortNumber;
private readonly string tenantId = settings.TenantId ?? throw new ArgumentException("tenantId not set in app settings");
private readonly string clientId = settings.ClientId ?? throw new ArgumentException("clientId not set in app settings");

/// <summary>
/// Initializes a new instance of the <see cref="M365AppConfigService"/> class.
/// Deserializes the body of an incoming HTTP POST request.
/// </summary>
/// <param name="settings">The application settings.</param>
public M365AppConfigService(AppSettings settings)
/// <param name="postBody">The input <see cref="Stream"/> to deserialize.</param>
/// <returns>An instance of the <see cref="Changes"/> class.</returns>
public static async Task<Changes?> DeserializePostBody(Stream postBody)
{
tenantId = settings.TenantId ?? throw new ArgumentException("tenantId not set in app settings");
clientId = settings.ClientId ?? throw new ArgumentException("clientId not set in app settings");
port = settings.PortNumber;
listener = new();
var parseNode = await new JsonParseNodeFactory().GetRootParseNodeAsync("application/json", postBody);
return parseNode.GetObjectValue(Changes.CreateFromDiscriminatorValue);
}

/// <summary>
Expand All @@ -53,17 +56,6 @@ public void Stop()
listener.Stop();
}

/// <summary>
/// Deserializes the body of an incoming HTTP POST request.
/// </summary>
/// <param name="postBody">The input <see cref="Stream"/> to deserialize.</param>
/// <returns>An instance of the <see cref="Changes"/> class.</returns>
public Changes? DeserializePostBody(Stream postBody)
{
var parseNode = new JsonParseNodeFactory().GetRootParseNode("application/json", postBody);
return parseNode.GetObjectValue(Changes.CreateFromDiscriminatorValue);
}

/// <summary>
/// Validates the tokens sent by Microsoft Graph.
/// </summary>
Expand Down
18 changes: 9 additions & 9 deletions src/Services/SearchConnectorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ public SearchConnectorService(AppSettings settings)
Description = description,
ActivitySettings = new()
{
UrlToItemResolvers = new()
{
UrlToItemResolvers =
[
new ItemIdResolver
{
Priority = 1,
Expand All @@ -136,15 +136,15 @@ public SearchConnectorService(AppSettings settings)
UrlPattern = itemType == "issues" ?
$"/{gitHubOwner}/{gitHubRepo}/issues/(?<issueId>[0-9]+)" :
$"/{gitHubOwner}/(?<repo>.*)/",
BaseUrls = new() { "https://github.com" },
BaseUrls = ["https://github.com"],
},
},
},
],
},
SearchSettings = new()
{
SearchResultTemplates = new()
{
SearchResultTemplates =
[
new()
{
Id = itemType == "issues" ? "issueDisplay" : "repoDisplay",
Expand All @@ -154,7 +154,7 @@ public SearchConnectorService(AppSettings settings)
AdditionalData = await GetResultTemplateAsync(resultCardJsonFile),
},
},
},
],
},
};

Expand All @@ -173,7 +173,7 @@ public SearchConnectorService(AppSettings settings)
{
if (useM365Properties)
{
config.Headers.Add("GraphConnectors-Ticket", new[] { connectorTicket! });
config.Headers.Add("GraphConnectors-Ticket", [connectorTicket!]);
}
});
}
Expand Down Expand Up @@ -280,7 +280,7 @@ public Task<Identity> GetIdentityForGitHubUserAsync(string? gitHubLogin)
});
}

private async Task<Dictionary<string, object>> GetResultTemplateAsync(string resultCardJsonFile)
private static async Task<Dictionary<string, object>> GetResultTemplateAsync(string resultCardJsonFile)
{
var cardContents = await File.ReadAllTextAsync(resultCardJsonFile);
return JsonSerializer.Deserialize<Dictionary<string, object>>(cardContents) ??
Expand Down

0 comments on commit feab41a

Please sign in to comment.