-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2da7d85
commit d8d26ff
Showing
10 changed files
with
294 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
using Microsoft.Extensions.Logging; | ||
using System.Text.Json; | ||
using Schemio; | ||
|
||
namespace ApiAggregator | ||
{ | ||
/// <summary> | ||
/// Implement to create a Web query using api endpoint. | ||
/// </summary> | ||
/// <typeparam name="TParameter">Type of Query parameter</typeparam> | ||
/// <typeparam name="TResult">Type of Query Result</typeparam> | ||
public abstract class BaseWebQuery<TParameter, TResult> : BaseQuery<TParameter, TResult>, IWebQuery, IRootQuery, IChildQuery | ||
where TParameter : IQueryParameter where TResult : IQueryResult | ||
{ | ||
protected BaseWebQuery(string baseAddress) | ||
Check warning on line 15 in src/ApiAggregator/BaseWebQuery.cs GitHub Actions / build
Check warning on line 15 in src/ApiAggregator/BaseWebQuery.cs GitHub Actions / build
Check warning on line 15 in src/ApiAggregator/BaseWebQuery.cs GitHub Actions / build
|
||
{ | ||
BaseAddress = baseAddress; | ||
Url = GetUrl(QueryParameter); | ||
Check warning on line 18 in src/ApiAggregator/BaseWebQuery.cs GitHub Actions / build
|
||
Headers = GetHeaders(); | ||
Check warning on line 19 in src/ApiAggregator/BaseWebQuery.cs GitHub Actions / build
|
||
} | ||
|
||
/// <summary> | ||
/// List of Request headers for the api call. | ||
/// </summary> | ||
public List<KeyValuePair<string, string>> Headers { get; protected set; } | ||
|
||
/// <summary> | ||
/// Base address for the api call. | ||
/// </summary> | ||
public string BaseAddress { get; protected set; } | ||
|
||
/// <summary> | ||
/// Api endpoint - complete or relative. | ||
/// </summary> | ||
public string Url { get; protected set; } | ||
|
||
/// <summary> | ||
/// Override to pass custom headers with the api request. | ||
/// </summary> | ||
/// <returns></returns> | ||
protected virtual List<KeyValuePair<string, string>>? GetHeaders() | ||
{ return []; } | ||
|
||
/// <summary> | ||
/// Implement to construct the api endpoint. | ||
/// </summary> | ||
/// <param name="queryParameter">Query Parameter</param> | ||
/// <returns></returns> | ||
protected abstract string? GetUrl(TParameter queryParameter); | ||
|
||
/// <summary> | ||
/// Implement to resolve query parameter. | ||
/// </summary> | ||
/// <param name="context">root context.</param> | ||
/// <param name="parentQueryResult">query result from parent query (when configured as nested query). Can be null.</param> | ||
protected abstract void ResolveQueryParameter(IDataContext context, IQueryResult parentQueryResult); | ||
|
||
/// <summary> | ||
/// Implement to resolve query parameter for nested queries | ||
/// </summary> | ||
/// <param name="context">root context</param> | ||
/// <param name="parentQueryResult">query result from parent query.</param> | ||
public void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult) | ||
{ | ||
ResolveQueryParameter(context, parentQueryResult); | ||
} | ||
|
||
/// <summary> | ||
/// Implement to resolve query parameter for first level queries. | ||
/// </summary> | ||
/// <param name="context">root context</param> | ||
public void ResolveRootQueryParameter(IDataContext context) | ||
{ | ||
ResolveQueryParameter(context, null); | ||
} | ||
|
||
/// <summary> | ||
/// Run this web query to get results. | ||
/// </summary> | ||
/// <param name="httpClientFactory">HttpClientFactory</param> | ||
/// <param name="logger">Logger</param> | ||
/// <returns></returns> | ||
/// <exception cref="ArgumentNullException">when httpclientfactory is null.</exception> | ||
public virtual async Task<IQueryResult[]> Run(IHttpClientFactory httpClientFactory, ILogger logger) | ||
{ | ||
if (httpClientFactory == null) | ||
throw new ArgumentNullException("HttpClientFactory is required"); | ||
|
||
var localStorage = new List<TResult>(); | ||
|
||
logger?.LogInformation($"Run query: {GetType().Name}"); | ||
|
||
using (var client = httpClientFactory.CreateClient()) | ||
{ | ||
logger?.LogInformation($"Executing web queries on thread {Thread.CurrentThread.ManagedThreadId} (task {Task.CurrentId})"); | ||
|
||
try | ||
{ | ||
HttpResponseMessage result; | ||
|
||
try | ||
{ | ||
if (!string.IsNullOrEmpty(BaseAddress)) | ||
client.BaseAddress = new Uri(BaseAddress); | ||
|
||
if (Headers != null && Headers.Any()) | ||
foreach (var header in Headers) | ||
client.DefaultRequestHeaders.Add(header.Key, header.Value); | ||
|
||
result = await client.GetAsync(Url); | ||
|
||
if (!result.IsSuccessStatusCode) | ||
{ | ||
logger?.LogInformation($"Result of executing web query {Url} is not success status code"); | ||
} | ||
|
||
var raw = result.Content.ReadAsStringAsync().Result; | ||
|
||
if (string.IsNullOrWhiteSpace(raw)) | ||
logger?.LogInformation($"Result.Content of executing web query {Url} is null or whitespace"); | ||
|
||
if (ResultType.IsArray) | ||
{ | ||
var arrObject = JsonSerializer.Deserialize(raw, ResultType); | ||
if (arrObject != null) | ||
localStorage.AddRange((IEnumerable<TResult>)arrObject); | ||
} | ||
else | ||
{ | ||
var obj = JsonSerializer.Deserialize(raw, ResultType); | ||
if (obj != null) | ||
localStorage.Add((TResult)obj); | ||
} | ||
} | ||
catch (TaskCanceledException ex) | ||
{ | ||
logger?.LogWarning(ex, $"An error occurred while sending the request. Query URL: {Url}"); | ||
} | ||
catch (HttpRequestException ex) | ||
{ | ||
logger?.LogWarning(ex, $"An error occurred while sending the request. Query URL: {Url}"); | ||
} | ||
} | ||
catch (AggregateException ex) | ||
{ | ||
logger?.LogInformation($"Web query {GetType().Name} failed"); | ||
foreach (var e in ex.InnerExceptions) | ||
{ | ||
logger?.LogError(e, ""); | ||
} | ||
} | ||
} | ||
|
||
return localStorage.Cast<IQueryResult>().ToArray(); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Schemio; | ||
using Schemio.Helpers; | ||
|
||
namespace ApiAggregator | ||
{ | ||
public class ColonSeparatedMatcher : ISchemaPathMatcher | ||
{ | ||
public bool IsMatch(string inputXPath, ISchemaPaths configuredXPaths) => | ||
// Does the template xpath contain any of the mapping xpaths? | ||
inputXPath.IsNotNullOrEmpty() | ||
&& configuredXPaths.Paths.Any(x => inputXPath.ToLower().Contains(x.ToLower())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Schemio; | ||
|
||
namespace ApiAggregator | ||
{ | ||
public static class EnumerableExtensions | ||
{ | ||
public static IEnumerable<T> GetByType<T>(this IEnumerable<IQuery> list) where T : class, IQuery | ||
{ | ||
var filtered = list.Where(q => (q as T) != null); | ||
return filtered.Cast<T>(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Microsoft.Extensions.Logging; | ||
using Schemio; | ||
|
||
namespace ApiAggregator | ||
{ | ||
public interface IWebQuery : IQuery | ||
{ | ||
List<KeyValuePair<string, string>> Headers { get; } | ||
string BaseAddress { get; } | ||
string Url { get; } | ||
|
||
Task<IQueryResult[]> Run(IHttpClientFactory httpClientFactory, ILogger logger); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using Microsoft.Extensions.Logging; | ||
using Schemio; | ||
|
||
namespace ApiAggregator | ||
{ | ||
public class QueryEngine : IQueryEngine | ||
{ | ||
private readonly ILogger<QueryEngine> logger; | ||
private readonly IHttpClientFactory httpClientFactory; | ||
|
||
public QueryEngine(IHttpClientFactory httpClientFactory, ILogger<QueryEngine> logger) | ||
{ | ||
this.httpClientFactory = httpClientFactory; | ||
this.logger = logger; | ||
} | ||
|
||
public bool CanExecute(IQuery query) => query is IWebQuery; | ||
|
||
public IEnumerable<IQueryResult> Execute(IEnumerable<IQuery> queries) | ||
{ | ||
if (queries == null || !queries.Any()) | ||
return []; | ||
|
||
var webQueries = queries.GetByType<IWebQuery>(); | ||
|
||
if (!webQueries.Any()) | ||
return []; | ||
|
||
logger.LogInformation($"Total web queries to execute: {webQueries.Count()}"); | ||
|
||
var tasks = webQueries | ||
.Select(q => q.Run(httpClientFactory, logger)) | ||
.ToArray(); | ||
|
||
Task.WhenAll(tasks); | ||
|
||
var result = new List<IQueryResult>(); | ||
|
||
foreach (var task in tasks) | ||
{ | ||
result.AddRange(task.Result); | ||
} | ||
|
||
return result.ToArray(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using Schemio; | ||
using Schemio.Impl; | ||
|
||
namespace ApiAggregator | ||
{ | ||
public static class ServicesExtensions | ||
{ | ||
public static IServiceCollection UseApiAggregator(this IServiceCollection services, Func<IEntity, IEntitySchema<IEntity>> schemas) | ||
{ | ||
services.AddTransient(typeof(IQueryBuilder<>), typeof(QueryBuilder<>)); | ||
services.AddTransient(typeof(ITransformExecutor<>), typeof(TransformExecutor<>)); | ||
services.AddTransient(typeof(IDataProvider<>), typeof(DataProvider<>)); | ||
//services.AddTransient(typeof(IEntitySchema<>), typeof(BaseEntitySchema<>)); | ||
|
||
services.AddTransient<IQueryExecutor, QueryExecutor>(); | ||
services.AddTransient<ISchemaPathMatcher, ColonSeparatedMatcher>(); | ||
services.AddTransient<IQueryEngine, QueryEngine>(); | ||
|
||
//services.AddTransient((c) => schema); | ||
|
||
return services; | ||
} | ||
|
||
public static IServiceCollection AddEntitySchema<TEntity>(this IServiceCollection services, IEntitySchema<IEntity> schema) | ||
where TEntity : IEntity | ||
{ | ||
if (schema != null) | ||
services.AddTransient(c => (IEntitySchema<TEntity>)schema); | ||
|
||
return services; | ||
} | ||
} | ||
} |