Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
126 changes: 126 additions & 0 deletions SW.Bitween.Api/Controllers/GatewayController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SW.Bitween.Domain;
using SW.Bitween.Domain.Gateway;
using SW.Bitween.Model;
using SW.PrimitiveTypes;

namespace SW.Bitween.Controllers;

[ApiController]
[Route("api/[controller]")]
public class GatewayController(
BitweenDbContext dbContext,
RequestContext requestContext,
IInfolinkCache cache,
XchangeService xchangeService,
BitweenOptions bitweenSettings) : ControllerBase
{
[HttpPost("{gatewayApiName}/sync")]
public Task<IActionResult> PostSync([FromRoute] string gatewayApiName)
{
return ProcessAsync(gatewayApiName, resultSync: true);
}

[HttpPost("{gatewayApiName}/async")]
public Task<IActionResult> PostAsync([FromRoute] string gatewayApiName)
{
return ProcessAsync(gatewayApiName, resultSync: false);
}

private async Task<IActionResult> ProcessAsync([FromRoute] string gatewayApiName, bool resultSync)
{
var globalAdapterValuesSet = await cache.ListGlobalAdapterValuesSetsAsync();
var apiGateway = await dbContext.Set<ApiGateway>()
.Include(ag => ag.Partners)
.ThenInclude(agp => agp.Partner)
.FirstOrDefaultAsync(ag => ag.UrlName == gatewayApiName);

if (apiGateway == null)
return NotFound();

// Resolve partner using API key
var (authorized, partner, keyName) = await dbContext.CheckPartnerAuthorized(requestContext);

if (!authorized)
return Unauthorized();

// Verify partner is part of the API Gateway
var apiGatewayPartner = apiGateway.Partners.FirstOrDefault(agp => agp.PartnerId == partner.Id);
if (apiGatewayPartner == null)
return Unauthorized();

var subscription = await cache.SubscriptionByIdAsync(apiGatewayPartner.SubscriptionId);


var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();

var xchangeFile = new XchangeFile(json);

var validatorProperties = subscription.ValidatorProperties.ToDictionary()
.Fill(partner, globalAdapterValuesSet);
await xchangeService.RunValidator(subscription.ValidatorId, validatorProperties,
xchangeFile);

var xchangeReferences = new List<string> { $"partnerkey: {keyName}" };
var globalAdapterValuesSets = await dbContext.Set<GlobalAdapterValuesSet>().ToArrayAsync();
var xchangeId = await xchangeService.SubmitSubscriptionXchange(subscription.Id, xchangeFile,
xchangeReferences.ToArray(), partner, globalAdapterValuesSets);
if (!resultSync)
{
return Accepted(xchangeId);
}

var waitResponse = 120;
// check headers for wait response value
var waitResponseHeader = Request.Headers["Wait-Period"].FirstOrDefault();
if (int.TryParse(waitResponseHeader, out var waitResponseValue))
{
waitResponse = waitResponseValue <= 0 ? 120 : waitResponseValue;
}

var currentFibTerm = 1;
var previousTerm = 1;
while (currentFibTerm <= waitResponse)
{
await Task.Delay(TimeSpan.FromSeconds(currentFibTerm));
var nextTerm = Math.Min(currentFibTerm + previousTerm, 8);
previousTerm = currentFibTerm;
currentFibTerm = nextTerm;
if (!await dbContext.Set<XchangeResult>()
.AsNoTracking()
.AnyAsync(i => i.Id == xchangeId)) continue;

var xchangeResult = await dbContext.FindAsync<XchangeResult>(xchangeId);


switch (xchangeResult!.Success)
{
case true when xchangeResult.ResponseSize == 0:
{
return Ok(xchangeId);
}
case true when xchangeResult.ResponseSize != 0:
{
var response = await xchangeService.GetFile(xchangeId, XchangeFileType.Response);
return new ContentResult
{
StatusCode = xchangeResult.ResponseBad ? 400 : 200,
Content = response,
ContentType = xchangeResult.ResponseContentType ?? MediaTypeNames.Application.Json,
};
}
case false:
return BadRequest();
}
}
Comment thread
AhmadRAbuhussein marked this conversation as resolved.

return Accepted(xchangeId);
}
}
39 changes: 36 additions & 3 deletions SW.Bitween.Api/Data/BitweenDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using SW.EfCoreExtensions;
using SW.Bitween.Domain;
using SW.PrimitiveTypes;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SW.Bitween.Domain.Accounts;
using SW.Bitween.Domain.Gateway;
using SW.Bitween.JsonConverters;

namespace SW.Bitween
Expand Down Expand Up @@ -92,11 +91,43 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

});

modelBuilder.Entity<ApiGateway>(ag =>
{
ag.ToTable("ApiGateways");
ag.HasKey(i => i.Id);
ag.Property(i => i.Id).ValueGeneratedOnAdd();
ag.Property(p => p.Name).IsRequired().HasMaxLength(200);
ag.Property(p => p.UrlName).IsRequired().HasMaxLength(200);
ag.HasIndex(p => p.UrlName).IsUnique();
ag.HasMany(p => p.Partners).WithOne(p => p.ApiGateway).HasForeignKey(p => p.ApiGatewayId)
.OnDelete(DeleteBehavior.Restrict);
});

modelBuilder.Entity<ApiGatewayPartner>(agp =>
{
agp.ToTable("ApiGatewayPartners");
agp.HasKey(p => new { p.ApiGatewayId, p.PartnerId, p.SubscriptionId });
agp.HasOne(p => p.ApiGateway).WithMany(p => p.Partners).HasForeignKey(p => p.ApiGatewayId)
.OnDelete(DeleteBehavior.Restrict);
agp.HasOne(p => p.Partner).WithMany().HasForeignKey(p => p.PartnerId)
.OnDelete(DeleteBehavior.Restrict);
agp.HasOne(p => p.Subscription).WithMany().HasForeignKey(p => p.SubscriptionId)
.OnDelete(DeleteBehavior.Restrict);
});

modelBuilder.Entity<GlobalAdapterValuesSet>(gav =>
{
gav.ToTable("GlobalAdapterValuesSets");
gav.HasKey(i => i.Id);
gav.Property(p => p.Id).IsUnicode(false).HasMaxLength(200);
gav.Property(p => p.Values).StoreAsJson();
});
modelBuilder.Entity<Partner>(b =>
{
b.ToTable("Partners");
b.Metadata.SetNavigationAccessMode(PropertyAccessMode.Field);
b.Property(p => p.Name).IsRequired().IsUnicode(false).HasMaxLength(200);
b.Property(p => p.AdapterProperties).StoreAsJson();
b.HasMany(p => p.Subscriptions).WithOne().IsRequired(false).HasForeignKey(p => p.PartnerId)
.OnDelete(DeleteBehavior.Restrict);
b.OwnsMany(p => p.ApiCredentials, apicred =>
Expand Down Expand Up @@ -305,6 +336,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
b.Property(p => p.AccountId);
b.Property(p => p.LoginMethod).HasConversion<byte>();
});

}

async public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -334,4 +366,5 @@ await publish.Publish(hasWorkGroup.GetBusMessageName(),
return affectedRecords;
}
}
}
}

16 changes: 16 additions & 0 deletions SW.Bitween.Api/Domain/Gateway/ApiGateway.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using SW.PrimitiveTypes;

namespace SW.Bitween.Domain.Gateway;

public class ApiGateway : BaseEntity,IAudited
{
public string Name { get; set; }
public string UrlName { get; set; }
public ICollection<ApiGatewayPartner> Partners { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime? ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
}
18 changes: 18 additions & 0 deletions SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using SW.PrimitiveTypes;

namespace SW.Bitween.Domain.Gateway;

public class ApiGatewayPartner: IAudited
{
public ApiGateway ApiGateway { get; set; }
public int ApiGatewayId { get; set; }
public Partner Partner { get; set; }
public int PartnerId { get; set; }
public Subscription Subscription { get; set; }
public int SubscriptionId { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime? ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
}
11 changes: 11 additions & 0 deletions SW.Bitween.Api/Domain/GlobalAdapterValue/GlobalAdapterValuesSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using SW.PrimitiveTypes;

namespace SW.Bitween.Domain;

public class GlobalAdapterValuesSet:BaseEntity<string>
{
public string Name { get; set; }
public Dictionary<string, string> Values { get; set; }
}
4 changes: 3 additions & 1 deletion SW.Bitween.Api/Domain/Partner/Partner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace SW.Bitween.Domain
{
public class Partner : BaseEntity
{
public const string TemplateVariableNamePrefix = "partner";
public const int SystemId = 1;

private Partner()
Expand All @@ -29,7 +30,7 @@ public Partner(string name)
}

public string Name { get; set; }

public Dictionary<string,string> AdapterProperties { get; set; }

readonly HashSet<Subscription> _Subscriptions;
public IReadOnlyCollection<Subscription> Subscriptions => _Subscriptions;
Expand All @@ -41,6 +42,7 @@ public void SetApiCredentials(IEnumerable<ApiCredential> apiCredentials)
{
_ApiCredentials.Update(apiCredentials);
}


}
}
7 changes: 7 additions & 0 deletions SW.Bitween.Api/Domain/Subscription/Subscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public Subscription(string name, int documentId, SubscriptionType type, int part
throw new ArgumentException();
}

public Subscription(string name, int documentId, SubscriptionType type): this(WorkGroup.None,name, documentId,
type)
{
Inactive = true;
if (type != SubscriptionType.GatewayApiCall)
throw new ArgumentException();
}
private Subscription(WorkGroup workGroup, string name, int documentId, SubscriptionType type, int? partnerId = null,
int? aggregationForId = null, bool temporary = false)
{
Expand Down
27 changes: 16 additions & 11 deletions SW.Bitween.Api/Domain/Xchange/Xchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ private Xchange()
{
}

public Xchange(int documentId, IWorkGroup workGroup, XchangeFile file, string[] references = null, SubscriptionType subscriptionType = SubscriptionType.Internal, string correlationId = null)
public Xchange(int documentId, IWorkGroup workGroup, XchangeFile file, string[] references = null,
SubscriptionType subscriptionType = SubscriptionType.Internal, string correlationId = null)
{
Id = Guid.NewGuid().ToString("N");
Id = Guid.NewGuid().ToString("N");
DocumentId = documentId;
References = references ?? new string[] { };
InputName = file.Filename;
Expand All @@ -28,6 +29,7 @@ public Xchange(int documentId, IWorkGroup workGroup, XchangeFile file, string[]
//break;
SubscriptionType.Internal => new InternalXchangeCreatedEvent(),
SubscriptionType.ApiCall => new ApiXchangeCreatedEvent(),
SubscriptionType.GatewayApiCall => new ApiXchangeCreatedEvent(),
SubscriptionType.Receiving => new ReceivingXchangeCreatedEvent(),
SubscriptionType.Aggregation => new AggregateXchangeCreatedEvent(),
_ => throw new ArgumentOutOfRangeException(nameof(subscriptionType), subscriptionType, null)
Expand All @@ -38,22 +40,25 @@ public Xchange(int documentId, IWorkGroup workGroup, XchangeFile file, string[]
Events.Add(xchangeEvent);
}

public Xchange(Subscription subscription, XchangeFile file, string[] references = null, string correlationId = null) :
public Xchange(Subscription subscription, XchangeFile file, string[] references = null,
string correlationId = null, Partner gatewayPartner = null,GlobalAdapterValuesSet[] globalAdapterValuesSets = null) :
this(subscription.DocumentId, subscription.WorkGroup, file, references, subscription.Type)
{
SubscriptionId = subscription.Id;
MapperId = subscription.MapperId;
HandlerId = subscription.HandlerId;
MapperProperties = subscription.MapperProperties;
HandlerProperties = subscription.HandlerProperties;
ResponseSubscriptionId = subscription.ResponseSubscriptionId;
ResponseMessageTypeName = subscription.ResponseMessageTypeName;
MapperProperties = subscription.MapperProperties.ToDictionary().Fill(gatewayPartner,globalAdapterValuesSets);
HandlerProperties = subscription.HandlerProperties.ToDictionary()
.Fill(gatewayPartner, globalAdapterValuesSets);
CorrelationId = correlationId;

}

//retry xchange
public Xchange(Xchange xchange, XchangeFile file,IWorkGroup workGroup) :
this(xchange.DocumentId,workGroup, file, xchange.References)
public Xchange(Xchange xchange, XchangeFile file, IWorkGroup workGroup) :
this(xchange.DocumentId, workGroup, file, xchange.References)
{
SubscriptionId = xchange.SubscriptionId;
MapperId = xchange.MapperId;
Expand All @@ -64,9 +69,10 @@ public Xchange(Xchange xchange, XchangeFile file,IWorkGroup workGroup) :
RetryFor = xchange.Id;
CorrelationId = xchange.CorrelationId;
}

//retry with reset subscription properties
public Xchange(Subscription subscription, Xchange xchange, XchangeFile file) :
this(xchange.DocumentId,subscription.WorkGroup, file, xchange.References)
public Xchange(Subscription subscription, Xchange xchange, XchangeFile file) :
this(xchange.DocumentId, subscription.WorkGroup, file, xchange.References)
{
SubscriptionId = xchange.SubscriptionId;
MapperId = subscription.MapperId;
Expand Down Expand Up @@ -95,6 +101,5 @@ public Xchange(Subscription subscription, Xchange xchange, XchangeFile file) :

public string RetryFor { get; private set; }
public string CorrelationId { get; set; }

}
}
}
Loading