From 8ba2910d08f483dff92f823dac87751cd7dc3f60 Mon Sep 17 00:00:00 2001 From: samerz Date: Wed, 11 Feb 2026 14:21:31 +0300 Subject: [PATCH 01/10] native adapters --- .../Resources/Adapters/GetProperties.cs | 15 +- SW.Bitween.Api/Resources/Adapters/Search.cs | 13 +- .../Resources/Adapters/SearchVersioned.cs | 20 +- .../Resources/Subscriptions/Update.cs | 67 ++++- SW.Bitween.Api/SW.Bitween.Api.csproj | 1 + .../Services/NativeAdapterDiscoveryService.cs | 172 ++++++++++++ SW.Bitween.Api/Services/ReceivingService.cs | 106 ++++++- SW.Bitween.Api/Services/XchangeService.cs | 132 +++++++-- .../HttpHandler/HttpHandler.cs | 159 +++++++++++ .../HttpHandler/HttpHandlerInput.cs | 29 ++ .../HttpHandler/HttpHandlerModels.cs | 18 ++ .../SW.Bitween.NativeAdapters.csproj | 15 + .../Services/DictionaryConverter.cs | 157 +++++++++++ SW.Bitween.Web/DEVELOPMENT.md | 263 ++++++++++++++++++ SW.Bitween.Web/SW.Bitween.Web.csproj | 1 + SW.Bitween.Web/Startup.cs | 1 + SW.Bitween.sln | 6 + 17 files changed, 1126 insertions(+), 49 deletions(-) create mode 100644 SW.Bitween.Api/Services/NativeAdapterDiscoveryService.cs create mode 100644 SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs create mode 100644 SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerInput.cs create mode 100644 SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerModels.cs create mode 100644 SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj create mode 100644 SW.Bitween.NativeAdapters/Services/DictionaryConverter.cs create mode 100644 SW.Bitween.Web/DEVELOPMENT.md diff --git a/SW.Bitween.Api/Resources/Adapters/GetProperties.cs b/SW.Bitween.Api/Resources/Adapters/GetProperties.cs index 4974470e..67bb6312 100644 --- a/SW.Bitween.Api/Resources/Adapters/GetProperties.cs +++ b/SW.Bitween.Api/Resources/Adapters/GetProperties.cs @@ -12,15 +12,26 @@ namespace SW.Bitween.Resources.Adapters public class GetProperties : IGetHandler { private readonly IServerlessService serverless; + private readonly NativeAdapterDiscoveryService _nativeAdapterDiscovery; - public GetProperties(IServerlessService serverless) + public GetProperties(IServerlessService serverless, NativeAdapterDiscoveryService nativeAdapterDiscovery) { this.serverless = serverless; + _nativeAdapterDiscovery = nativeAdapterDiscovery; } async public Task Handle(string key) { - await serverless.StartAsync( Uri.UnescapeDataString(key), null); + var decodedKey = Uri.UnescapeDataString(key); + + // Check if it's a native adapter + if (decodedKey.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + return _nativeAdapterDiscovery.GetNativeAdapterProperties(decodedKey); + } + + // Handle serverless adapters + await serverless.StartAsync(decodedKey, null); var expected = await serverless.GetExpectedStartupValues(); return expected.ToList().ToDictionary(k => k.Key, v => $"{v.Key} {(v.Value.Optional ? $" ({v.Value.Default ?? "null"})" : " *")}"); } diff --git a/SW.Bitween.Api/Resources/Adapters/Search.cs b/SW.Bitween.Api/Resources/Adapters/Search.cs index 108afbe9..ee84d76a 100644 --- a/SW.Bitween.Api/Resources/Adapters/Search.cs +++ b/SW.Bitween.Api/Resources/Adapters/Search.cs @@ -10,17 +10,23 @@ public class Search : IQueryHandler { private readonly ServerlessOptions _serverlessOptions; private readonly ICloudFilesService _cloudFilesService; + private readonly NativeAdapterDiscoveryService _nativeAdapterDiscovery; - public Search(ServerlessOptions serverlessOptions, ICloudFilesService cloudFilesService) + public Search(ServerlessOptions serverlessOptions, ICloudFilesService cloudFilesService, + NativeAdapterDiscoveryService nativeAdapterDiscovery) { _serverlessOptions = serverlessOptions; _cloudFilesService = cloudFilesService; + _nativeAdapterDiscovery = nativeAdapterDiscovery; } public async Task Handle(AdapterSearchRequest request) { + // Get native adapters first + var nativeAdapters = _nativeAdapterDiscovery.GetNativeAdapters(request.Prefix).ToList(); + // Get external adapters from storage var cloudFilesList = (await _cloudFilesService.ListAsync( $"{_serverlessOptions.AdapterRemotePath}/infolink6.{request.Prefix}")) @@ -33,10 +39,13 @@ public async Task Handle(AdapterSearchRequest request) return key; }) + .Distinct() .ToList(); + // Combine native (first) and external adapters + var allAdapters = nativeAdapters.Concat(cloudFilesList); - return cloudFilesList.Distinct().ToDictionary(k => k, v => v); + return allAdapters.ToDictionary(k => k, v => v); } } } \ No newline at end of file diff --git a/SW.Bitween.Api/Resources/Adapters/SearchVersioned.cs b/SW.Bitween.Api/Resources/Adapters/SearchVersioned.cs index 3bb3d313..1f4528d4 100644 --- a/SW.Bitween.Api/Resources/Adapters/SearchVersioned.cs +++ b/SW.Bitween.Api/Resources/Adapters/SearchVersioned.cs @@ -12,11 +12,14 @@ public class SearchVersioned : IQueryHandler { private readonly ServerlessOptions _serverlessOptions; private readonly ICloudFilesService _cloudFilesService; + private readonly NativeAdapterDiscoveryService _nativeAdapterDiscovery; - public SearchVersioned(ServerlessOptions serverlessOptions, ICloudFilesService cloudFilesService) + public SearchVersioned(ServerlessOptions serverlessOptions, ICloudFilesService cloudFilesService, + NativeAdapterDiscoveryService nativeAdapterDiscovery) { _serverlessOptions = serverlessOptions; _cloudFilesService = cloudFilesService; + _nativeAdapterDiscovery = nativeAdapterDiscovery; } @@ -24,6 +27,16 @@ public async Task Handle(AdapterSearchRequest request) { var index = _serverlessOptions.AdapterRemotePath.Length + 1; + // Get native adapters first (they don't have versions) + var nativeAdapters = _nativeAdapterDiscovery.GetNativeAdapters(request.Prefix) + .Select(key => new + { + Key = key, + Versions = new List() // Native adapters have no versions + }) + .ToList(); + + // Get external adapters from storage var cloudFilesList = (await _cloudFilesService.ListAsync( $"{_serverlessOptions.AdapterRemotePath}/infolink6.{request.Prefix}")) @@ -40,7 +53,7 @@ public async Task Handle(AdapterSearchRequest request) return key; }); - return grouped.Select(i => new + var externalAdapters = grouped.Select(i => new { i.Key, Versions = i.Where(v => v.Key != i.Key && Semver.IsVersionNumber(v.Key.Split("/").Last())) @@ -49,6 +62,9 @@ public async Task Handle(AdapterSearchRequest request) Key = v.Key[index..] }).ToList() }); + + // Return native adapters first, then external + return nativeAdapters.Concat(externalAdapters); } } } \ No newline at end of file diff --git a/SW.Bitween.Api/Resources/Subscriptions/Update.cs b/SW.Bitween.Api/Resources/Subscriptions/Update.cs index f918b028..ad0b3f83 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Update.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Update.cs @@ -102,10 +102,24 @@ public Validate(IServiceProvider serviceProvider) { RuleFor(i => i.MapperProperties).CustomAsync(async (i, context, ct) => { - var serverless = serviceProvider.GetService(); - await serverless.StartAsync(((SubscriptionUpdate)context.InstanceToValidate).MapperId, null); - var mustProps = (await serverless.GetExpectedStartupValues()) - .Where(p => p.Value.Optional == false).Select(p => p.Key); + var mapperId = ((SubscriptionUpdate)context.InstanceToValidate).MapperId; + var mustProps = Enumerable.Empty(); + + // Check if it's a native adapter + if (mapperId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var nativeAdapterDiscovery = serviceProvider.GetService(); + var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(mapperId); + mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); + } + else + { + var serverless = serviceProvider.GetService(); + await serverless.StartAsync(mapperId, null); + mustProps = (await serverless.GetExpectedStartupValues()) + .Where(p => p.Value.Optional == false).Select(p => p.Key); + } + var missing = mustProps.ToHashSet(StringComparer.OrdinalIgnoreCase) .Except(i.Where(p => !string.IsNullOrEmpty(p.Value)).Select(p => p.Key)); if (missing.Any()) @@ -117,10 +131,24 @@ public Validate(IServiceProvider serviceProvider) { RuleFor(i => i.HandlerProperties).CustomAsync(async (i, context, ct) => { - var serverless = serviceProvider.GetService(); - await serverless.StartAsync(((SubscriptionUpdate)context.InstanceToValidate).HandlerId, null); - var mustProps = (await serverless.GetExpectedStartupValues()) - .Where(p => p.Value.Optional == false).Select(p => p.Key); + var handlerId = ((SubscriptionUpdate)context.InstanceToValidate).HandlerId; + var mustProps = Enumerable.Empty(); + + // Check if it's a native adapter + if (handlerId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var nativeAdapterDiscovery = serviceProvider.GetService(); + var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(handlerId); + mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); + } + else + { + var serverless = serviceProvider.GetService(); + await serverless.StartAsync(handlerId, null); + mustProps = (await serverless.GetExpectedStartupValues()) + .Where(p => p.Value.Optional == false).Select(p => p.Key); + } + var missing = mustProps.ToHashSet(StringComparer.OrdinalIgnoreCase) .Except(i.Where(p => !string.IsNullOrEmpty(p.Value)).Select(p => p.Key)); if (missing.Any()) @@ -137,11 +165,24 @@ public Validate(IServiceProvider serviceProvider) { RuleFor(i => i.ReceiverProperties).CustomAsync(async (i, context, ct) => { - var serverless = serviceProvider.GetService(); - await serverless.StartAsync(((SubscriptionUpdate)context.InstanceToValidate).ReceiverId, - null); - var mustProps = (await serverless.GetExpectedStartupValues()) - .Where(p => p.Value.Optional == false).Select(p => p.Key); + var receiverId = ((SubscriptionUpdate)context.InstanceToValidate).ReceiverId; + var mustProps = Enumerable.Empty(); + + // Check if it's a native adapter + if (receiverId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var nativeAdapterDiscovery = serviceProvider.GetService(); + var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(receiverId); + mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); + } + else + { + var serverless = serviceProvider.GetService(); + await serverless.StartAsync(receiverId, null); + mustProps = (await serverless.GetExpectedStartupValues()) + .Where(p => p.Value.Optional == false).Select(p => p.Key); + } + var missing = mustProps.ToHashSet(StringComparer.OrdinalIgnoreCase) .Except(i.Where(p => !string.IsNullOrEmpty(p.Value)).Select(p => p.Key)); if (missing.Any()) diff --git a/SW.Bitween.Api/SW.Bitween.Api.csproj b/SW.Bitween.Api/SW.Bitween.Api.csproj index 5694d925..98448c29 100644 --- a/SW.Bitween.Api/SW.Bitween.Api.csproj +++ b/SW.Bitween.Api/SW.Bitween.Api.csproj @@ -27,6 +27,7 @@ + diff --git a/SW.Bitween.Api/Services/NativeAdapterDiscoveryService.cs b/SW.Bitween.Api/Services/NativeAdapterDiscoveryService.cs new file mode 100644 index 00000000..6bf15119 --- /dev/null +++ b/SW.Bitween.Api/Services/NativeAdapterDiscoveryService.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using SW.Bitween.NativeAdapters; +using SW.PrimitiveTypes; + +namespace SW.Bitween +{ + public class NativeAdapterDiscoveryService + { + private readonly Dictionary> _adaptersCache; + + public NativeAdapterDiscoveryService() + { + _adaptersCache = new Dictionary>(); + DiscoverNativeAdapters(); + } + + private void DiscoverNativeAdapters() + { + var assemblies = new List() { typeof(DictionaryConverter).Assembly }; + + + + foreach (var assembly in assemblies) + { + try + { + var types = assembly.GetTypes() + .Where(t => t.IsClass && !t.IsAbstract); + + foreach (var type in types) + { + if (typeof(IInfolinkHandler).IsAssignableFrom(type)) + { + AddAdapter("handlers", type); + } + else if (typeof(IInfolinkValidator).IsAssignableFrom(type)) + { + AddAdapter("validators", type); + } + else if (typeof(IInfolinkReceiver).IsAssignableFrom(type)) + { + AddAdapter("receivers", type); + } + } + } + catch + { + // Skip assemblies that can't be loaded or scanned + } + } + } + + private void AddAdapter(string category, Type type) + { + if (!_adaptersCache.ContainsKey(category)) + { + _adaptersCache[category] = new List(); + } + + var adapterName = type.Name;//.Replace("Handler", "").Replace("Mapper", "") + //.Replace("Validator", "").Replace("Receiver", "").ToLower(); + + _adaptersCache[category].Add(new NativeAdapterInfo + { + Key = $"native.{adapterName}", + Name = type.Name, + Type = type, + Category = category + }); + } + + public IEnumerable GetNativeAdapters(string prefix) + { + if (string.IsNullOrEmpty(prefix)) + { + return _adaptersCache.Values.SelectMany(v => v).Select(a => a.Key); + } + + var category = prefix.ToLower().TrimStart('.'); + + if (_adaptersCache.TryGetValue(category, out var adapters)) + { + return adapters.Select(a => a.Key); + } + + return Enumerable.Empty(); + } + + public NativeAdapterInfo GetNativeAdapterInfo(string adapterId) + { + return _adaptersCache.Values + .SelectMany(v => v) + .FirstOrDefault(a => a.Key.Equals(adapterId, StringComparison.OrdinalIgnoreCase)); + } + + public Dictionary GetNativeAdapterProperties(string adapterId) + { + var adapterInfo = GetNativeAdapterInfo(adapterId); + if (adapterInfo == null) + return new Dictionary(); + + var result = new Dictionary(); + + // Get constructor parameters + var constructor = adapterInfo.Type.GetConstructors() + .FirstOrDefault(c => c.GetParameters().Length > 0); + + if (constructor == null) + return result; + + // Get the first parameter type (input model) + var inputParameter = constructor.GetParameters().FirstOrDefault(); + if (inputParameter == null) + return result; + + var inputType = inputParameter.ParameterType; + + // Get all properties from the input model + var properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (var prop in properties) + { + var defaultValue = GetDefaultValue(prop); + var hasRequiredAttribute = prop.GetCustomAttribute() != null; + var isRequired = hasRequiredAttribute || (!IsNullableType(prop.PropertyType) && defaultValue == null); + + if (isRequired) + { + result[prop.Name] = $"{prop.Name} *"; + } + else + { + result[prop.Name] = $"{prop.Name} ({defaultValue ?? "null"})"; + } + } + + return result; + } + + private string? GetDefaultValue(PropertyInfo property) + { + // Try to get default value from DefaultValueAttribute if it exists + var defaultAttr = property.GetCustomAttribute(); + if (defaultAttr != null) + return defaultAttr.Value?.ToString(); + + // For value types, return their default + if (property.PropertyType.IsValueType) + return Activator.CreateInstance(property.PropertyType)?.ToString(); + + return null; + } + + private bool IsNullableType(Type type) + { + return !type.IsValueType || + Nullable.GetUnderlyingType(type) != null || + (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + } + + public class NativeAdapterInfo + { + public string Key { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public Type Type { get; set; } = null!; + public string Category { get; set; } = string.Empty; + } +} diff --git a/SW.Bitween.Api/Services/ReceivingService.cs b/SW.Bitween.Api/Services/ReceivingService.cs index ab0bb40b..684001aa 100644 --- a/SW.Bitween.Api/Services/ReceivingService.cs +++ b/SW.Bitween.Api/Services/ReceivingService.cs @@ -78,26 +78,106 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) async Task RunReceiver(IServiceProvider serviceProvider, string serverlessId, IDictionary startupParameters, int subId) { - var serverless = serviceProvider.GetRequiredService(); - await serverless.StartAsync(serverlessId, null, startupParameters); - await serverless.InvokeAsync(nameof(IInfolinkReceiver.Initialize), null); - var fileList = - (await serverless.InvokeAsync>(nameof(IInfolinkReceiver.ListFiles), null)).ToList(); + // Check if it's a native adapter + if (serverlessId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var nativeAdapterDiscovery = serviceProvider.GetRequiredService(); + var receiver = InstantiateNativeReceiver(nativeAdapterDiscovery, serverlessId, startupParameters); + + await receiver.Initialize(); + var fileList = (await receiver.ListFiles()).ToList(); + + logger.LogInformation($"Subscription:'{subId}' found {fileList.Count()} items for retrieval."); + + foreach (var file in fileList) + { + var xchangeFile = await receiver.GetFile(file); - logger.LogInformation($"Subscription:'{subId}' found {fileList.Count()} items for retrieval."); + logger.LogInformation($"Submitting received file for subscriber: '{subId}'."); - foreach (var file in fileList) + var xchangeService = serviceProvider.GetService(); + await xchangeService.SubmitSubscriptionXchange(subId, xchangeFile); + await receiver.DeleteFile(file); + } + + await receiver.Finalize(); + } + else { - var xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkReceiver.GetFile), file); + // Use serverless for external adapters + var serverless = serviceProvider.GetRequiredService(); + await serverless.StartAsync(serverlessId, null, startupParameters); + await serverless.InvokeAsync(nameof(IInfolinkReceiver.Initialize), null); + var fileList = + (await serverless.InvokeAsync>(nameof(IInfolinkReceiver.ListFiles), null)).ToList(); + + logger.LogInformation($"Subscription:'{subId}' found {fileList.Count()} items for retrieval."); + + foreach (var file in fileList) + { + var xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkReceiver.GetFile), file); + + logger.LogInformation($"Submitting received file for subscriber: '{subId}'."); + + var xchangeService = serviceProvider.GetService(); + await xchangeService.SubmitSubscriptionXchange(subId, xchangeFile); + await serverless.InvokeAsync(nameof(IInfolinkReceiver.DeleteFile), file); + } + + await serverless.InvokeAsync(nameof(IInfolinkReceiver.Finalize), null); + } + } + + private IInfolinkReceiver InstantiateNativeReceiver(NativeAdapterDiscoveryService nativeAdapterDiscovery, + string adapterId, IDictionary properties) + { + var adapterInfo = nativeAdapterDiscovery.GetNativeAdapterInfo(adapterId); + if (adapterInfo == null) + throw new BitweenException($"Native adapter not found: {adapterId}"); + + // Get the constructor that takes a parameter + var constructor = adapterInfo.Type.GetConstructors() + .FirstOrDefault(c => c.GetParameters().Length > 0); + + if (constructor == null) + throw new BitweenException($"Native adapter {adapterId} must have a constructor that accepts an input model"); + + // Get the input parameter type + var inputParameter = constructor.GetParameters().First(); + var inputType = inputParameter.ParameterType; - logger.LogInformation($"Submitting received file for subscriber: '{subId}'."); + // Create an instance of the input model by mapping properties + var inputInstance = Activator.CreateInstance(inputType); - var xchangeService = serviceProvider.GetService(); - await xchangeService.SubmitSubscriptionXchange(subId, xchangeFile); - await serverless.InvokeAsync(nameof(IInfolinkReceiver.DeleteFile), file); + // Map dictionary properties to the input model + foreach (var prop in inputType.GetProperties()) + { + // Case-insensitive property lookup + var propEntry = properties.FirstOrDefault(p => + string.Equals(p.Key, prop.Name, StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(propEntry.Key)) + { + var value = propEntry.Value; + try + { + var convertedValue = Convert.ChangeType(value, + Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); + prop.SetValue(inputInstance, convertedValue); + } + catch + { + // If conversion fails, set string value directly + if (prop.PropertyType == typeof(string)) + prop.SetValue(inputInstance, value); + } + } } - await serverless.InvokeAsync(nameof(IInfolinkReceiver.Finalize), null); + // Instantiate the adapter with the input model + var adapter = Activator.CreateInstance(adapterInfo.Type, inputInstance); + + return (IInfolinkReceiver)adapter; } diff --git a/SW.Bitween.Api/Services/XchangeService.cs b/SW.Bitween.Api/Services/XchangeService.cs index ecf5fc39..b0c058b5 100644 --- a/SW.Bitween.Api/Services/XchangeService.cs +++ b/SW.Bitween.Api/Services/XchangeService.cs @@ -33,16 +33,19 @@ public class XchangeService : private readonly IPublish _publish; private readonly ILogger _logger; private readonly IInfolinkCache _BitweenCache; + private readonly NativeAdapterDiscoveryService _nativeAdapterDiscovery; public XchangeService(BitweenOptions BitweenSettings, BitweenDbContext dbContext, FilterService filterService, ICloudFilesService cloudFiles, IServiceProvider serviceProvider, - IPublish publish, ILogger logger, IInfolinkCache BitweenCache) + IPublish publish, ILogger logger, IInfolinkCache BitweenCache, + NativeAdapterDiscoveryService nativeAdapterDiscovery) { _BitweenSettings = BitweenSettings; _dbContext = dbContext; _filterService = filterService; _cloudFiles = cloudFiles; + _nativeAdapterDiscovery = nativeAdapterDiscovery; _serviceProvider = serviceProvider; _publish = publish; _logger = logger; @@ -125,13 +128,23 @@ private async Task RunMapper(Xchange xchange, XchangeFile xchangeFi { if (xchange.MapperId == null) return xchangeFile; - var serverless = _serviceProvider.GetRequiredService(); - var mapperProperties = xchange.MapperProperties.ToDictionary(); mapperProperties["xchangeid"] = xchange.Id; - await serverless.StartAsync(xchange.MapperId, xchange.CorrelationId ?? xchange.Id, mapperProperties); - xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), xchangeFile); + // Check if it's a native adapter + if (xchange.MapperId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var handler = InstantiateNativeAdapter(xchange.MapperId, mapperProperties); + xchangeFile = await handler.Handle(xchangeFile); + } + else + { + // Use serverless for external adapters + var serverless = _serviceProvider.GetRequiredService(); + await serverless.StartAsync(xchange.MapperId, xchange.CorrelationId ?? xchange.Id, mapperProperties); + xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), xchangeFile); + } + if (xchangeFile is null) throw new BitweenException( $"Unexpected null return value after running mapping for exchange id: {xchange.Id}, adapter id: {xchange.MapperId}"); @@ -145,10 +158,22 @@ public async Task RunValidator(string validatorId, IDictionary p { if (validatorId == null) return; - var serverless = _serviceProvider.GetRequiredService(); - await serverless.StartAsync(validatorId, null, properties); - var result = - await serverless.InvokeAsync(nameof(IInfolinkValidator.Validate), xchangeFile); + InfolinkValidatorResult result; + + // Check if it's a native adapter + if (validatorId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var validator = InstantiateNativeAdapter(validatorId, properties); + result = await validator.Validate(xchangeFile); + } + else + { + // Use serverless for external adapters + var serverless = _serviceProvider.GetRequiredService(); + await serverless.StartAsync(validatorId, null, properties); + result = await serverless.InvokeAsync(nameof(IInfolinkValidator.Validate), xchangeFile); + } + if (!result.Success) throw new SWValidationException(result.Validations); } @@ -157,18 +182,79 @@ private async Task RunHandler(Xchange xchange, XchangeFile xchangeF { if (xchange.HandlerId == null) return null; - var serverless = _serviceProvider.GetRequiredService(); - var handlerProperties = xchange.HandlerProperties.ToDictionary(); handlerProperties["xchangeid"] = xchange.Id; - await serverless.StartAsync(xchange.HandlerId, xchange.CorrelationId ?? xchange.Id, handlerProperties); - xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), xchangeFile); + // Check if it's a native adapter + if (xchange.HandlerId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var handler = InstantiateNativeAdapter(xchange.HandlerId, handlerProperties); + xchangeFile = await handler.Handle(xchangeFile); + } + else + { + // Use serverless for external adapters + var serverless = _serviceProvider.GetRequiredService(); + await serverless.StartAsync(xchange.HandlerId, xchange.CorrelationId ?? xchange.Id, handlerProperties); + xchangeFile = await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), xchangeFile); + } + if (xchangeFile != null) await AddFile(xchange.Id, XchangeFileType.Response, xchangeFile); return xchangeFile; } + private T InstantiateNativeAdapter(string adapterId, IDictionary properties) + { + var adapterInfo = _nativeAdapterDiscovery.GetNativeAdapterInfo(adapterId); + if (adapterInfo == null) + throw new BitweenException($"Native adapter not found: {adapterId}"); + + // Get the constructor that takes a parameter + var constructor = adapterInfo.Type.GetConstructors() + .FirstOrDefault(c => c.GetParameters().Length > 0); + + if (constructor == null) + throw new BitweenException($"Native adapter {adapterId} must have a constructor that accepts an input model"); + + // Get the input parameter type + var inputParameter = constructor.GetParameters().First(); + var inputType = inputParameter.ParameterType; + + // Create an instance of the input model by mapping properties + var inputInstance = Activator.CreateInstance(inputType); + + // Map dictionary properties to the input model + foreach (var prop in inputType.GetProperties()) + { + // Case-insensitive property lookup + var propEntry = properties.FirstOrDefault(p => + string.Equals(p.Key, prop.Name, StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(propEntry.Key)) + { + var value = propEntry.Value; + try + { + var convertedValue = Convert.ChangeType(value, + Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); + prop.SetValue(inputInstance, convertedValue); + } + catch + { + // If conversion fails, set string value directly + if (prop.PropertyType == typeof(string)) + prop.SetValue(inputInstance, value); + } + } + } + + // Instantiate the adapter with the input model + var adapter = Activator.CreateInstance(adapterInfo.Type, inputInstance); + + return (T)adapter; + } + private async Task AddFile(string xchangeId, XchangeFileType type, XchangeFile file) { await _cloudFiles.WriteTextAsync(file.Data, new WriteFileSettings @@ -354,16 +440,28 @@ private async Task NotifyResult(Notifier notifier, XchangeResult xchangeResult, CorrelationId = xchange.CorrelationId }; - var serverless = _serviceProvider.GetRequiredService(); + var handlerProperties = notifier.HandlerProperties.ToDictionary(); handlerProperties["xchangeid"] = xchangeResult.Id; try { - await serverless.StartAsync(notifier.HandlerId, correlationId, handlerProperties); - await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), - new XchangeFile(JsonConvert.SerializeObject(notificationData), xchangeResult.Id)); + // Check if it's a native adapter + if (notifier.HandlerId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + { + var handler = InstantiateNativeAdapter(notifier.HandlerId, handlerProperties); + await handler.Handle(new XchangeFile(JsonConvert.SerializeObject(notificationData), xchangeResult.Id)); + } + else + { + + // Use serverless for external adapters + var serverless = _serviceProvider.GetRequiredService(); + await serverless.StartAsync(notifier.HandlerId, correlationId, handlerProperties); + await serverless.InvokeAsync(nameof(IInfolinkHandler.Handle), + new XchangeFile(JsonConvert.SerializeObject(notificationData), xchangeResult.Id)); + } _dbContext.Add(new XchangeNotification(xchangeResult.Id, notifier.Id, notifier.Name)); } diff --git a/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs new file mode 100644 index 00000000..8f6ae161 --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs @@ -0,0 +1,159 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using DotLiquid; +using Newtonsoft.Json; +using SW.PrimitiveTypes; + +namespace SW.Bitween.NativeAdapters; + +public class HttpHandler : IInfolinkHandler +{ + private HttpMethod HttpMethodFromString(string method) + { + switch (method.ToLower()) + { + case "get": + return HttpMethod.Get; + case "delete": + return HttpMethod.Delete; + case "put": + return HttpMethod.Put; + default: + return HttpMethod.Post; + } + } + + private readonly HttpHandlerInput _options; + + public HttpHandler(HttpHandlerInput options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + } + + public async Task Handle(XchangeFile xchangeFile) + { + HttpClient client = new HttpClient(); + if (_options.AuthType == "ApiKey") + client.DefaultRequestHeaders.Add("ApiKey", _options.ApiKey); + else if (_options.AuthType == "Bearer") + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", _options.LoginPassword); + else if (_options.AuthType == "Basic") + { + string credentials = + Convert.ToBase64String( + Encoding.ASCII.GetBytes(_options.LoginUsername + ":" + _options.LoginPassword)); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); + } + else if (_options.AuthType == "Login") + { + string loginJson = JsonConvert.SerializeObject(new UserLoginModel() + { + Email = _options.LoginUsername, + Password = _options.LoginPassword + }); + HttpResponseMessage loginResponse = await client.PostAsync(new Uri(_options.LoginUrl!), + new StringContent(loginJson, Encoding.UTF8, "application/json")); + loginResponse.EnsureSuccessStatusCode(); + if (loginResponse.StatusCode != HttpStatusCode.OK) + throw new Exception(loginResponse.StatusCode.ToString()); + string rs = await loginResponse.Content.ReadAsStringAsync(); + LoginResponse? rsDeserialized = JsonConvert.DeserializeObject(rs); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", rsDeserialized?.Jwt); + } + else if (_options.AuthType == "OAuth2") + { + var oathRequest = new HttpRequestMessage(HttpMethod.Post, _options.LoginUrl); + var oauthContentDictionary = new List>(); + oauthContentDictionary.Add(new("client_id", _options.ClientId!)); + oauthContentDictionary.Add(new("client_secret", _options.ClientSecret!)); + oauthContentDictionary.Add(new("grant_type", "client_credentials")); + var oauthContent = new FormUrlEncodedContent(oauthContentDictionary); + oathRequest.Content = oauthContent; + var oauthResponse = await client.SendAsync(oathRequest); + var res = await oauthResponse.Content.ReadAsStringAsync(); + var resDeserialized = JsonConvert.DeserializeObject(res); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", resDeserialized?.access_token); + } + + string requestBody = xchangeFile.Data; + if (string.IsNullOrEmpty(requestBody)) + requestBody = _options.DefaultRequest ?? string.Empty; + string str = _options.ContentType.ToLower(); + HttpContent content; + MultipartFormDataContent multipartTmp; + byte[] fileContent; + switch (str) + { + case "application/x-www-form-urlencoded": + content = new FormUrlEncodedContent( + JsonConvert.DeserializeObject>(requestBody) + ?? new Dictionary()); + break; + case "multipart/form-data": + multipartTmp = new MultipartFormDataContent(); + fileContent = Encoding.UTF8.GetBytes(requestBody); + multipartTmp.Add(new ByteArrayContent(fileContent), "file", xchangeFile.Filename ?? "file"); + content = multipartTmp; + break; + case "application/json": + content = new StringContent(requestBody, Encoding.UTF8, "application/json"); + break; + default: + content = new StringContent(requestBody, Encoding.UTF8, _options.ContentType); + break; + } + + Uri uri; + if (!string.IsNullOrEmpty(xchangeFile.Data) && _options.Url.Contains("{{")) + { + Template parsedTemplate = Template.Parse(_options.Url); + IDictionary obj = + JsonConvert.DeserializeObject>(xchangeFile.Data, + new DictionaryConverter()) ?? new Dictionary(); + Hash jsonHash = Hash.FromDictionary(obj); + uri = new Uri(parsedTemplate.Render(jsonHash)); + } + else + uri = new Uri(_options.Url); + + HttpRequestMessage request = new HttpRequestMessage() + { + RequestUri = uri, + Method = HttpMethodFromString(_options.Verb), + Content = content + }; + string? headers1 = _options.Headers; + IEnumerable>? headers = headers1 != null + ? (headers1.Split(',')).Select((Func>)(h => + { + string[] strArray = h.Split(':'); + return new KeyValuePair(strArray[0], strArray[1]); + })) + : null; + if (headers != null) + { + foreach (KeyValuePair keyValuePair1 in headers) + { + KeyValuePair keyValuePair = keyValuePair1; + request.Headers.Add(keyValuePair.Key, keyValuePair.Value); + } + } + + if (!string.IsNullOrEmpty(_options.CorrelationId)) + request.Headers.Add("request-context-correlation-id", _options.CorrelationId); + HttpResponseMessage response = await client.SendAsync(request); + if (response.StatusCode < HttpStatusCode.OK || response.StatusCode >= HttpStatusCode.InternalServerError) + throw new Exception(response.StatusCode.ToString()); + string resp = await response.Content.ReadAsStringAsync(); + XchangeFile xchangeFile1 = response.StatusCode < HttpStatusCode.BadRequest + ? new XchangeFile(resp) + : new XchangeFile(resp, badData: true); + return xchangeFile1; + } + + +} \ No newline at end of file diff --git a/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerInput.cs b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerInput.cs new file mode 100644 index 00000000..c69fc53b --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerInput.cs @@ -0,0 +1,29 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace SW.Bitween.NativeAdapters; + +public class HttpHandlerInput +{ + public string? AuthType { get; set; } + public string? ApiKey { get; set; } + public string? LoginUrl { get; set; } + + [DefaultValue("post")] + public string Verb { get; set; } = "post"; + + public string? LoginUsername { get; set; } + public string? LoginPassword { get; set; } + + [Required] + public string Url { get; set; } = string.Empty; + + [DefaultValue("application/json")] + public string ContentType { get; set; } = "application/json"; + + public string? Headers { get; set; } + public string? CorrelationId { get; set; } + public string? ClientId { get; set; } + public string? ClientSecret { get; set; } + public string? DefaultRequest { get; set; } +} \ No newline at end of file diff --git a/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerModels.cs b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerModels.cs new file mode 100644 index 00000000..623b5059 --- /dev/null +++ b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandlerModels.cs @@ -0,0 +1,18 @@ +namespace SW.Bitween.NativeAdapters; + +public class UserLoginModel +{ + public string? Email { get; set; } + public string? Password { get; set; } +} + +public class LoginResponse +{ + public string? Jwt { get; set; } + public string? Refresh { get; set; } +} + +public class OAuth2Response +{ + public string? access_token { get; set; } +} diff --git a/SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj b/SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj new file mode 100644 index 00000000..f0d60c4b --- /dev/null +++ b/SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/SW.Bitween.NativeAdapters/Services/DictionaryConverter.cs b/SW.Bitween.NativeAdapters/Services/DictionaryConverter.cs new file mode 100644 index 00000000..28edd394 --- /dev/null +++ b/SW.Bitween.NativeAdapters/Services/DictionaryConverter.cs @@ -0,0 +1,157 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace SW.Bitween.NativeAdapters; + +public class DictionaryConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + this.WriteValue(writer, value); + } + + private void WriteValue(JsonWriter writer, object? value) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + var t = JToken.FromObject(value); + switch (t.Type) + { + case JTokenType.Object: + this.WriteObject(writer, value); + break; + case JTokenType.Array: + this.WriteArray(writer, value); + break; + default: + writer.WriteValue(value); + break; + } + } + + private void WriteObject(JsonWriter writer, object value) + { + writer.WriteStartObject(); + var obj = value as IDictionary; + if (obj != null) + { + foreach (var kvp in obj) + { + writer.WritePropertyName(kvp.Key); + this.WriteValue(writer, kvp.Value); + } + } + writer.WriteEndObject(); + } + + private void WriteArray(JsonWriter writer, object value) + { + writer.WriteStartArray(); + var array = value as IEnumerable; + if (array != null) + { + foreach (var o in array) + { + this.WriteValue(writer, o); + } + } + writer.WriteEndArray(); + } + + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + return ReadValue(reader); + } + + private object? ReadValue(JsonReader reader) + { + while (reader.TokenType == JsonToken.Comment) + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected Token when converting IDictionary"); + } + + switch (reader.TokenType) + { + case JsonToken.StartObject: + return ReadObject(reader); + case JsonToken.StartArray: + return this.ReadArray(reader); + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.Bytes: + return reader.Value; + default: + throw new JsonSerializationException( + $"Unexpected token when converting IDictionary: {reader.TokenType}"); + } + } + + private object ReadArray(JsonReader reader) + { + IList list = new List(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + break; + case JsonToken.EndArray: + return list; + default: + var v = ReadValue(reader); + if (v != null) + list.Add(v); + break; + } + } + + throw new JsonSerializationException("Unexpected end when reading IDictionary"); + } + + private object ReadObject(JsonReader reader) + { + var obj = new Dictionary(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + var propertyName = reader.Value?.ToString(); + if (propertyName == null) break; + + if (!reader.Read()) + { + throw new JsonSerializationException("Unexpected end when reading IDictionary"); + } + + var v = ReadValue(reader); + if (v != null) + obj[propertyName] = v; + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + return obj; + } + } + + throw new JsonSerializationException("Unexpected end when reading IDictionary"); + } + + public override bool CanConvert(Type objectType) + { + return typeof(IDictionary).IsAssignableFrom(objectType); + } +} diff --git a/SW.Bitween.Web/DEVELOPMENT.md b/SW.Bitween.Web/DEVELOPMENT.md new file mode 100644 index 00000000..95ada7ab --- /dev/null +++ b/SW.Bitween.Web/DEVELOPMENT.md @@ -0,0 +1,263 @@ +# Bitween Development Configuration Guide + +## Prerequisites + +Before running Bitween in development mode, ensure you have: + +### 1. **PostgreSQL Database** +```bash +# Using Docker +docker run --name bitween-postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=bitween_dev -p 5432:5432 -d postgres:15 + +# Or install locally on macOS +brew install postgresql@15 +brew services start postgresql@15 +createdb bitween_dev +``` + +### 2. **MinIO (S3-Compatible Storage)** +```bash +# Using Docker +docker run --name bitween-minio \ + -p 9000:9000 \ + -p 9001:9001 \ + -e MINIO_ROOT_USER=minioadmin \ + -e MINIO_ROOT_PASSWORD=minioadmin \ + -d minio/minio server /data --console-address ":9001" + +# Access MinIO Console at http://localhost:9001 +# Login: minioadmin / minioadmin +# Create bucket named: bitween-dev +``` + +### 3. **RabbitMQ (Message Bus)** +```bash +# Using Docker +docker run --name bitween-rabbitmq \ + -p 5672:5672 \ + -p 15672:15672 \ + -d rabbitmq:3-management + +# Access RabbitMQ Management UI at http://localhost:15672 +# Login: guest / guest +``` + +## Quick Start with Docker Compose + +Create a `docker-compose.yml` file in the root: + +```yaml +version: '3.8' + +services: + postgres: + image: postgres:15 + container_name: bitween-postgres + environment: + POSTGRES_DB: bitween_dev + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + minio: + image: minio/minio + container_name: bitween-minio + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio_data:/data + + rabbitmq: + image: rabbitmq:3-management + container_name: bitween-rabbitmq + ports: + - "5672:5672" + - "15672:15672" + volumes: + - rabbitmq_data:/var/lib/rabbitmq + +volumes: + postgres_data: + minio_data: + rabbitmq_data: +``` + +Start all services: +```bash +docker-compose up -d +``` + +## Configuration Sections Explained + +### **ConnectionStrings** +- **BitweenDb**: PostgreSQL connection string + - Format: `Host=localhost;Port=5432;Database=bitween_dev;Username=postgres;Password=postgres` + - Change `DatabaseType` in Bitween section if using MySQL or SQL Server + +### **Bitween Section** +Core platform configuration: +- **DatabaseType**: `PgSql` | `MySql` | `MsSql` +- **AdminCredentials**: Format `username:password` for initial admin user +- **StorageProvider**: `S3` | `AS` (Azure Storage) | `OC` (Oracle Cloud) +- **DocumentPrefix**: Path prefix for file storage (e.g., `bitween-dev/documents`) +- **QueuePrefix**: RabbitMQ queue prefix (e.g., `bitween-dev`) +- **JwtExpiryMinutes**: JWT token expiration (1440 = 24 hours) + +### **S3CloudFiles Section** +MinIO/S3 configuration for file storage: +- **ServiceUrl**: MinIO endpoint (http://localhost:9000 for local) +- **AccessKeyId**: MinIO access key (minioadmin) +- **SecretAccessKey**: MinIO secret key (minioadmin) +- **BucketName**: S3 bucket name (bitween-dev) +- **ForcePathStyle**: true for MinIO, false for AWS S3 + +### **Bus Section** +RabbitMQ message bus configuration: +- **ConnectionString**: `amqp://guest:guest@localhost:5672` +- For production, use proper credentials and connection pooling + +### **Serverless Section** +Adapter execution configuration: +- **AdapterRemotePath**: Path in S3 where external adapters are stored +- **CommandTimeout**: Timeout for adapter execution (300 seconds) + +### **JwtTokenParameters** +Authentication configuration: +- **Key**: Must be at least 32 characters +- **Issuer**: Token issuer identifier +- **Audience**: Token audience identifier + +## Database Migration + +After configuration, run database migrations: + +```bash +cd SW.Bitween.Web + +# For PostgreSQL +dotnet ef database update --context PgSql.BitweenDbContext + +# For MySQL +dotnet ef database update --context MySql.BitweenDbContext + +# For SQL Server +dotnet ef database update --context MsSql.BitweenDbContext +``` + +Or use the migration script: +```bash +./migratedb.sh +``` + +## Running Bitween + +```bash +cd SW.Bitween.Web +dotnet run +``` + +The API will be available at: +- HTTP: http://localhost:5000 +- HTTPS: https://localhost:5001 +- Swagger: http://localhost:5000/swagger + +## Default Admin Login + +After first run, log in with: +- **Username**: admin +- **Password**: Admin@123456 + +Change this immediately in production! + +## Troubleshooting + +### Port Already in Use +```bash +# Check what's using the port +lsof -i :5000 +# Kill the process +kill -9 +``` + +### Database Connection Failed +- Ensure PostgreSQL is running: `pg_isready` +- Check connection string in appsettings.Development.json +- Verify database exists: `psql -l` + +### MinIO Connection Failed +- Check MinIO is running: `curl http://localhost:9000/minio/health/live` +- Verify bucket exists via MinIO Console (http://localhost:9001) +- Check ForcePathStyle=true in configuration + +### RabbitMQ Connection Failed +- Check RabbitMQ is running: `curl http://localhost:15672` +- Verify guest user is enabled +- Check firewall/network settings + +## Environment-Specific Settings + +### For MySQL +```json +{ + "ConnectionStrings": { + "BitweenDb": "Server=localhost;Database=bitween_dev;Uid=root;Pwd=password;" + }, + "Bitween": { + "DatabaseType": "MySql" + } +} +``` + +### For SQL Server +```json +{ + "ConnectionStrings": { + "BitweenDb": "Server=localhost;Database=bitween_dev;User Id=sa;Password=YourStrong@Passw0rd;" + }, + "Bitween": { + "DatabaseType": "MsSql" + } +} +``` + +### For Azure Storage +```json +{ + "Bitween": { + "StorageProvider": "AS" + }, + "AzureBlobStorage": { + "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net", + "ContainerName": "bitween-dev" + } +} +``` + +## Production Considerations + +1. **Change default passwords** in all services +2. **Use environment variables** for sensitive data +3. **Enable SSL/TLS** for all connections +4. **Set UseAzureManagedIdentity=true** when running in Azure +5. **Configure proper logging** (ElasticSearch URL in SWLogger section) +6. **Use production-grade message broker** settings +7. **Set AreXChangeFilesPrivate=true** for sensitive data +8. **Reduce BusDefaultQueuePrefetch** in high-load scenarios + +## Next Steps + +1. Start the UI project (Bitween-UI) +2. Create your first Document +3. Configure Partners +4. Set up Subscriptions +5. Deploy external adapters or use native adapters (native.http) + +For more details, see the [full documentation](../docs/getting-started.md). diff --git a/SW.Bitween.Web/SW.Bitween.Web.csproj b/SW.Bitween.Web/SW.Bitween.Web.csproj index 795a599f..b8f2030f 100644 --- a/SW.Bitween.Web/SW.Bitween.Web.csproj +++ b/SW.Bitween.Web/SW.Bitween.Web.csproj @@ -34,6 +34,7 @@ + diff --git a/SW.Bitween.Web/Startup.cs b/SW.Bitween.Web/Startup.cs index 7d62f424..1b136cfe 100644 --- a/SW.Bitween.Web/Startup.cs +++ b/SW.Bitween.Web/Startup.cs @@ -57,6 +57,7 @@ public void ConfigureServices(IServiceCollection services) services.AddMemoryCache(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddScoped(); services.AddHostedService(); diff --git a/SW.Bitween.sln b/SW.Bitween.sln index 2d76a9c1..0c82ab3e 100644 --- a/SW.Bitween.sln +++ b/SW.Bitween.sln @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SW.Bitween.SampleValidator" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SW.Bitween.PgSql", "SW.Bitween.PgSql\SW.Bitween.PgSql.csproj", "{1474658D-E225-478E-80D6-D41A0376F88C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SW.Bitween.NativeAdapters", "SW.Bitween.NativeAdapters\SW.Bitween.NativeAdapters.csproj", "{5D7B6BD7-427E-4F1B-B4CA-CF6B3A3ED89F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,6 +75,10 @@ Global {1474658D-E225-478E-80D6-D41A0376F88C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1474658D-E225-478E-80D6-D41A0376F88C}.Release|Any CPU.ActiveCfg = Release|Any CPU {1474658D-E225-478E-80D6-D41A0376F88C}.Release|Any CPU.Build.0 = Release|Any CPU + {5D7B6BD7-427E-4F1B-B4CA-CF6B3A3ED89F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D7B6BD7-427E-4F1B-B4CA-CF6B3A3ED89F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D7B6BD7-427E-4F1B-B4CA-CF6B3A3ED89F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D7B6BD7-427E-4F1B-B4CA-CF6B3A3ED89F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d9cec1fe4a492ecb47b0e783830dc3e7f450efac Mon Sep 17 00:00:00 2001 From: samerz Date: Wed, 11 Feb 2026 14:22:56 +0300 Subject: [PATCH 02/10] remove md --- SW.Bitween.Web/DEVELOPMENT.md | 263 ---------------------------------- 1 file changed, 263 deletions(-) delete mode 100644 SW.Bitween.Web/DEVELOPMENT.md diff --git a/SW.Bitween.Web/DEVELOPMENT.md b/SW.Bitween.Web/DEVELOPMENT.md deleted file mode 100644 index 95ada7ab..00000000 --- a/SW.Bitween.Web/DEVELOPMENT.md +++ /dev/null @@ -1,263 +0,0 @@ -# Bitween Development Configuration Guide - -## Prerequisites - -Before running Bitween in development mode, ensure you have: - -### 1. **PostgreSQL Database** -```bash -# Using Docker -docker run --name bitween-postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=bitween_dev -p 5432:5432 -d postgres:15 - -# Or install locally on macOS -brew install postgresql@15 -brew services start postgresql@15 -createdb bitween_dev -``` - -### 2. **MinIO (S3-Compatible Storage)** -```bash -# Using Docker -docker run --name bitween-minio \ - -p 9000:9000 \ - -p 9001:9001 \ - -e MINIO_ROOT_USER=minioadmin \ - -e MINIO_ROOT_PASSWORD=minioadmin \ - -d minio/minio server /data --console-address ":9001" - -# Access MinIO Console at http://localhost:9001 -# Login: minioadmin / minioadmin -# Create bucket named: bitween-dev -``` - -### 3. **RabbitMQ (Message Bus)** -```bash -# Using Docker -docker run --name bitween-rabbitmq \ - -p 5672:5672 \ - -p 15672:15672 \ - -d rabbitmq:3-management - -# Access RabbitMQ Management UI at http://localhost:15672 -# Login: guest / guest -``` - -## Quick Start with Docker Compose - -Create a `docker-compose.yml` file in the root: - -```yaml -version: '3.8' - -services: - postgres: - image: postgres:15 - container_name: bitween-postgres - environment: - POSTGRES_DB: bitween_dev - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - ports: - - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data - - minio: - image: minio/minio - container_name: bitween-minio - command: server /data --console-address ":9001" - environment: - MINIO_ROOT_USER: minioadmin - MINIO_ROOT_PASSWORD: minioadmin - ports: - - "9000:9000" - - "9001:9001" - volumes: - - minio_data:/data - - rabbitmq: - image: rabbitmq:3-management - container_name: bitween-rabbitmq - ports: - - "5672:5672" - - "15672:15672" - volumes: - - rabbitmq_data:/var/lib/rabbitmq - -volumes: - postgres_data: - minio_data: - rabbitmq_data: -``` - -Start all services: -```bash -docker-compose up -d -``` - -## Configuration Sections Explained - -### **ConnectionStrings** -- **BitweenDb**: PostgreSQL connection string - - Format: `Host=localhost;Port=5432;Database=bitween_dev;Username=postgres;Password=postgres` - - Change `DatabaseType` in Bitween section if using MySQL or SQL Server - -### **Bitween Section** -Core platform configuration: -- **DatabaseType**: `PgSql` | `MySql` | `MsSql` -- **AdminCredentials**: Format `username:password` for initial admin user -- **StorageProvider**: `S3` | `AS` (Azure Storage) | `OC` (Oracle Cloud) -- **DocumentPrefix**: Path prefix for file storage (e.g., `bitween-dev/documents`) -- **QueuePrefix**: RabbitMQ queue prefix (e.g., `bitween-dev`) -- **JwtExpiryMinutes**: JWT token expiration (1440 = 24 hours) - -### **S3CloudFiles Section** -MinIO/S3 configuration for file storage: -- **ServiceUrl**: MinIO endpoint (http://localhost:9000 for local) -- **AccessKeyId**: MinIO access key (minioadmin) -- **SecretAccessKey**: MinIO secret key (minioadmin) -- **BucketName**: S3 bucket name (bitween-dev) -- **ForcePathStyle**: true for MinIO, false for AWS S3 - -### **Bus Section** -RabbitMQ message bus configuration: -- **ConnectionString**: `amqp://guest:guest@localhost:5672` -- For production, use proper credentials and connection pooling - -### **Serverless Section** -Adapter execution configuration: -- **AdapterRemotePath**: Path in S3 where external adapters are stored -- **CommandTimeout**: Timeout for adapter execution (300 seconds) - -### **JwtTokenParameters** -Authentication configuration: -- **Key**: Must be at least 32 characters -- **Issuer**: Token issuer identifier -- **Audience**: Token audience identifier - -## Database Migration - -After configuration, run database migrations: - -```bash -cd SW.Bitween.Web - -# For PostgreSQL -dotnet ef database update --context PgSql.BitweenDbContext - -# For MySQL -dotnet ef database update --context MySql.BitweenDbContext - -# For SQL Server -dotnet ef database update --context MsSql.BitweenDbContext -``` - -Or use the migration script: -```bash -./migratedb.sh -``` - -## Running Bitween - -```bash -cd SW.Bitween.Web -dotnet run -``` - -The API will be available at: -- HTTP: http://localhost:5000 -- HTTPS: https://localhost:5001 -- Swagger: http://localhost:5000/swagger - -## Default Admin Login - -After first run, log in with: -- **Username**: admin -- **Password**: Admin@123456 - -Change this immediately in production! - -## Troubleshooting - -### Port Already in Use -```bash -# Check what's using the port -lsof -i :5000 -# Kill the process -kill -9 -``` - -### Database Connection Failed -- Ensure PostgreSQL is running: `pg_isready` -- Check connection string in appsettings.Development.json -- Verify database exists: `psql -l` - -### MinIO Connection Failed -- Check MinIO is running: `curl http://localhost:9000/minio/health/live` -- Verify bucket exists via MinIO Console (http://localhost:9001) -- Check ForcePathStyle=true in configuration - -### RabbitMQ Connection Failed -- Check RabbitMQ is running: `curl http://localhost:15672` -- Verify guest user is enabled -- Check firewall/network settings - -## Environment-Specific Settings - -### For MySQL -```json -{ - "ConnectionStrings": { - "BitweenDb": "Server=localhost;Database=bitween_dev;Uid=root;Pwd=password;" - }, - "Bitween": { - "DatabaseType": "MySql" - } -} -``` - -### For SQL Server -```json -{ - "ConnectionStrings": { - "BitweenDb": "Server=localhost;Database=bitween_dev;User Id=sa;Password=YourStrong@Passw0rd;" - }, - "Bitween": { - "DatabaseType": "MsSql" - } -} -``` - -### For Azure Storage -```json -{ - "Bitween": { - "StorageProvider": "AS" - }, - "AzureBlobStorage": { - "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net", - "ContainerName": "bitween-dev" - } -} -``` - -## Production Considerations - -1. **Change default passwords** in all services -2. **Use environment variables** for sensitive data -3. **Enable SSL/TLS** for all connections -4. **Set UseAzureManagedIdentity=true** when running in Azure -5. **Configure proper logging** (ElasticSearch URL in SWLogger section) -6. **Use production-grade message broker** settings -7. **Set AreXChangeFilesPrivate=true** for sensitive data -8. **Reduce BusDefaultQueuePrefetch** in high-load scenarios - -## Next Steps - -1. Start the UI project (Bitween-UI) -2. Create your first Document -3. Configure Partners -4. Set up Subscriptions -5. Deploy external adapters or use native adapters (native.http) - -For more details, see the [full documentation](../docs/getting-started.md). From 14fb13deaf7adc7f69da613e5aa1c8b4238e5900 Mon Sep 17 00:00:00 2001 From: Muhannad Al-Khatib Date: Thu, 12 Feb 2026 18:27:50 +0300 Subject: [PATCH 03/10] api gateway initial --- .../Controllers/GatewayController.cs | 20 + SW.Bitween.Api/Domain/Gateway/ApiGateway.cs | 6 + .../Domain/Gateway/ApiGatewayPartner.cs | 11 + SW.Bitween.Api/Helpers/StartupValuesFiller.cs | 41 + .../Resources/ApiGateways/AddPartner.cs | 0 .../Resources/ApiGateways/Create.cs | 0 .../Resources/ApiGateways/Delete.cs | 0 SW.Bitween.Api/Resources/ApiGateways/Get.cs | 0 .../Resources/ApiGateways/RemovePartner.cs | 0 .../Resources/ApiGateways/Search.cs | 0 .../Resources/ApiGateways/Update.cs | 0 .../Resources/ApiGateways/UpdatePartner.cs | 0 .../20260211120500_AddApiGateway.cs | 0 .../20260211121000_AddApiGateway.cs | 0 .../20260211130000_AddApiGateway.cs | 0 .../20260211120500_AddApiGateway.cs | 0 .../20260211121000_AddApiGateway.cs | 0 .../20260211130000_AddApiGateway.cs | 0 .../20260211120440_AddApiGateway.Designer.cs | 1273 +++++++++++++++++ .../20260211120440_AddApiGateway.cs | 129 ++ .../20260211121000_AddApiGateway.cs | 0 .../20260211121819_AddApiGateway.Designer.cs | 1272 ++++++++++++++++ .../20260211121819_AddApiGateway.cs | 123 ++ .../20260211130000_AddApiGateway.cs | 0 SW.Bitween.Sdk/Model/ApiGateway.cs | 0 25 files changed, 2875 insertions(+) create mode 100644 SW.Bitween.Api/Controllers/GatewayController.cs create mode 100644 SW.Bitween.Api/Domain/Gateway/ApiGateway.cs create mode 100644 SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs create mode 100644 SW.Bitween.Api/Helpers/StartupValuesFiller.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/Create.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/Delete.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/Get.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/Search.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/Update.cs create mode 100644 SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs create mode 100644 SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs create mode 100644 SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs create mode 100644 SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs create mode 100644 SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs create mode 100644 SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs create mode 100644 SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs create mode 100644 SW.Bitween.Sdk/Model/ApiGateway.cs diff --git a/SW.Bitween.Api/Controllers/GatewayController.cs b/SW.Bitween.Api/Controllers/GatewayController.cs new file mode 100644 index 00000000..20b126b6 --- /dev/null +++ b/SW.Bitween.Api/Controllers/GatewayController.cs @@ -0,0 +1,20 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace SW.Bitween.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class GatewayController: ControllerBase +{ + + [HttpPost("{gatewayApiName}")] + public async Task Post([FromRoute] string gatewayApiName) + { + + var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); + + return Ok(); + } +} \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs new file mode 100644 index 00000000..c5d86081 --- /dev/null +++ b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs @@ -0,0 +1,6 @@ +namespace SW.Bitween.Domain.Gateway; + +public class ApiGateway +{ + +} \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs new file mode 100644 index 00000000..544822c8 --- /dev/null +++ b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs @@ -0,0 +1,11 @@ +namespace SW.Bitween.Domain.Gateway; + +public class ApiGatewayPartner +{ + 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; } +} \ No newline at end of file diff --git a/SW.Bitween.Api/Helpers/StartupValuesFiller.cs b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs new file mode 100644 index 00000000..af803dd4 --- /dev/null +++ b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SW.Bitween; + +public static class StartupValuesFiller +{ + //{{partner.XY}} => input["XY"] + public static Dictionary Fill(this IDictionary inputTemplated, + Dictionary input, string variableNamePrefix) + { + var result = new Dictionary(); + var prefix = $"{{{{{variableNamePrefix}."; // {{partner. + + foreach (var kvp in inputTemplated) + { + var value = kvp.Value; + + // Check if value is a template like {{partner.XY}} + if (value != null && value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && value.EndsWith("}}")) + { + // Extract the variable name (e.g., "XY" from "{{partner.XY}}") + var variableName = value.Substring(prefix.Length, value.Length - prefix.Length - 2); + + // Look up in input dictionary (case-insensitive) + var inputValue = input.FirstOrDefault(i => + i.Key.Equals(variableName, StringComparison.OrdinalIgnoreCase)).Value; + + result[kvp.Key] = inputValue ?? value; // Use original if not found + } + else + { + // Keep the original value if it's not a template + result[kvp.Key] = value; + } + } + + return result; + } +} \ No newline at end of file diff --git a/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs b/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/Create.cs b/SW.Bitween.Api/Resources/ApiGateways/Create.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/Delete.cs b/SW.Bitween.Api/Resources/ApiGateways/Delete.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/Get.cs b/SW.Bitween.Api/Resources/ApiGateways/Get.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/Search.cs b/SW.Bitween.Api/Resources/ApiGateways/Search.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/Update.cs b/SW.Bitween.Api/Resources/ApiGateways/Update.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs new file mode 100644 index 00000000..77ff6a17 --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs @@ -0,0 +1,1273 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SW.Bitween.Model; +using SW.Bitween.PgSql; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + [DbContext(typeof(BitweenDbContext))] + [Migration("20260211120440_AddApiGateway")] + partial class AddApiGateway + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("infolink") + .HasAnnotation("ProductVersion", "8.0.23") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("Disabled") + .HasColumnType("boolean") + .HasColumnName("disabled"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("display_name"); + + b.Property("Email") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("email"); + + b.Property("EmailProvider") + .HasColumnType("smallint") + .HasColumnName("email_provider"); + + b.Property("LoginMethods") + .HasColumnType("smallint") + .HasColumnName("login_methods"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.Property("Password") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("character varying(500)") + .HasColumnName("password"); + + b.Property("Phone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character varying(20)") + .HasColumnName("phone"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.HasKey("Id") + .HasName("pk_accounts"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_accounts_email"); + + b.ToTable("Accounts", "infolink"); + + b.HasData( + new + { + Id = 9999, + CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), + Deleted = false, + Disabled = false, + DisplayName = "Admin", + Email = "admin@Bitween.systems", + EmailProvider = (byte)0, + LoginMethods = (byte)2, + Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", + Role = 0 + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("integer") + .HasColumnName("account_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("LoginMethod") + .HasColumnType("smallint") + .HasColumnName("login_method"); + + b.HasKey("Id") + .HasName("pk_refresh_tokens"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_refresh_tokens_account_id"); + + b.ToTable("RefreshTokens", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Document", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("BusEnabled") + .HasColumnType("boolean") + .HasColumnName("bus_enabled"); + + b.Property("BusMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("bus_message_type_name"); + + b.Property("DisregardsUnfilteredMessages") + .HasColumnType("boolean") + .HasColumnName("disregards_unfiltered_messages"); + + b.Property("DocumentFormat") + .HasColumnType("integer") + .HasColumnName("document_format"); + + b.Property("DuplicateInterval") + .HasColumnType("integer") + .HasColumnName("duplicate_interval"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PromotedProperties") + .HasColumnType("jsonb") + .HasColumnName("promoted_properties"); + + b.HasKey("Id") + .HasName("pk_document"); + + b.HasIndex("BusMessageTypeName") + .IsUnique() + .HasDatabaseName("ix_document_bus_message_type_name"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_document_name"); + + b.ToTable("document", "infolink"); + + b.HasData( + new + { + Id = 10001, + BusEnabled = false, + DocumentFormat = 0, + DuplicateInterval = 0, + Name = "Aggregation Document", + PromotedProperties = "{}" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.HasKey("Id") + .HasName("pk_document_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_document_trail_created_on"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_document_trail_document_id"); + + b.ToTable("document_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_api_gateway"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_subscription_id"); + + b.ToTable("api_gateway", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("integer") + .HasColumnName("api_gateway_id"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") + .HasName("pk_api_gateway_partner"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_api_gateway_partner_partner_id"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_partner_subscription_id"); + + b.ToTable("api_gateway_partner", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property("HandlerProperties") + .HasColumnType("text") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("RunOnBadResult") + .HasColumnType("boolean") + .HasColumnName("run_on_bad_result"); + + b.Property("RunOnFailedResult") + .HasColumnType("boolean") + .HasColumnName("run_on_failed_result"); + + b.Property("RunOnSubscriptions") + .HasColumnType("integer[]") + .HasColumnName("run_on_subscriptions"); + + b.Property("RunOnSuccessfulResult") + .HasColumnType("boolean") + .HasColumnName("run_on_successful_result"); + + b.HasKey("Id") + .HasName("pk_notifier"); + + b.ToTable("notifier", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BadData") + .HasColumnType("boolean") + .HasColumnName("bad_data"); + + b.Property("Data") + .HasColumnType("text") + .HasColumnName("data"); + + b.Property("FileName") + .HasColumnType("text") + .HasColumnName("file_name"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_on_hold_xchange"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_on_hold_xchange_subscription_id"); + + b.ToTable("on_hold_xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property>("AdditionalValues") + .HasColumnType("hstore") + .HasColumnName("additional_values"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_partner"); + + b.ToTable("partner", "infolink"); + + b.HasData( + new + { + Id = 1, + Name = "SYSTEM" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AggregateOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregate_on"); + + b.Property("AggregationForId") + .HasColumnType("integer") + .HasColumnName("aggregation_for_id"); + + b.Property("AggregationTarget") + .HasColumnType("smallint") + .HasColumnName("aggregation_target"); + + b.Property("CategoryId") + .HasColumnType("integer") + .HasColumnName("category_id"); + + b.Property("ConsecutiveFailures") + .HasColumnType("integer") + .HasColumnName("consecutive_failures"); + + b.Property>("DocumentFilter") + .HasColumnType("jsonb") + .HasColumnName("document_filter"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.Property("LastException") + .HasColumnType("text") + .HasColumnName("last_exception"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("MatchExpression") + .HasColumnType("text") + .HasColumnName("match_expression"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("PausedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("paused_on"); + + b.Property("ReceiveOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("receive_on"); + + b.Property("ReceiverId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("receiver_id"); + + b.Property>("ReceiverProperties") + .HasColumnType("jsonb") + .HasColumnName("receiver_properties"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("Temporary") + .HasColumnType("boolean") + .HasColumnName("temporary"); + + b.Property("Type") + .HasColumnType("smallint") + .HasColumnName("type"); + + b.Property("ValidatorId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("validator_id"); + + b.Property>("ValidatorProperties") + .HasColumnType("jsonb") + .HasColumnName("validator_properties"); + + b.Property("WorkGroupId") + .HasColumnType("integer") + .HasColumnName("work_group_id"); + + b.HasKey("Id") + .HasName("pk_subscription"); + + b.HasIndex("AggregationForId") + .HasDatabaseName("ix_subscription_aggregation_for_id"); + + b.HasIndex("CategoryId") + .HasDatabaseName("ix_subscription_category_id"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_subscription_document_id"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_subscription_partner_id"); + + b.HasIndex("ResponseSubscriptionId") + .HasDatabaseName("ix_subscription_response_subscription_id"); + + b.HasIndex("WorkGroupId") + .HasDatabaseName("ix_subscription_work_group_id"); + + b.ToTable("subscription", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .HasColumnType("text") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.HasKey("Id") + .HasName("pk_subscription_category"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_subscription_category_code"); + + b.ToTable("subscription_category", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_subscription_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_subscription_trail_created_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_subscription_trail_subscription_id"); + + b.ToTable("subscription_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BusMessageName") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("character varying(100)") + .HasColumnName("bus_message_name"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Options") + .HasColumnType("jsonb") + .HasColumnName("options"); + + b.HasKey("Id") + .HasName("pk_work_group"); + + b.ToTable("work_group", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("CorrelationId") + .HasColumnType("text") + .HasColumnName("correlation_id"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("InputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_content_type"); + + b.Property("InputHash") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("input_hash"); + + b.Property("InputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_name"); + + b.Property("InputSize") + .HasColumnType("integer") + .HasColumnName("input_size"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("RetryFor") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("retry_for"); + + b.Property("StartedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_on"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_xchange"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_xchange_document_id"); + + b.HasIndex("InputHash") + .HasDatabaseName("ix_xchange_input_hash"); + + b.HasIndex("RetryFor") + .HasDatabaseName("ix_xchange_retry_for"); + + b.HasIndex("StartedOn") + .HasDatabaseName("ix_xchange_started_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_xchange_subscription_id"); + + b.ToTable("xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AggregatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregated_on"); + + b.Property("AggregationXchangeId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("aggregation_xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_aggregation"); + + b.HasIndex("AggregationXchangeId") + .HasDatabaseName("ix_xchange_aggregation_aggregation_xchange_id"); + + b.ToTable("xchange_aggregation", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("DeliveredOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("delivered_on"); + + b.HasKey("Id") + .HasName("pk_xchange_delivery"); + + b.HasIndex("DeliveredOn") + .HasDatabaseName("ix_xchange_delivery_delivered_on"); + + b.ToTable("xchange_delivery", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("NotifierId") + .HasColumnType("integer") + .HasColumnName("notifier_id"); + + b.Property("NotifierName") + .HasColumnType("text") + .HasColumnName("notifier_name"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.Property("XchangeId") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_notification"); + + b.ToTable("xchange_notification", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Hits") + .HasColumnType("integer[]") + .HasColumnName("hits"); + + b.Property>("Properties") + .HasColumnType("jsonb") + .HasColumnName("properties"); + + b.Property("PropertiesRaw") + .HasColumnType("text") + .HasColumnName("properties_raw"); + + b.HasKey("Id") + .HasName("pk_xchange_promoted_properties"); + + b.HasIndex("PropertiesRaw") + .HasDatabaseName("ix_xchange_promoted_properties_properties_raw"); + + b.ToTable("xchange_promoted_properties", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("OutputBad") + .HasColumnType("boolean") + .HasColumnName("output_bad"); + + b.Property("OutputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_content_type"); + + b.Property("OutputHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("output_hash"); + + b.Property("OutputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_name"); + + b.Property("OutputSize") + .HasColumnType("integer") + .HasColumnName("output_size"); + + b.Property("ResponseBad") + .HasColumnType("boolean") + .HasColumnName("response_bad"); + + b.Property("ResponseContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_content_type"); + + b.Property("ResponseHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("response_hash"); + + b.Property("ResponseName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_name"); + + b.Property("ResponseSize") + .HasColumnType("integer") + .HasColumnName("response_size"); + + b.Property("ResponseXchangeId") + .HasColumnType("text") + .HasColumnName("response_xchange_id"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_xchange_result"); + + b.ToTable("xchange_result", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => + { + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.ToTable("running_result", "infolink"); + + b.ToView(null, (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.HasOne("SW.Bitween.Domain.Accounts.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_refresh_tokens_accounts_account_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.HasOne("SW.Bitween.Domain.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_document_trail_document_document_id"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => + { + b1.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Key") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("key"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("name"); + + b1.HasKey("PartnerId", "Id") + .HasName("pk_partner_api_credential"); + + b1.HasIndex("Key") + .IsUnique() + .HasDatabaseName("ix_partner_api_credential_key"); + + b1.ToTable("partner_api_credential", "infolink"); + + b1.WithOwner() + .HasForeignKey("PartnerId") + .HasConstraintName("fk_partner_api_credential_partner_partner_id"); + + b1.HasData( + new + { + PartnerId = 1, + Id = 1, + Key = "7facc758283844b49cc4ffd26a75b1de", + Name = "default" + }); + }); + + b.Navigation("ApiCredentials"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("AggregationForId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_aggregation_for"); + + b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .HasConstraintName("fk_subscription_subscription_category_category_id"); + + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_subscription_document_document_id"); + + b.HasOne("SW.Bitween.Domain.Partner", null) + .WithMany("Subscriptions") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("ResponseSubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_response_subscriber"); + + b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") + .WithMany() + .HasForeignKey("WorkGroupId") + .HasConstraintName("fk_subscription_work_group_work_group_id"); + + b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => + { + b1.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Backwards") + .HasColumnType("boolean") + .HasColumnName("backwards"); + + b1.Property("On") + .HasColumnType("bigint") + .HasColumnName("on"); + + b1.Property("Recurrence") + .HasColumnType("smallint") + .HasColumnName("recurrence"); + + b1.HasKey("SubscriptionId", "Id") + .HasName("pk_subscription_schedule"); + + b1.ToTable("subscription_schedule", "infolink"); + + b1.WithOwner() + .HasForeignKey("SubscriptionId") + .HasConstraintName("fk_subscription_schedule_subscription_subscription_id"); + }); + + b.Navigation("Category"); + + b.Navigation("Schedules"); + + b.Navigation("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_subscription_trail_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_xchange_document_document_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_aggregation_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_delivery_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_promoted_properties_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_result_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs new file mode 100644 index 00000000..8f6c9414 --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + /// + public partial class AddApiGateway : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:hstore", ",,"); + + migrationBuilder.AddColumn>( + name: "additional_values", + schema: "infolink", + table: "partner", + type: "hstore", + nullable: true); + + migrationBuilder.CreateTable( + name: "api_gateway", + schema: "infolink", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + subscription_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway", x => x.id); + table.ForeignKey( + name: "fk_api_gateway_subscription_subscription_id", + column: x => x.subscription_id, + principalSchema: "infolink", + principalTable: "subscription", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "api_gateway_partner", + schema: "infolink", + columns: table => new + { + api_gateway_id = table.Column(type: "integer", nullable: false), + partner_id = table.Column(type: "integer", nullable: false), + subscription_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway_partner", x => new { x.api_gateway_id, x.partner_id, x.subscription_id }); + table.ForeignKey( + name: "fk_api_gateway_partner_api_gateway_api_gateway_id", + column: x => x.api_gateway_id, + principalSchema: "infolink", + principalTable: "api_gateway", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_partner_partner_id", + column: x => x.partner_id, + principalSchema: "infolink", + principalTable: "partner", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_subscription_subscription_id", + column: x => x.subscription_id, + principalSchema: "infolink", + principalTable: "subscription", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.UpdateData( + schema: "infolink", + table: "partner", + keyColumn: "id", + keyValue: 1, + column: "additional_values", + value: null); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_subscription_id", + schema: "infolink", + table: "api_gateway", + column: "subscription_id"); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_partner_id", + schema: "infolink", + table: "api_gateway_partner", + column: "partner_id"); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_subscription_id", + schema: "infolink", + table: "api_gateway_partner", + column: "subscription_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "api_gateway_partner", + schema: "infolink"); + + migrationBuilder.DropTable( + name: "api_gateway", + schema: "infolink"); + + migrationBuilder.DropColumn( + name: "additional_values", + schema: "infolink", + table: "partner"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:PostgresExtension:hstore", ",,"); + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs new file mode 100644 index 00000000..b803c76d --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs @@ -0,0 +1,1272 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SW.Bitween.Model; +using SW.Bitween.PgSql; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + [DbContext(typeof(BitweenDbContext))] + [Migration("20260211121819_AddApiGateway")] + partial class AddApiGateway + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("infolink") + .HasAnnotation("ProductVersion", "8.0.23") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("Disabled") + .HasColumnType("boolean") + .HasColumnName("disabled"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("display_name"); + + b.Property("Email") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("email"); + + b.Property("EmailProvider") + .HasColumnType("smallint") + .HasColumnName("email_provider"); + + b.Property("LoginMethods") + .HasColumnType("smallint") + .HasColumnName("login_methods"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.Property("Password") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("character varying(500)") + .HasColumnName("password"); + + b.Property("Phone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character varying(20)") + .HasColumnName("phone"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.HasKey("Id") + .HasName("pk_accounts"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_accounts_email"); + + b.ToTable("Accounts", "infolink"); + + b.HasData( + new + { + Id = 9999, + CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), + Deleted = false, + Disabled = false, + DisplayName = "Admin", + Email = "admin@Bitween.systems", + EmailProvider = (byte)0, + LoginMethods = (byte)2, + Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", + Role = 0 + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("integer") + .HasColumnName("account_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("LoginMethod") + .HasColumnType("smallint") + .HasColumnName("login_method"); + + b.HasKey("Id") + .HasName("pk_refresh_tokens"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_refresh_tokens_account_id"); + + b.ToTable("RefreshTokens", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Document", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("BusEnabled") + .HasColumnType("boolean") + .HasColumnName("bus_enabled"); + + b.Property("BusMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("bus_message_type_name"); + + b.Property("DisregardsUnfilteredMessages") + .HasColumnType("boolean") + .HasColumnName("disregards_unfiltered_messages"); + + b.Property("DocumentFormat") + .HasColumnType("integer") + .HasColumnName("document_format"); + + b.Property("DuplicateInterval") + .HasColumnType("integer") + .HasColumnName("duplicate_interval"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PromotedProperties") + .HasColumnType("jsonb") + .HasColumnName("promoted_properties"); + + b.HasKey("Id") + .HasName("pk_document"); + + b.HasIndex("BusMessageTypeName") + .IsUnique() + .HasDatabaseName("ix_document_bus_message_type_name"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_document_name"); + + b.ToTable("document", "infolink"); + + b.HasData( + new + { + Id = 10001, + BusEnabled = false, + DocumentFormat = 0, + DuplicateInterval = 0, + Name = "Aggregation Document", + PromotedProperties = "{}" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.HasKey("Id") + .HasName("pk_document_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_document_trail_created_on"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_document_trail_document_id"); + + b.ToTable("document_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_api_gateway"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_subscription_id"); + + b.ToTable("api_gateway", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("integer") + .HasColumnName("api_gateway_id"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") + .HasName("pk_api_gateway_partner"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_api_gateway_partner_partner_id"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_partner_subscription_id"); + + b.ToTable("api_gateway_partner", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property("HandlerProperties") + .HasColumnType("text") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("RunOnBadResult") + .HasColumnType("boolean") + .HasColumnName("run_on_bad_result"); + + b.Property("RunOnFailedResult") + .HasColumnType("boolean") + .HasColumnName("run_on_failed_result"); + + b.Property("RunOnSubscriptions") + .HasColumnType("integer[]") + .HasColumnName("run_on_subscriptions"); + + b.Property("RunOnSuccessfulResult") + .HasColumnType("boolean") + .HasColumnName("run_on_successful_result"); + + b.HasKey("Id") + .HasName("pk_notifier"); + + b.ToTable("notifier", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BadData") + .HasColumnType("boolean") + .HasColumnName("bad_data"); + + b.Property("Data") + .HasColumnType("text") + .HasColumnName("data"); + + b.Property("FileName") + .HasColumnType("text") + .HasColumnName("file_name"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_on_hold_xchange"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_on_hold_xchange_subscription_id"); + + b.ToTable("on_hold_xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property>("AdditionalValues") + .HasColumnType("jsonb") + .HasColumnName("additional_values"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_partner"); + + b.ToTable("partner", "infolink"); + + b.HasData( + new + { + Id = 1, + Name = "SYSTEM" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AggregateOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregate_on"); + + b.Property("AggregationForId") + .HasColumnType("integer") + .HasColumnName("aggregation_for_id"); + + b.Property("AggregationTarget") + .HasColumnType("smallint") + .HasColumnName("aggregation_target"); + + b.Property("CategoryId") + .HasColumnType("integer") + .HasColumnName("category_id"); + + b.Property("ConsecutiveFailures") + .HasColumnType("integer") + .HasColumnName("consecutive_failures"); + + b.Property>("DocumentFilter") + .HasColumnType("jsonb") + .HasColumnName("document_filter"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.Property("LastException") + .HasColumnType("text") + .HasColumnName("last_exception"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("MatchExpression") + .HasColumnType("text") + .HasColumnName("match_expression"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("PausedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("paused_on"); + + b.Property("ReceiveOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("receive_on"); + + b.Property("ReceiverId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("receiver_id"); + + b.Property>("ReceiverProperties") + .HasColumnType("jsonb") + .HasColumnName("receiver_properties"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("Temporary") + .HasColumnType("boolean") + .HasColumnName("temporary"); + + b.Property("Type") + .HasColumnType("smallint") + .HasColumnName("type"); + + b.Property("ValidatorId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("validator_id"); + + b.Property>("ValidatorProperties") + .HasColumnType("jsonb") + .HasColumnName("validator_properties"); + + b.Property("WorkGroupId") + .HasColumnType("integer") + .HasColumnName("work_group_id"); + + b.HasKey("Id") + .HasName("pk_subscription"); + + b.HasIndex("AggregationForId") + .HasDatabaseName("ix_subscription_aggregation_for_id"); + + b.HasIndex("CategoryId") + .HasDatabaseName("ix_subscription_category_id"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_subscription_document_id"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_subscription_partner_id"); + + b.HasIndex("ResponseSubscriptionId") + .HasDatabaseName("ix_subscription_response_subscription_id"); + + b.HasIndex("WorkGroupId") + .HasDatabaseName("ix_subscription_work_group_id"); + + b.ToTable("subscription", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .HasColumnType("text") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.HasKey("Id") + .HasName("pk_subscription_category"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_subscription_category_code"); + + b.ToTable("subscription_category", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_subscription_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_subscription_trail_created_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_subscription_trail_subscription_id"); + + b.ToTable("subscription_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BusMessageName") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("character varying(100)") + .HasColumnName("bus_message_name"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Options") + .HasColumnType("jsonb") + .HasColumnName("options"); + + b.HasKey("Id") + .HasName("pk_work_group"); + + b.ToTable("work_group", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("CorrelationId") + .HasColumnType("text") + .HasColumnName("correlation_id"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("InputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_content_type"); + + b.Property("InputHash") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("input_hash"); + + b.Property("InputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_name"); + + b.Property("InputSize") + .HasColumnType("integer") + .HasColumnName("input_size"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("RetryFor") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("retry_for"); + + b.Property("StartedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_on"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_xchange"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_xchange_document_id"); + + b.HasIndex("InputHash") + .HasDatabaseName("ix_xchange_input_hash"); + + b.HasIndex("RetryFor") + .HasDatabaseName("ix_xchange_retry_for"); + + b.HasIndex("StartedOn") + .HasDatabaseName("ix_xchange_started_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_xchange_subscription_id"); + + b.ToTable("xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AggregatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregated_on"); + + b.Property("AggregationXchangeId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("aggregation_xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_aggregation"); + + b.HasIndex("AggregationXchangeId") + .HasDatabaseName("ix_xchange_aggregation_aggregation_xchange_id"); + + b.ToTable("xchange_aggregation", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("DeliveredOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("delivered_on"); + + b.HasKey("Id") + .HasName("pk_xchange_delivery"); + + b.HasIndex("DeliveredOn") + .HasDatabaseName("ix_xchange_delivery_delivered_on"); + + b.ToTable("xchange_delivery", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("NotifierId") + .HasColumnType("integer") + .HasColumnName("notifier_id"); + + b.Property("NotifierName") + .HasColumnType("text") + .HasColumnName("notifier_name"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.Property("XchangeId") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_notification"); + + b.ToTable("xchange_notification", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Hits") + .HasColumnType("integer[]") + .HasColumnName("hits"); + + b.Property>("Properties") + .HasColumnType("jsonb") + .HasColumnName("properties"); + + b.Property("PropertiesRaw") + .HasColumnType("text") + .HasColumnName("properties_raw"); + + b.HasKey("Id") + .HasName("pk_xchange_promoted_properties"); + + b.HasIndex("PropertiesRaw") + .HasDatabaseName("ix_xchange_promoted_properties_properties_raw"); + + b.ToTable("xchange_promoted_properties", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("OutputBad") + .HasColumnType("boolean") + .HasColumnName("output_bad"); + + b.Property("OutputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_content_type"); + + b.Property("OutputHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("output_hash"); + + b.Property("OutputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_name"); + + b.Property("OutputSize") + .HasColumnType("integer") + .HasColumnName("output_size"); + + b.Property("ResponseBad") + .HasColumnType("boolean") + .HasColumnName("response_bad"); + + b.Property("ResponseContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_content_type"); + + b.Property("ResponseHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("response_hash"); + + b.Property("ResponseName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_name"); + + b.Property("ResponseSize") + .HasColumnType("integer") + .HasColumnName("response_size"); + + b.Property("ResponseXchangeId") + .HasColumnType("text") + .HasColumnName("response_xchange_id"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_xchange_result"); + + b.ToTable("xchange_result", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => + { + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.ToTable("running_result", "infolink"); + + b.ToView(null, (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.HasOne("SW.Bitween.Domain.Accounts.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_refresh_tokens_accounts_account_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.HasOne("SW.Bitween.Domain.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_document_trail_document_document_id"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => + { + b1.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Key") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("key"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("name"); + + b1.HasKey("PartnerId", "Id") + .HasName("pk_partner_api_credential"); + + b1.HasIndex("Key") + .IsUnique() + .HasDatabaseName("ix_partner_api_credential_key"); + + b1.ToTable("partner_api_credential", "infolink"); + + b1.WithOwner() + .HasForeignKey("PartnerId") + .HasConstraintName("fk_partner_api_credential_partner_partner_id"); + + b1.HasData( + new + { + PartnerId = 1, + Id = 1, + Key = "7facc758283844b49cc4ffd26a75b1de", + Name = "default" + }); + }); + + b.Navigation("ApiCredentials"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("AggregationForId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_aggregation_for"); + + b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .HasConstraintName("fk_subscription_subscription_category_category_id"); + + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_subscription_document_document_id"); + + b.HasOne("SW.Bitween.Domain.Partner", null) + .WithMany("Subscriptions") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("ResponseSubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_response_subscriber"); + + b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") + .WithMany() + .HasForeignKey("WorkGroupId") + .HasConstraintName("fk_subscription_work_group_work_group_id"); + + b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => + { + b1.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Backwards") + .HasColumnType("boolean") + .HasColumnName("backwards"); + + b1.Property("On") + .HasColumnType("bigint") + .HasColumnName("on"); + + b1.Property("Recurrence") + .HasColumnType("smallint") + .HasColumnName("recurrence"); + + b1.HasKey("SubscriptionId", "Id") + .HasName("pk_subscription_schedule"); + + b1.ToTable("subscription_schedule", "infolink"); + + b1.WithOwner() + .HasForeignKey("SubscriptionId") + .HasConstraintName("fk_subscription_schedule_subscription_subscription_id"); + }); + + b.Navigation("Category"); + + b.Navigation("Schedules"); + + b.Navigation("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_subscription_trail_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_xchange_document_document_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_aggregation_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_delivery_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_promoted_properties_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_result_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs new file mode 100644 index 00000000..b2a52263 --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + /// + public partial class AddApiGateway : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn>( + name: "additional_values", + schema: "infolink", + table: "partner", + type: "jsonb", + nullable: true); + + migrationBuilder.CreateTable( + name: "api_gateway", + schema: "infolink", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + subscription_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway", x => x.id); + table.ForeignKey( + name: "fk_api_gateway_subscription_subscription_id", + column: x => x.subscription_id, + principalSchema: "infolink", + principalTable: "subscription", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "api_gateway_partner", + schema: "infolink", + columns: table => new + { + api_gateway_id = table.Column(type: "integer", nullable: false), + partner_id = table.Column(type: "integer", nullable: false), + subscription_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway_partner", x => new { x.api_gateway_id, x.partner_id, x.subscription_id }); + table.ForeignKey( + name: "fk_api_gateway_partner_api_gateway_api_gateway_id", + column: x => x.api_gateway_id, + principalSchema: "infolink", + principalTable: "api_gateway", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_partner_partner_id", + column: x => x.partner_id, + principalSchema: "infolink", + principalTable: "partner", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_subscription_subscription_id", + column: x => x.subscription_id, + principalSchema: "infolink", + principalTable: "subscription", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.UpdateData( + schema: "infolink", + table: "partner", + keyColumn: "id", + keyValue: 1, + column: "additional_values", + value: null); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_subscription_id", + schema: "infolink", + table: "api_gateway", + column: "subscription_id"); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_partner_id", + schema: "infolink", + table: "api_gateway_partner", + column: "partner_id"); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_subscription_id", + schema: "infolink", + table: "api_gateway_partner", + column: "subscription_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "api_gateway_partner", + schema: "infolink"); + + migrationBuilder.DropTable( + name: "api_gateway", + schema: "infolink"); + + migrationBuilder.DropColumn( + name: "additional_values", + schema: "infolink", + table: "partner"); + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.Sdk/Model/ApiGateway.cs b/SW.Bitween.Sdk/Model/ApiGateway.cs new file mode 100644 index 00000000..e69de29b From 9b98028740956342bec95d59f640d89d8083be49 Mon Sep 17 00:00:00 2001 From: Muhannad Al-Khatib Date: Thu, 12 Feb 2026 18:28:40 +0300 Subject: [PATCH 04/10] m --- .DS_Store | Bin 0 -> 6148 bytes .../Controllers/GatewayController.cs | 111 +- SW.Bitween.Api/Data/BitweenDbContext.cs | 32 +- SW.Bitween.Api/Domain/Gateway/ApiGateway.cs | 9 +- .../Domain/Gateway/ApiGatewayPartner.cs | 2 +- SW.Bitween.Api/Domain/Partner/Partner.cs | 4 +- .../Domain/Subscription/Subscription.cs | 7 + SW.Bitween.Api/Domain/Xchange/Xchange.cs | 34 +- .../Extensions/InfolinkDbContextExtensions.cs | 2 +- .../Resources/ApiGateways/AddPartner.cs | 66 + .../Resources/ApiGateways/Create.cs | 39 + .../Resources/ApiGateways/Delete.cs | 29 + SW.Bitween.Api/Resources/ApiGateways/Get.cs | 44 + .../Resources/ApiGateways/RemovePartner.cs | 50 + .../Resources/ApiGateways/Search.cs | 49 + .../Resources/ApiGateways/Update.cs | 45 + .../Resources/ApiGateways/UpdatePartner.cs | 58 + .../Resources/Subscriptions/Create.cs | 4 + SW.Bitween.Api/Resources/Subscriptions/Get.cs | 2 +- .../Resources/Subscriptions/Update.cs | 61 +- SW.Bitween.Api/SW.Bitween.Api.csproj | 4 +- SW.Bitween.Api/Services/XchangeService.cs | 6 +- .../20260211120500_AddApiGateway.cs | 0 .../20260211121000_AddApiGateway.cs | 0 .../20260211130000_AddApiGateway.cs | 0 .../20260211120500_AddApiGateway.cs | 0 .../20260211121000_AddApiGateway.cs | 0 .../20260211130000_AddApiGateway.cs | 0 SW.Bitween.PgSql/BitweenDbContext.cs | 28 +- .../20260211120440_AddApiGateway.Designer.cs | 1273 ----------------- .../20260211120440_AddApiGateway.cs | 129 -- .../20260211121000_AddApiGateway.cs | 0 .../20260211121819_AddApiGateway.Designer.cs | 1272 ---------------- .../20260211121819_AddApiGateway.cs | 123 -- .../20260211130000_AddApiGateway.cs | 0 .../BitweenDbContextModelSnapshot.cs | 105 ++ SW.Bitween.Sdk/Model/ApiGateway.cs | 37 + SW.Bitween.Sdk/Model/Subscription.cs | 22 +- SW.Bitween.Web/Properties/launchSettings.json | 55 +- SW.Bitween.Web/Startup.cs | 2 +- SW.Bitween.Web/appsettings.Migration.json | 8 + SW.Bitween.Web/appsettings.json | 26 +- SW.Bus.RabbitMqExtensions/.DS_Store | Bin 0 -> 6148 bytes SW.Bus/.DS_Store | Bin 0 -> 6148 bytes 44 files changed, 882 insertions(+), 2856 deletions(-) create mode 100644 .DS_Store delete mode 100644 SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs delete mode 100644 SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs delete mode 100644 SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs delete mode 100644 SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs delete mode 100644 SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs delete mode 100644 SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs delete mode 100644 SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs create mode 100644 SW.Bitween.Web/appsettings.Migration.json create mode 100644 SW.Bus.RabbitMqExtensions/.DS_Store create mode 100644 SW.Bus/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..724942e1f93111b3704703d6fd64ab6444a5cff9 GIT binary patch literal 6148 zcmeHKu};G<5IvU)6}ohU%{vlOH--)&N=FtlFd;!2C`f5t2t|xxLSo@B_yPU^A@Kt& zYaIFJ=X~e*o?^R4L@J!7U7`*VwV;g6Wi($1kF(aK;Vo^TvU5x+ zKTPvMVoX-^7K1P#4E#3+c<(M!OgpqrA(i$oHgt6$8+PM?8BJ^kOB-vS$44fLlFTHq zw{~B8&-XWvn{}V`N1w@2={;?YDXQx@k59*$84qFSHLjKyk0iEfpA!6z>5R7Mg!*)W z-T{p%hs`j)9Z@reSBPj__GVi|nXcQ1>$|5bOV PostSync([FromRoute] string gatewayApiName) + { + return ProcessAsync(gatewayApiName, resultSync: false); + } + + [HttpPost("{gatewayApiName}/async")] + public Task PostAsync([FromRoute] string gatewayApiName) + { + return ProcessAsync(gatewayApiName, resultSync: false); + } - [HttpPost("{gatewayApiName}")] - public async Task Post([FromRoute] string gatewayApiName) + private async Task ProcessAsync([FromRoute] string gatewayApiName, bool resultSync) { + var apiGateway = await dbContext.Set() + .Include(ag => ag.Partners) + .ThenInclude(agp => agp.Partner) + .FirstOrDefaultAsync(ag => ag.UrlName == gatewayApiName); + + if (apiGateway == null) + throw new SWNotFoundException($"API Gateway with URL name '{gatewayApiName}' not found"); + + // Resolve partner using API key + var (partner, keyName) = await dbContext.AuthorizePartner(requestContext); + + // Verify partner is part of the API Gateway + var apiGatewayPartner = apiGateway.Partners.FirstOrDefault(agp => agp.PartnerId == partner.Id); + if (apiGatewayPartner == null) + throw new SWUnauthorizedException("Partner is not authorized for this API Gateway"); + + 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.AdapterProperties, Partner.TemplateVariableNamePrefix); + await xchangeService.RunValidator(subscription.ValidatorId, validatorProperties, + xchangeFile); + + var xchangeReferences = new List { $"partnerkey: {keyName}" }; + var xchangeId= await xchangeService.SubmitSubscriptionXchange(subscription.Id, xchangeFile, xchangeReferences.ToArray()); + 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; + } - return Ok(); + 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() + .AsNoTracking() + .AnyAsync(i => i.Id == xchangeId)) continue; + + var xchangeResult = await dbContext.FindAsync(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: + throw new SWValidationException("failure", "Internal processing error."); + } + } + + return Accepted(xchangeId); } + } \ No newline at end of file diff --git a/SW.Bitween.Api/Data/BitweenDbContext.cs b/SW.Bitween.Api/Data/BitweenDbContext.cs index 77295fd6..2b6eb602 100644 --- a/SW.Bitween.Api/Data/BitweenDbContext.cs +++ b/SW.Bitween.Api/Data/BitweenDbContext.cs @@ -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 @@ -92,11 +91,36 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) }); + modelBuilder.Entity(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(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(b => { b.ToTable("Partners"); b.Metadata.SetNavigationAccessMode(PropertyAccessMode.Field); b.Property(p => p.Name).IsRequired().IsUnicode(false).HasMaxLength(200); + b.Property(p => p.AdditionalValues).StoreAsJson(); b.HasMany(p => p.Subscriptions).WithOne().IsRequired(false).HasForeignKey(p => p.PartnerId) .OnDelete(DeleteBehavior.Restrict); b.OwnsMany(p => p.ApiCredentials, apicred => @@ -305,6 +329,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) b.Property(p => p.AccountId); b.Property(p => p.LoginMethod).HasConversion(); }); + } async public override Task SaveChangesAsync(CancellationToken cancellationToken = default) @@ -334,4 +359,5 @@ await publish.Publish(hasWorkGroup.GetBusMessageName(), return affectedRecords; } } -} \ No newline at end of file +} + diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs index c5d86081..3aa0bc3b 100644 --- a/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs +++ b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs @@ -1,6 +1,11 @@ +using System.Collections.Generic; +using SW.PrimitiveTypes; + namespace SW.Bitween.Domain.Gateway; -public class ApiGateway +public class ApiGateway : BaseEntity { - + public string Name { get; set; } + public string UrlName { get; set; } + public ICollection Partners { get; set; } } \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs index 544822c8..1811befb 100644 --- a/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs +++ b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs @@ -7,5 +7,5 @@ public class ApiGatewayPartner public Partner Partner { get; set; } public int PartnerId { get; set; } public Subscription Subscription { get; set; } - public int? SubscriptionId { get; set; } + public int SubscriptionId { get; set; } } \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Partner/Partner.cs b/SW.Bitween.Api/Domain/Partner/Partner.cs index 838f3772..004adc22 100644 --- a/SW.Bitween.Api/Domain/Partner/Partner.cs +++ b/SW.Bitween.Api/Domain/Partner/Partner.cs @@ -9,6 +9,7 @@ namespace SW.Bitween.Domain { public class Partner : BaseEntity { + public const string TemplateVariableNamePrefix = "partner"; public const int SystemId = 1; private Partner() @@ -29,7 +30,7 @@ public Partner(string name) } public string Name { get; set; } - + public Dictionary AdapterProperties { get; set; } readonly HashSet _Subscriptions; public IReadOnlyCollection Subscriptions => _Subscriptions; @@ -41,6 +42,7 @@ public void SetApiCredentials(IEnumerable apiCredentials) { _ApiCredentials.Update(apiCredentials); } + } } diff --git a/SW.Bitween.Api/Domain/Subscription/Subscription.cs b/SW.Bitween.Api/Domain/Subscription/Subscription.cs index 3664494b..b36d4451 100644 --- a/SW.Bitween.Api/Domain/Subscription/Subscription.cs +++ b/SW.Bitween.Api/Domain/Subscription/Subscription.cs @@ -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) { diff --git a/SW.Bitween.Api/Domain/Xchange/Xchange.cs b/SW.Bitween.Api/Domain/Xchange/Xchange.cs index 879708c8..7506d847 100644 --- a/SW.Bitween.Api/Domain/Xchange/Xchange.cs +++ b/SW.Bitween.Api/Domain/Xchange/Xchange.cs @@ -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; @@ -38,22 +39,33 @@ 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) : 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; CorrelationId = correlationId; + if (gatewayPartner != null) + { + MapperProperties = subscription.MapperProperties.ToDictionary().Fill(gatewayPartner.AdapterProperties, + Partner.TemplateVariableNamePrefix); + HandlerProperties = subscription.HandlerProperties.ToDictionary().Fill(gatewayPartner.AdapterProperties, + Partner.TemplateVariableNamePrefix); + } + else + { + MapperProperties = subscription.MapperProperties; + HandlerProperties = subscription.HandlerProperties; + } } //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; @@ -64,9 +76,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; @@ -95,6 +108,5 @@ public Xchange(Subscription subscription, Xchange xchange, XchangeFile file) : public string RetryFor { get; private set; } public string CorrelationId { get; set; } - } -} +} \ No newline at end of file diff --git a/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs b/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs index ce90241c..d82e38fb 100644 --- a/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs +++ b/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs @@ -25,7 +25,7 @@ where partner.ApiCredentials.Any(cred => cred.Key == partnerKey) if (par == null) throw new SWUnauthorizedException(); - return (par, par.ApiCredentials.First(c => c.Key == partnerKey).Name); + return (par, par.ApiCredentials.Single(c => c.Key == partnerKey).Name); } public static IQueryable Subscriptions(this BitweenDbContext dbContext) => diff --git a/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs b/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs index e69de29b..310662da 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs @@ -0,0 +1,66 @@ +using SW.Bitween.Domain.Gateway; +using SW.Bitween.Model; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using SW.Bitween.Domain; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class AddPartner : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public AddPartner(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(int gatewayId, ApiGatewayPartnerCreate model) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var gateway = await _dbContext.Set() + .Include(ag => ag.Partners) + .FirstOrDefaultAsync(ag => ag.Id == gatewayId); + + if (gateway == null) + throw new SWNotFoundException($"ApiGateway with Id {gatewayId} not found"); + + // Validate subscription exists and is of type GatewayApiCall + var subscription = await _dbContext.Set() + .FirstOrDefaultAsync(s => s.Id == model.SubscriptionId); + + if (subscription == null) + throw new SWNotFoundException($"Subscription with Id {model.SubscriptionId} not found"); + + if (subscription.Type != SubscriptionType.GatewayApiCall) + throw new SWException($"Subscription must be of type GatewayApiCall. Current type: {subscription.Type}"); + + // Check if partner already exists + var existingPartner = gateway.Partners != null + ? gateway.Partners.FirstOrDefault(p => p.PartnerId == model.PartnerId && p.SubscriptionId == model.SubscriptionId) + : null; + + if (existingPartner != null) + throw new SWException("Partner already exists in this gateway"); + + var partnerLink = new ApiGatewayPartner + { + ApiGatewayId = gatewayId, + PartnerId = model.PartnerId, + SubscriptionId = model.SubscriptionId + }; + + _dbContext.Add(partnerLink); + await _dbContext.SaveChangesAsync(); + + return null; + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/Create.cs b/SW.Bitween.Api/Resources/ApiGateways/Create.cs index e69de29b..c4702745 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Create.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Create.cs @@ -0,0 +1,39 @@ +using SW.Bitween.Domain.Gateway; +using SW.Bitween.Model; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class Create : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Create(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(ApiGatewayCreate model) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + if (string.IsNullOrWhiteSpace(model.UrlName)) + throw new SWException("UrlName is required"); + + var entity = new ApiGateway + { + Name = model.Name, + UrlName = model.UrlName + }; + + _dbContext.Add(entity); + await _dbContext.SaveChangesAsync(); + return entity.Id; + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/Delete.cs b/SW.Bitween.Api/Resources/ApiGateways/Delete.cs index e69de29b..88143b5a 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Delete.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Delete.cs @@ -0,0 +1,29 @@ +using SW.EfCoreExtensions; +using SW.Bitween.Domain.Gateway; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class Delete : IDeleteHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Delete(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(int key) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + await _dbContext.DeleteByKeyAsync(key); + return null; + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/Get.cs b/SW.Bitween.Api/Resources/ApiGateways/Get.cs index e69de29b..28347b3d 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Get.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Get.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using SW.Bitween.Domain.Gateway; +using SW.PrimitiveTypes; +using System.Linq; +using System.Threading.Tasks; +using SW.Bitween.Model; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class Get : IGetHandler + { + private readonly BitweenDbContext _dbContext; + + public Get(BitweenDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(int key) + { + return await _dbContext.Set() + .AsNoTracking() + .Include(ag => ag.Partners) + .ThenInclude(p => p.Partner) + .Include(ag => ag.Partners) + .ThenInclude(p => p.Subscription) + .Where(ag => ag.Id == key) + .Select(gateway => new ApiGatewayUpdate + { + Name = gateway.Name, + UrlName = gateway.UrlName, + Partners = gateway.Partners.Select(p => new ApiGatewayPartnerDto + { + PartnerId = p.PartnerId, + SubscriptionId = p.SubscriptionId, + PartnerName = p.Partner.Name, + SubscriptionName = p.Subscription.Name + }).ToList() + }) + .SingleOrDefaultAsync(); + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs index e69de29b..07d41e76 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs @@ -0,0 +1,50 @@ +using SW.Bitween.Domain.Gateway; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; +using Microsoft.EntityFrameworkCore; +using System.Linq; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class RemovePartner : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public RemovePartner(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(int gatewayId, RemovePartnerRequest request) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var gateway = await _dbContext.Set() + .Include(ag => ag.Partners) + .FirstOrDefaultAsync(ag => ag.Id == gatewayId); + + if (gateway == null) + throw new SWNotFoundException($"ApiGateway with Id {gatewayId} not found"); + + var partnerLink = gateway.Partners? + .FirstOrDefault(p => p.PartnerId == request.PartnerId); + + if (partnerLink == null) + throw new SWNotFoundException($"Partner with Id {request.PartnerId} not found in gateway {gatewayId}"); + + _dbContext.Remove(partnerLink); + await _dbContext.SaveChangesAsync(); + + return null; + } + } + + public class RemovePartnerRequest + { + public int PartnerId { get; set; } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/Search.cs b/SW.Bitween.Api/Resources/ApiGateways/Search.cs index e69de29b..b22f722f 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Search.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Search.cs @@ -0,0 +1,49 @@ +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.EfCoreExtensions; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using SW.Bitween.Domain.Gateway; +using SW.Bitween.Model; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class Search : ISearchyHandler + { + private readonly BitweenDbContext _dbContext; + + public Search(BitweenDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(SearchyRequest searchyRequest, bool lookup = false, string searchPhrase = null) + { + var query = from gateway in _dbContext.Set() + select new ApiGatewayRow + { + Id = gateway.Id, + Name = gateway.Name, + UrlName = gateway.UrlName, + PartnersCount = gateway.Partners.Count + }; + + query = query.AsNoTracking(); + + if (lookup) + { + return await query.Search(searchyRequest.Conditions).ToDictionaryAsync(k => k.Id.ToString(), v => v.Name); + } + + // Apply ordering by Id descending + query = query.OrderByDescending(g => g.Id); + + return new SearchyResponse + { + TotalCount = await query.Search(searchyRequest.Conditions).CountAsync(), + Result = await query.Search(searchyRequest.Conditions, searchyRequest.Sorts, searchyRequest.PageSize, searchyRequest.PageIndex).ToListAsync() + }; + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/Update.cs b/SW.Bitween.Api/Resources/ApiGateways/Update.cs index e69de29b..b53bef93 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Update.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Update.cs @@ -0,0 +1,45 @@ +using SW.EfCoreExtensions; +using SW.Bitween.Domain.Gateway; +using SW.Bitween.Model; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; +using Microsoft.EntityFrameworkCore; +using System.Linq; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class Update : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Update(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(int key, ApiGatewayUpdate model) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var entity = await _dbContext.Set() + .Include(ag => ag.Partners) + .FirstOrDefaultAsync(ag => ag.Id == key); + + if (entity == null) + throw new SWNotFoundException($"ApiGateway with Id {key} not found"); + + if (string.IsNullOrWhiteSpace(model.UrlName)) + throw new SWException("UrlName is required"); + + entity.Name = model.Name; + entity.UrlName = model.UrlName; + + await _dbContext.SaveChangesAsync(); + return null; + } + } +} + diff --git a/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs index e69de29b..caf4a731 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs @@ -0,0 +1,58 @@ +using SW.Bitween.Domain.Gateway; +using SW.Bitween.Model; +using SW.PrimitiveTypes; +using System.Threading.Tasks; +using SW.Bitween.Domain.Accounts; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using SW.Bitween.Domain; + +namespace SW.Bitween.Resources.ApiGateways +{ + public class UpdatePartner : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public UpdatePartner(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(int gatewayId, ApiGatewayPartnerCreate model) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var gateway = await _dbContext.Set() + .Include(ag => ag.Partners) + .FirstOrDefaultAsync(ag => ag.Id == gatewayId); + + if (gateway == null) + throw new SWNotFoundException($"ApiGateway with Id {gatewayId} not found"); + + // Validate subscription exists and is of type GatewayApiCall + var subscription = await _dbContext.Set() + .FirstOrDefaultAsync(s => s.Id == model.SubscriptionId); + + if (subscription == null) + throw new SWNotFoundException($"Subscription with Id {model.SubscriptionId} not found"); + + if (subscription.Type != SubscriptionType.GatewayApiCall) + throw new SWException($"Subscription must be of type GatewayApiCall. Current type: {subscription.Type}"); + + var partnerLink = gateway.Partners? + .FirstOrDefault(p => p.PartnerId == model.PartnerId); + + if (partnerLink == null) + throw new SWNotFoundException($"Partner with Id {model.PartnerId} not found in gateway {gatewayId}"); + + partnerLink.SubscriptionId = model.SubscriptionId; + + await _dbContext.SaveChangesAsync(); + + return null; + } + } +} + diff --git a/SW.Bitween.Api/Resources/Subscriptions/Create.cs b/SW.Bitween.Api/Resources/Subscriptions/Create.cs index f36c78b0..7d15bfea 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Create.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Create.cs @@ -36,6 +36,10 @@ public async Task Handle(SubscriptionCreate model) case SubscriptionType.Internal: entity = new Subscription(model.Name, model.DocumentId, model.Type, model.PartnerId!.Value); break; + case SubscriptionType.GatewayApiCall: + entity = new Subscription(model.Name, model.DocumentId, model.Type); + break; + case SubscriptionType.Unknown: default: throw new BitweenException(); diff --git a/SW.Bitween.Api/Resources/Subscriptions/Get.cs b/SW.Bitween.Api/Resources/Subscriptions/Get.cs index 3bdc6b66..19c46deb 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Get.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Get.cs @@ -23,7 +23,7 @@ public async Task Handle(int key) await dbContext.Set().AsNoTracking().Search("Id", key).SingleOrDefaultAsync(); return - new SubscriptionUpdate + new SubscriptionGet { AggregationForId = subscriber.AggregationForId, DocumentFilter = subscriber.DocumentFilter.ToKeyAndValueCollection(), diff --git a/SW.Bitween.Api/Resources/Subscriptions/Update.cs b/SW.Bitween.Api/Resources/Subscriptions/Update.cs index ad0b3f83..1a77f310 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Update.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Update.cs @@ -156,46 +156,73 @@ public Validate(IServiceProvider serviceProvider) }); }); - When(i => i.Type == SubscriptionType.Receiving, () => + RuleFor(i => i).CustomAsync(async (model, context, ct) => { - RuleFor(i => i.ReceiverId).NotEmpty(); - RuleFor(i => i.Schedules).NotEmpty(); - - When(i => i.ReceiverId != null, () => + var dbContext = serviceProvider.GetService(); + var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); + + if (subscription?.Type == SubscriptionType.Receiving) { - RuleFor(i => i.ReceiverProperties).CustomAsync(async (i, context, ct) => + if (string.IsNullOrEmpty(model.ReceiverId)) + context.AddFailure(nameof(model.ReceiverId), "ReceiverId is required for Receiving subscriptions"); + + if (model.Schedules == null || !model.Schedules.Any()) + context.AddFailure(nameof(model.Schedules), "Schedules are required for Receiving subscriptions"); + + if (!string.IsNullOrEmpty(model.ReceiverId)) { - var receiverId = ((SubscriptionUpdate)context.InstanceToValidate).ReceiverId; var mustProps = Enumerable.Empty(); // Check if it's a native adapter - if (receiverId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) + if (model.ReceiverId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) { var nativeAdapterDiscovery = serviceProvider.GetService(); - var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(receiverId); + var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(model.ReceiverId); mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); } else { var serverless = serviceProvider.GetService(); - await serverless.StartAsync(receiverId, null); + await serverless.StartAsync(model.ReceiverId, null); mustProps = (await serverless.GetExpectedStartupValues()) .Where(p => p.Value.Optional == false).Select(p => p.Key); } var missing = mustProps.ToHashSet(StringComparer.OrdinalIgnoreCase) - .Except(i.Where(p => !string.IsNullOrEmpty(p.Value)).Select(p => p.Key)); + .Except(model.ReceiverProperties.Where(p => !string.IsNullOrEmpty(p.Value)).Select(p => p.Key)); if (missing.Any()) - context.AddFailure($"Missing properties: {string.Join(",", missing)}"); - }); - }); + context.AddFailure(nameof(model.ReceiverProperties), $"Missing properties: {string.Join(",", missing)}"); + } + } }); - When(i => i.Type == SubscriptionType.Aggregation, () => + RuleFor(i => i).CustomAsync(async (model, context, ct) => { - RuleFor(i => i.Schedules).NotEmpty(); - RuleFor(i => i.AggregationForId).NotEmpty(); + var dbContext = serviceProvider.GetService(); + var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); + + if (subscription?.Type == SubscriptionType.Aggregation) + { + if (model.Schedules == null || !model.Schedules.Any()) + context.AddFailure(nameof(model.Schedules), "Schedules are required for Aggregation subscriptions"); + + if (!model.AggregationForId.HasValue) + context.AddFailure(nameof(model.AggregationForId), "AggregationForId is required for Aggregation subscriptions"); + } + }); + + RuleFor(i => i).CustomAsync(async (model, context, ct) => + { + var dbContext = serviceProvider.GetService(); + var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); + + if (subscription?.Type == SubscriptionType.GatewayApiCall) + { + if (model.PartnerId.HasValue) + context.AddFailure(nameof(model.PartnerId), "PartnerId must be null for GatewayApiCall subscriptions"); + } }); + } } } diff --git a/SW.Bitween.Api/SW.Bitween.Api.csproj b/SW.Bitween.Api/SW.Bitween.Api.csproj index 98448c29..6f0b94e3 100644 --- a/SW.Bitween.Api/SW.Bitween.Api.csproj +++ b/SW.Bitween.Api/SW.Bitween.Api.csproj @@ -13,7 +13,9 @@ - + + + diff --git a/SW.Bitween.Api/Services/XchangeService.cs b/SW.Bitween.Api/Services/XchangeService.cs index b0c058b5..eff0d4c4 100644 --- a/SW.Bitween.Api/Services/XchangeService.cs +++ b/SW.Bitween.Api/Services/XchangeService.cs @@ -53,11 +53,11 @@ public XchangeService(BitweenOptions BitweenSettings, BitweenDbContext dbContext } public async Task SubmitSubscriptionXchange(int subscriptionId, XchangeFile file, - string[] references = null) + string[] references = null, Partner gatewayPartner = null) { var subscription = await _BitweenCache.SubscriptionByIdAsync(subscriptionId); - var xchange = await CreateXchange(subscription, file, references, Guid.NewGuid().ToString("N")); + var xchange = await CreateXchange(subscription, file, references, Guid.NewGuid().ToString("N"), gatewayPartner); await _dbContext.SaveChangesAsync(); return xchange.Id; } @@ -108,7 +108,7 @@ public async Task CreateXchange(Document document, WorkGroup workGroup, } public async Task CreateXchange(Subscription subscription, XchangeFile file, - string[] references = null, string correlationId = null) + string[] references = null, string correlationId = null, Partner gatewayPartner = null) { var xchange = new Xchange(subscription, file, references, correlationId); await AddFile(xchange.Id, XchangeFileType.Input, file); diff --git a/SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211120500_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211121000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.MsSql/Migrations/20260211130000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211120500_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211121000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.MySql/Migrations/20260211130000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.PgSql/BitweenDbContext.cs b/SW.Bitween.PgSql/BitweenDbContext.cs index a3a5cac6..ce23e2c8 100644 --- a/SW.Bitween.PgSql/BitweenDbContext.cs +++ b/SW.Bitween.PgSql/BitweenDbContext.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using SW.EfCoreExtensions; using SW.Bitween.Domain; @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using SW.Bitween.Domain.Accounts; +using SW.Bitween.Domain.Gateway; namespace SW.Bitween.PgSql { @@ -86,6 +87,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) //b.ToTable("Partners"); b.Metadata.SetNavigationAccessMode(PropertyAccessMode.Field); b.Property(p => p.Name).IsRequired().HasMaxLength(200); + b.Property(p => p.AdapterProperties).HasColumnType("jsonb"); b.HasMany(p => p.Subscriptions).WithOne().IsRequired(false).HasForeignKey(p => p.PartnerId) .OnDelete(DeleteBehavior.Restrict); b.OwnsMany(p => p.ApiCredentials, apicred => @@ -114,6 +116,30 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity(ag => + { + ag.ToTable("api_gateway"); + 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(agp => + { + agp.ToTable("api_gateway_partner"); + 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) + .IsRequired().OnDelete(DeleteBehavior.Restrict); + }); + modelBuilder.Entity(b => { //b.ToTable("Subscriptions"); diff --git a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs deleted file mode 100644 index 77ff6a17..00000000 --- a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.Designer.cs +++ /dev/null @@ -1,1273 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SW.Bitween.Model; -using SW.Bitween.PgSql; - -#nullable disable - -namespace SW.Bitween.PgSql.Migrations -{ - [DbContext(typeof(BitweenDbContext))] - [Migration("20260211120440_AddApiGateway")] - partial class AddApiGateway - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("infolink") - .HasAnnotation("ProductVersion", "8.0.23") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("Deleted") - .HasColumnType("boolean") - .HasColumnName("deleted"); - - b.Property("Disabled") - .HasColumnType("boolean") - .HasColumnName("disabled"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Email") - .HasMaxLength(200) - .IsUnicode(false) - .HasColumnType("character varying(200)") - .HasColumnName("email"); - - b.Property("EmailProvider") - .HasColumnType("smallint") - .HasColumnName("email_provider"); - - b.Property("LoginMethods") - .HasColumnType("smallint") - .HasColumnName("login_methods"); - - b.Property("ModifiedBy") - .HasColumnType("text") - .HasColumnName("modified_by"); - - b.Property("ModifiedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("modified_on"); - - b.Property("Password") - .HasMaxLength(500) - .IsUnicode(false) - .HasColumnType("character varying(500)") - .HasColumnName("password"); - - b.Property("Phone") - .HasMaxLength(20) - .IsUnicode(false) - .HasColumnType("character varying(20)") - .HasColumnName("phone"); - - b.Property("Role") - .HasColumnType("integer") - .HasColumnName("role"); - - b.HasKey("Id") - .HasName("pk_accounts"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_accounts_email"); - - b.ToTable("Accounts", "infolink"); - - b.HasData( - new - { - Id = 9999, - CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), - Deleted = false, - Disabled = false, - DisplayName = "Admin", - Email = "admin@Bitween.systems", - EmailProvider = (byte)0, - LoginMethods = (byte)2, - Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", - Role = 0 - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => - { - b.Property("Id") - .HasMaxLength(50) - .IsUnicode(false) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("AccountId") - .HasColumnType("integer") - .HasColumnName("account_id"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("LoginMethod") - .HasColumnType("smallint") - .HasColumnName("login_method"); - - b.HasKey("Id") - .HasName("pk_refresh_tokens"); - - b.HasIndex("AccountId") - .HasDatabaseName("ix_refresh_tokens_account_id"); - - b.ToTable("RefreshTokens", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Document", b => - { - b.Property("Id") - .HasColumnType("integer") - .HasColumnName("id"); - - b.Property("BusEnabled") - .HasColumnType("boolean") - .HasColumnName("bus_enabled"); - - b.Property("BusMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("bus_message_type_name"); - - b.Property("DisregardsUnfilteredMessages") - .HasColumnType("boolean") - .HasColumnName("disregards_unfiltered_messages"); - - b.Property("DocumentFormat") - .HasColumnType("integer") - .HasColumnName("document_format"); - - b.Property("DuplicateInterval") - .HasColumnType("integer") - .HasColumnName("duplicate_interval"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("PromotedProperties") - .HasColumnType("jsonb") - .HasColumnName("promoted_properties"); - - b.HasKey("Id") - .HasName("pk_document"); - - b.HasIndex("BusMessageTypeName") - .IsUnique() - .HasDatabaseName("ix_document_bus_message_type_name"); - - b.HasIndex("Name") - .IsUnique() - .HasDatabaseName("ix_document_name"); - - b.ToTable("document", "infolink"); - - b.HasData( - new - { - Id = 10001, - BusEnabled = false, - DocumentFormat = 0, - DuplicateInterval = 0, - Name = "Aggregation Document", - PromotedProperties = "{}" - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => - { - b.Property("Id") - .HasColumnType("text") - .HasColumnName("id"); - - b.Property("Code") - .HasColumnType("integer") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("StateAfter") - .HasColumnType("text") - .HasColumnName("state_after"); - - b.Property("StateBefore") - .HasColumnType("text") - .HasColumnName("state_before"); - - b.HasKey("Id") - .HasName("pk_document_trail"); - - b.HasIndex("CreatedOn") - .HasDatabaseName("ix_document_trail_created_on"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_document_trail_document_id"); - - b.ToTable("document_trail", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_api_gateway"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_api_gateway_subscription_id"); - - b.ToTable("api_gateway", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => - { - b.Property("ApiGatewayId") - .HasColumnType("integer") - .HasColumnName("api_gateway_id"); - - b.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") - .HasName("pk_api_gateway_partner"); - - b.HasIndex("PartnerId") - .HasDatabaseName("ix_api_gateway_partner_partner_id"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_api_gateway_partner_subscription_id"); - - b.ToTable("api_gateway_partner", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HandlerId") - .HasMaxLength(200) - .IsUnicode(false) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property("HandlerProperties") - .HasColumnType("text") - .HasColumnName("handler_properties"); - - b.Property("Inactive") - .HasColumnType("boolean") - .HasColumnName("inactive"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("RunOnBadResult") - .HasColumnType("boolean") - .HasColumnName("run_on_bad_result"); - - b.Property("RunOnFailedResult") - .HasColumnType("boolean") - .HasColumnName("run_on_failed_result"); - - b.Property("RunOnSubscriptions") - .HasColumnType("integer[]") - .HasColumnName("run_on_subscriptions"); - - b.Property("RunOnSuccessfulResult") - .HasColumnType("boolean") - .HasColumnName("run_on_successful_result"); - - b.HasKey("Id") - .HasName("pk_notifier"); - - b.ToTable("notifier", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BadData") - .HasColumnType("boolean") - .HasColumnName("bad_data"); - - b.Property("Data") - .HasColumnType("text") - .HasColumnName("data"); - - b.Property("FileName") - .HasColumnType("text") - .HasColumnName("file_name"); - - b.Property("References") - .HasColumnType("text[]") - .HasColumnName("references"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_on_hold_xchange"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_on_hold_xchange_subscription_id"); - - b.ToTable("on_hold_xchange", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property>("AdditionalValues") - .HasColumnType("hstore") - .HasColumnName("additional_values"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.HasKey("Id") - .HasName("pk_partner"); - - b.ToTable("partner", "infolink"); - - b.HasData( - new - { - Id = 1, - Name = "SYSTEM" - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AggregateOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("aggregate_on"); - - b.Property("AggregationForId") - .HasColumnType("integer") - .HasColumnName("aggregation_for_id"); - - b.Property("AggregationTarget") - .HasColumnType("smallint") - .HasColumnName("aggregation_target"); - - b.Property("CategoryId") - .HasColumnType("integer") - .HasColumnName("category_id"); - - b.Property("ConsecutiveFailures") - .HasColumnType("integer") - .HasColumnName("consecutive_failures"); - - b.Property>("DocumentFilter") - .HasColumnType("jsonb") - .HasColumnName("document_filter"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("HandlerId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property>("HandlerProperties") - .HasColumnType("jsonb") - .HasColumnName("handler_properties"); - - b.Property("Inactive") - .HasColumnType("boolean") - .HasColumnName("inactive"); - - b.Property("IsRunning") - .HasColumnType("boolean") - .HasColumnName("is_running"); - - b.Property("LastException") - .HasColumnType("text") - .HasColumnName("last_exception"); - - b.Property("MapperId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mapper_id"); - - b.Property>("MapperProperties") - .HasColumnType("jsonb") - .HasColumnName("mapper_properties"); - - b.Property("MatchExpression") - .HasColumnType("text") - .HasColumnName("match_expression"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b.Property("PausedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("paused_on"); - - b.Property("ReceiveOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("receive_on"); - - b.Property("ReceiverId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("receiver_id"); - - b.Property>("ReceiverProperties") - .HasColumnType("jsonb") - .HasColumnName("receiver_properties"); - - b.Property("ResponseMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("response_message_type_name"); - - b.Property("ResponseSubscriptionId") - .HasColumnType("integer") - .HasColumnName("response_subscription_id"); - - b.Property("Temporary") - .HasColumnType("boolean") - .HasColumnName("temporary"); - - b.Property("Type") - .HasColumnType("smallint") - .HasColumnName("type"); - - b.Property("ValidatorId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("validator_id"); - - b.Property>("ValidatorProperties") - .HasColumnType("jsonb") - .HasColumnName("validator_properties"); - - b.Property("WorkGroupId") - .HasColumnType("integer") - .HasColumnName("work_group_id"); - - b.HasKey("Id") - .HasName("pk_subscription"); - - b.HasIndex("AggregationForId") - .HasDatabaseName("ix_subscription_aggregation_for_id"); - - b.HasIndex("CategoryId") - .HasDatabaseName("ix_subscription_category_id"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_subscription_document_id"); - - b.HasIndex("PartnerId") - .HasDatabaseName("ix_subscription_partner_id"); - - b.HasIndex("ResponseSubscriptionId") - .HasDatabaseName("ix_subscription_response_subscription_id"); - - b.HasIndex("WorkGroupId") - .HasDatabaseName("ix_subscription_work_group_id"); - - b.ToTable("subscription", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Code") - .HasColumnType("text") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ModifiedBy") - .HasColumnType("text") - .HasColumnName("modified_by"); - - b.Property("ModifiedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("modified_on"); - - b.HasKey("Id") - .HasName("pk_subscription_category"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_subscription_category_code"); - - b.ToTable("subscription_category", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Code") - .HasColumnType("integer") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("StateAfter") - .HasColumnType("text") - .HasColumnName("state_after"); - - b.Property("StateBefore") - .HasColumnType("text") - .HasColumnName("state_before"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_subscription_trail"); - - b.HasIndex("CreatedOn") - .HasDatabaseName("ix_subscription_trail_created_on"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_subscription_trail_subscription_id"); - - b.ToTable("subscription_trail", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BusMessageName") - .IsRequired() - .HasMaxLength(100) - .IsUnicode(false) - .HasColumnType("character varying(100)") - .HasColumnName("bus_message_name"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Options") - .HasColumnType("jsonb") - .HasColumnName("options"); - - b.HasKey("Id") - .HasName("pk_work_group"); - - b.ToTable("work_group", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("CorrelationId") - .HasColumnType("text") - .HasColumnName("correlation_id"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("HandlerId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property>("HandlerProperties") - .HasColumnType("jsonb") - .HasColumnName("handler_properties"); - - b.Property("InputContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("input_content_type"); - - b.Property("InputHash") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("input_hash"); - - b.Property("InputName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("input_name"); - - b.Property("InputSize") - .HasColumnType("integer") - .HasColumnName("input_size"); - - b.Property("MapperId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mapper_id"); - - b.Property>("MapperProperties") - .HasColumnType("jsonb") - .HasColumnName("mapper_properties"); - - b.Property("References") - .HasColumnType("text[]") - .HasColumnName("references"); - - b.Property("ResponseMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("response_message_type_name"); - - b.Property("ResponseSubscriptionId") - .HasColumnType("integer") - .HasColumnName("response_subscription_id"); - - b.Property("RetryFor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("retry_for"); - - b.Property("StartedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_on"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_xchange"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_xchange_document_id"); - - b.HasIndex("InputHash") - .HasDatabaseName("ix_xchange_input_hash"); - - b.HasIndex("RetryFor") - .HasDatabaseName("ix_xchange_retry_for"); - - b.HasIndex("StartedOn") - .HasDatabaseName("ix_xchange_started_on"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_xchange_subscription_id"); - - b.ToTable("xchange", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("AggregatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("aggregated_on"); - - b.Property("AggregationXchangeId") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("aggregation_xchange_id"); - - b.HasKey("Id") - .HasName("pk_xchange_aggregation"); - - b.HasIndex("AggregationXchangeId") - .HasDatabaseName("ix_xchange_aggregation_aggregation_xchange_id"); - - b.ToTable("xchange_aggregation", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("DeliveredOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("delivered_on"); - - b.HasKey("Id") - .HasName("pk_xchange_delivery"); - - b.HasIndex("DeliveredOn") - .HasDatabaseName("ix_xchange_delivery_delivered_on"); - - b.ToTable("xchange_delivery", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Exception") - .HasColumnType("text") - .HasColumnName("exception"); - - b.Property("FinishedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_on"); - - b.Property("NotifierId") - .HasColumnType("integer") - .HasColumnName("notifier_id"); - - b.Property("NotifierName") - .HasColumnType("text") - .HasColumnName("notifier_name"); - - b.Property("Success") - .HasColumnType("boolean") - .HasColumnName("success"); - - b.Property("XchangeId") - .HasMaxLength(50) - .IsUnicode(false) - .HasColumnType("character varying(50)") - .HasColumnName("xchange_id"); - - b.HasKey("Id") - .HasName("pk_xchange_notification"); - - b.ToTable("xchange_notification", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Hits") - .HasColumnType("integer[]") - .HasColumnName("hits"); - - b.Property>("Properties") - .HasColumnType("jsonb") - .HasColumnName("properties"); - - b.Property("PropertiesRaw") - .HasColumnType("text") - .HasColumnName("properties_raw"); - - b.HasKey("Id") - .HasName("pk_xchange_promoted_properties"); - - b.HasIndex("PropertiesRaw") - .HasDatabaseName("ix_xchange_promoted_properties_properties_raw"); - - b.ToTable("xchange_promoted_properties", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Exception") - .HasColumnType("text") - .HasColumnName("exception"); - - b.Property("FinishedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_on"); - - b.Property("OutputBad") - .HasColumnType("boolean") - .HasColumnName("output_bad"); - - b.Property("OutputContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("output_content_type"); - - b.Property("OutputHash") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("output_hash"); - - b.Property("OutputName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("output_name"); - - b.Property("OutputSize") - .HasColumnType("integer") - .HasColumnName("output_size"); - - b.Property("ResponseBad") - .HasColumnType("boolean") - .HasColumnName("response_bad"); - - b.Property("ResponseContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("response_content_type"); - - b.Property("ResponseHash") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("response_hash"); - - b.Property("ResponseName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("response_name"); - - b.Property("ResponseSize") - .HasColumnType("integer") - .HasColumnName("response_size"); - - b.Property("ResponseXchangeId") - .HasColumnType("text") - .HasColumnName("response_xchange_id"); - - b.Property("Success") - .HasColumnType("boolean") - .HasColumnName("success"); - - b.HasKey("Id") - .HasName("pk_xchange_result"); - - b.ToTable("xchange_result", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => - { - b.Property("IsRunning") - .HasColumnType("boolean") - .HasColumnName("is_running"); - - b.ToTable("running_result", "infolink"); - - b.ToView(null, (string)null); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => - { - b.HasOne("SW.Bitween.Domain.Accounts.Account", null) - .WithMany() - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_refresh_tokens_accounts_account_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => - { - b.HasOne("SW.Bitween.Domain.Document", "Document") - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_document_trail_document_document_id"); - - b.Navigation("Document"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_subscription_subscription_id"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => - { - b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") - .WithMany("Partners") - .HasForeignKey("ApiGatewayId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); - - b.HasOne("SW.Bitween.Domain.Partner", "Partner") - .WithMany() - .HasForeignKey("PartnerId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); - - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); - - b.Navigation("ApiGateway"); - - b.Navigation("Partner"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => - { - b1.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); - - b1.Property("Key") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("key"); - - b1.Property("Name") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("name"); - - b1.HasKey("PartnerId", "Id") - .HasName("pk_partner_api_credential"); - - b1.HasIndex("Key") - .IsUnique() - .HasDatabaseName("ix_partner_api_credential_key"); - - b1.ToTable("partner_api_credential", "infolink"); - - b1.WithOwner() - .HasForeignKey("PartnerId") - .HasConstraintName("fk_partner_api_credential_partner_partner_id"); - - b1.HasData( - new - { - PartnerId = 1, - Id = 1, - Key = "7facc758283844b49cc4ffd26a75b1de", - Name = "default" - }); - }); - - b.Navigation("ApiCredentials"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", null) - .WithMany() - .HasForeignKey("AggregationForId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_aggregation_for"); - - b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") - .WithMany() - .HasForeignKey("CategoryId") - .HasConstraintName("fk_subscription_subscription_category_category_id"); - - b.HasOne("SW.Bitween.Domain.Document", null) - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_subscription_document_document_id"); - - b.HasOne("SW.Bitween.Domain.Partner", null) - .WithMany("Subscriptions") - .HasForeignKey("PartnerId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_partner_partner_id"); - - b.HasOne("SW.Bitween.Domain.Subscription", null) - .WithMany() - .HasForeignKey("ResponseSubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_response_subscriber"); - - b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") - .WithMany() - .HasForeignKey("WorkGroupId") - .HasConstraintName("fk_subscription_work_group_work_group_id"); - - b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => - { - b1.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); - - b1.Property("Backwards") - .HasColumnType("boolean") - .HasColumnName("backwards"); - - b1.Property("On") - .HasColumnType("bigint") - .HasColumnName("on"); - - b1.Property("Recurrence") - .HasColumnType("smallint") - .HasColumnName("recurrence"); - - b1.HasKey("SubscriptionId", "Id") - .HasName("pk_subscription_schedule"); - - b1.ToTable("subscription_schedule", "infolink"); - - b1.WithOwner() - .HasForeignKey("SubscriptionId") - .HasConstraintName("fk_subscription_schedule_subscription_subscription_id"); - }); - - b.Navigation("Category"); - - b.Navigation("Schedules"); - - b.Navigation("WorkGroup"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subscription_trail_subscription_subscription_id"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => - { - b.HasOne("SW.Bitween.Domain.Document", null) - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_xchange_document_document_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_aggregation_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_delivery_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_promoted_properties_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_result_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.Navigation("Partners"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.Navigation("Subscriptions"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs deleted file mode 100644 index 8f6c9414..00000000 --- a/SW.Bitween.PgSql/Migrations/20260211120440_AddApiGateway.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace SW.Bitween.PgSql.Migrations -{ - /// - public partial class AddApiGateway : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:hstore", ",,"); - - migrationBuilder.AddColumn>( - name: "additional_values", - schema: "infolink", - table: "partner", - type: "hstore", - nullable: true); - - migrationBuilder.CreateTable( - name: "api_gateway", - schema: "infolink", - columns: table => new - { - id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - subscription_id = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_api_gateway", x => x.id); - table.ForeignKey( - name: "fk_api_gateway_subscription_subscription_id", - column: x => x.subscription_id, - principalSchema: "infolink", - principalTable: "subscription", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "api_gateway_partner", - schema: "infolink", - columns: table => new - { - api_gateway_id = table.Column(type: "integer", nullable: false), - partner_id = table.Column(type: "integer", nullable: false), - subscription_id = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_api_gateway_partner", x => new { x.api_gateway_id, x.partner_id, x.subscription_id }); - table.ForeignKey( - name: "fk_api_gateway_partner_api_gateway_api_gateway_id", - column: x => x.api_gateway_id, - principalSchema: "infolink", - principalTable: "api_gateway", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_api_gateway_partner_partner_partner_id", - column: x => x.partner_id, - principalSchema: "infolink", - principalTable: "partner", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_api_gateway_partner_subscription_subscription_id", - column: x => x.subscription_id, - principalSchema: "infolink", - principalTable: "subscription", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.UpdateData( - schema: "infolink", - table: "partner", - keyColumn: "id", - keyValue: 1, - column: "additional_values", - value: null); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_subscription_id", - schema: "infolink", - table: "api_gateway", - column: "subscription_id"); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_partner_partner_id", - schema: "infolink", - table: "api_gateway_partner", - column: "partner_id"); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_partner_subscription_id", - schema: "infolink", - table: "api_gateway_partner", - column: "subscription_id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "api_gateway_partner", - schema: "infolink"); - - migrationBuilder.DropTable( - name: "api_gateway", - schema: "infolink"); - - migrationBuilder.DropColumn( - name: "additional_values", - schema: "infolink", - table: "partner"); - - migrationBuilder.AlterDatabase() - .OldAnnotation("Npgsql:PostgresExtension:hstore", ",,"); - } - } -} diff --git a/SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211121000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs deleted file mode 100644 index b803c76d..00000000 --- a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.Designer.cs +++ /dev/null @@ -1,1272 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using SW.Bitween.Model; -using SW.Bitween.PgSql; - -#nullable disable - -namespace SW.Bitween.PgSql.Migrations -{ - [DbContext(typeof(BitweenDbContext))] - [Migration("20260211121819_AddApiGateway")] - partial class AddApiGateway - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("infolink") - .HasAnnotation("ProductVersion", "8.0.23") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("Deleted") - .HasColumnType("boolean") - .HasColumnName("deleted"); - - b.Property("Disabled") - .HasColumnType("boolean") - .HasColumnName("disabled"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("display_name"); - - b.Property("Email") - .HasMaxLength(200) - .IsUnicode(false) - .HasColumnType("character varying(200)") - .HasColumnName("email"); - - b.Property("EmailProvider") - .HasColumnType("smallint") - .HasColumnName("email_provider"); - - b.Property("LoginMethods") - .HasColumnType("smallint") - .HasColumnName("login_methods"); - - b.Property("ModifiedBy") - .HasColumnType("text") - .HasColumnName("modified_by"); - - b.Property("ModifiedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("modified_on"); - - b.Property("Password") - .HasMaxLength(500) - .IsUnicode(false) - .HasColumnType("character varying(500)") - .HasColumnName("password"); - - b.Property("Phone") - .HasMaxLength(20) - .IsUnicode(false) - .HasColumnType("character varying(20)") - .HasColumnName("phone"); - - b.Property("Role") - .HasColumnType("integer") - .HasColumnName("role"); - - b.HasKey("Id") - .HasName("pk_accounts"); - - b.HasIndex("Email") - .IsUnique() - .HasDatabaseName("ix_accounts_email"); - - b.ToTable("Accounts", "infolink"); - - b.HasData( - new - { - Id = 9999, - CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), - Deleted = false, - Disabled = false, - DisplayName = "Admin", - Email = "admin@Bitween.systems", - EmailProvider = (byte)0, - LoginMethods = (byte)2, - Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", - Role = 0 - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => - { - b.Property("Id") - .HasMaxLength(50) - .IsUnicode(false) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("AccountId") - .HasColumnType("integer") - .HasColumnName("account_id"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("LoginMethod") - .HasColumnType("smallint") - .HasColumnName("login_method"); - - b.HasKey("Id") - .HasName("pk_refresh_tokens"); - - b.HasIndex("AccountId") - .HasDatabaseName("ix_refresh_tokens_account_id"); - - b.ToTable("RefreshTokens", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Document", b => - { - b.Property("Id") - .HasColumnType("integer") - .HasColumnName("id"); - - b.Property("BusEnabled") - .HasColumnType("boolean") - .HasColumnName("bus_enabled"); - - b.Property("BusMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("bus_message_type_name"); - - b.Property("DisregardsUnfilteredMessages") - .HasColumnType("boolean") - .HasColumnName("disregards_unfiltered_messages"); - - b.Property("DocumentFormat") - .HasColumnType("integer") - .HasColumnName("document_format"); - - b.Property("DuplicateInterval") - .HasColumnType("integer") - .HasColumnName("duplicate_interval"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("PromotedProperties") - .HasColumnType("jsonb") - .HasColumnName("promoted_properties"); - - b.HasKey("Id") - .HasName("pk_document"); - - b.HasIndex("BusMessageTypeName") - .IsUnique() - .HasDatabaseName("ix_document_bus_message_type_name"); - - b.HasIndex("Name") - .IsUnique() - .HasDatabaseName("ix_document_name"); - - b.ToTable("document", "infolink"); - - b.HasData( - new - { - Id = 10001, - BusEnabled = false, - DocumentFormat = 0, - DuplicateInterval = 0, - Name = "Aggregation Document", - PromotedProperties = "{}" - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => - { - b.Property("Id") - .HasColumnType("text") - .HasColumnName("id"); - - b.Property("Code") - .HasColumnType("integer") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("StateAfter") - .HasColumnType("text") - .HasColumnName("state_after"); - - b.Property("StateBefore") - .HasColumnType("text") - .HasColumnName("state_before"); - - b.HasKey("Id") - .HasName("pk_document_trail"); - - b.HasIndex("CreatedOn") - .HasDatabaseName("ix_document_trail_created_on"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_document_trail_document_id"); - - b.ToTable("document_trail", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_api_gateway"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_api_gateway_subscription_id"); - - b.ToTable("api_gateway", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => - { - b.Property("ApiGatewayId") - .HasColumnType("integer") - .HasColumnName("api_gateway_id"); - - b.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") - .HasName("pk_api_gateway_partner"); - - b.HasIndex("PartnerId") - .HasDatabaseName("ix_api_gateway_partner_partner_id"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_api_gateway_partner_subscription_id"); - - b.ToTable("api_gateway_partner", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("HandlerId") - .HasMaxLength(200) - .IsUnicode(false) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property("HandlerProperties") - .HasColumnType("text") - .HasColumnName("handler_properties"); - - b.Property("Inactive") - .HasColumnType("boolean") - .HasColumnName("inactive"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("RunOnBadResult") - .HasColumnType("boolean") - .HasColumnName("run_on_bad_result"); - - b.Property("RunOnFailedResult") - .HasColumnType("boolean") - .HasColumnName("run_on_failed_result"); - - b.Property("RunOnSubscriptions") - .HasColumnType("integer[]") - .HasColumnName("run_on_subscriptions"); - - b.Property("RunOnSuccessfulResult") - .HasColumnType("boolean") - .HasColumnName("run_on_successful_result"); - - b.HasKey("Id") - .HasName("pk_notifier"); - - b.ToTable("notifier", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BadData") - .HasColumnType("boolean") - .HasColumnName("bad_data"); - - b.Property("Data") - .HasColumnType("text") - .HasColumnName("data"); - - b.Property("FileName") - .HasColumnType("text") - .HasColumnName("file_name"); - - b.Property("References") - .HasColumnType("text[]") - .HasColumnName("references"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_on_hold_xchange"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_on_hold_xchange_subscription_id"); - - b.ToTable("on_hold_xchange", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property>("AdditionalValues") - .HasColumnType("jsonb") - .HasColumnName("additional_values"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("name"); - - b.HasKey("Id") - .HasName("pk_partner"); - - b.ToTable("partner", "infolink"); - - b.HasData( - new - { - Id = 1, - Name = "SYSTEM" - }); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AggregateOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("aggregate_on"); - - b.Property("AggregationForId") - .HasColumnType("integer") - .HasColumnName("aggregation_for_id"); - - b.Property("AggregationTarget") - .HasColumnType("smallint") - .HasColumnName("aggregation_target"); - - b.Property("CategoryId") - .HasColumnType("integer") - .HasColumnName("category_id"); - - b.Property("ConsecutiveFailures") - .HasColumnType("integer") - .HasColumnName("consecutive_failures"); - - b.Property>("DocumentFilter") - .HasColumnType("jsonb") - .HasColumnName("document_filter"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("HandlerId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property>("HandlerProperties") - .HasColumnType("jsonb") - .HasColumnName("handler_properties"); - - b.Property("Inactive") - .HasColumnType("boolean") - .HasColumnName("inactive"); - - b.Property("IsRunning") - .HasColumnType("boolean") - .HasColumnName("is_running"); - - b.Property("LastException") - .HasColumnType("text") - .HasColumnName("last_exception"); - - b.Property("MapperId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mapper_id"); - - b.Property>("MapperProperties") - .HasColumnType("jsonb") - .HasColumnName("mapper_properties"); - - b.Property("MatchExpression") - .HasColumnType("text") - .HasColumnName("match_expression"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)") - .HasColumnName("name"); - - b.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b.Property("PausedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("paused_on"); - - b.Property("ReceiveOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("receive_on"); - - b.Property("ReceiverId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("receiver_id"); - - b.Property>("ReceiverProperties") - .HasColumnType("jsonb") - .HasColumnName("receiver_properties"); - - b.Property("ResponseMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("response_message_type_name"); - - b.Property("ResponseSubscriptionId") - .HasColumnType("integer") - .HasColumnName("response_subscription_id"); - - b.Property("Temporary") - .HasColumnType("boolean") - .HasColumnName("temporary"); - - b.Property("Type") - .HasColumnType("smallint") - .HasColumnName("type"); - - b.Property("ValidatorId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("validator_id"); - - b.Property>("ValidatorProperties") - .HasColumnType("jsonb") - .HasColumnName("validator_properties"); - - b.Property("WorkGroupId") - .HasColumnType("integer") - .HasColumnName("work_group_id"); - - b.HasKey("Id") - .HasName("pk_subscription"); - - b.HasIndex("AggregationForId") - .HasDatabaseName("ix_subscription_aggregation_for_id"); - - b.HasIndex("CategoryId") - .HasDatabaseName("ix_subscription_category_id"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_subscription_document_id"); - - b.HasIndex("PartnerId") - .HasDatabaseName("ix_subscription_partner_id"); - - b.HasIndex("ResponseSubscriptionId") - .HasDatabaseName("ix_subscription_response_subscription_id"); - - b.HasIndex("WorkGroupId") - .HasDatabaseName("ix_subscription_work_group_id"); - - b.ToTable("subscription", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Code") - .HasColumnType("text") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("Description") - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("ModifiedBy") - .HasColumnType("text") - .HasColumnName("modified_by"); - - b.Property("ModifiedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("modified_on"); - - b.HasKey("Id") - .HasName("pk_subscription_category"); - - b.HasIndex("Code") - .IsUnique() - .HasDatabaseName("ix_subscription_category_code"); - - b.ToTable("subscription_category", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Code") - .HasColumnType("integer") - .HasColumnName("code"); - - b.Property("CreatedBy") - .HasColumnType("text") - .HasColumnName("created_by"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("StateAfter") - .HasColumnType("text") - .HasColumnName("state_after"); - - b.Property("StateBefore") - .HasColumnType("text") - .HasColumnName("state_before"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_subscription_trail"); - - b.HasIndex("CreatedOn") - .HasDatabaseName("ix_subscription_trail_created_on"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_subscription_trail_subscription_id"); - - b.ToTable("subscription_trail", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BusMessageName") - .IsRequired() - .HasMaxLength(100) - .IsUnicode(false) - .HasColumnType("character varying(100)") - .HasColumnName("bus_message_name"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Options") - .HasColumnType("jsonb") - .HasColumnName("options"); - - b.HasKey("Id") - .HasName("pk_work_group"); - - b.ToTable("work_group", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("CorrelationId") - .HasColumnType("text") - .HasColumnName("correlation_id"); - - b.Property("DocumentId") - .HasColumnType("integer") - .HasColumnName("document_id"); - - b.Property("HandlerId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("handler_id"); - - b.Property>("HandlerProperties") - .HasColumnType("jsonb") - .HasColumnName("handler_properties"); - - b.Property("InputContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("input_content_type"); - - b.Property("InputHash") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("input_hash"); - - b.Property("InputName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("input_name"); - - b.Property("InputSize") - .HasColumnType("integer") - .HasColumnName("input_size"); - - b.Property("MapperId") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("mapper_id"); - - b.Property>("MapperProperties") - .HasColumnType("jsonb") - .HasColumnName("mapper_properties"); - - b.Property("References") - .HasColumnType("text[]") - .HasColumnName("references"); - - b.Property("ResponseMessageTypeName") - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("response_message_type_name"); - - b.Property("ResponseSubscriptionId") - .HasColumnType("integer") - .HasColumnName("response_subscription_id"); - - b.Property("RetryFor") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("retry_for"); - - b.Property("StartedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("started_on"); - - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b.HasKey("Id") - .HasName("pk_xchange"); - - b.HasIndex("DocumentId") - .HasDatabaseName("ix_xchange_document_id"); - - b.HasIndex("InputHash") - .HasDatabaseName("ix_xchange_input_hash"); - - b.HasIndex("RetryFor") - .HasDatabaseName("ix_xchange_retry_for"); - - b.HasIndex("StartedOn") - .HasDatabaseName("ix_xchange_started_on"); - - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_xchange_subscription_id"); - - b.ToTable("xchange", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("AggregatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("aggregated_on"); - - b.Property("AggregationXchangeId") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("aggregation_xchange_id"); - - b.HasKey("Id") - .HasName("pk_xchange_aggregation"); - - b.HasIndex("AggregationXchangeId") - .HasDatabaseName("ix_xchange_aggregation_aggregation_xchange_id"); - - b.ToTable("xchange_aggregation", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("DeliveredOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("delivered_on"); - - b.HasKey("Id") - .HasName("pk_xchange_delivery"); - - b.HasIndex("DeliveredOn") - .HasDatabaseName("ix_xchange_delivery_delivered_on"); - - b.ToTable("xchange_delivery", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Exception") - .HasColumnType("text") - .HasColumnName("exception"); - - b.Property("FinishedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_on"); - - b.Property("NotifierId") - .HasColumnType("integer") - .HasColumnName("notifier_id"); - - b.Property("NotifierName") - .HasColumnType("text") - .HasColumnName("notifier_name"); - - b.Property("Success") - .HasColumnType("boolean") - .HasColumnName("success"); - - b.Property("XchangeId") - .HasMaxLength(50) - .IsUnicode(false) - .HasColumnType("character varying(50)") - .HasColumnName("xchange_id"); - - b.HasKey("Id") - .HasName("pk_xchange_notification"); - - b.ToTable("xchange_notification", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Hits") - .HasColumnType("integer[]") - .HasColumnName("hits"); - - b.Property>("Properties") - .HasColumnType("jsonb") - .HasColumnName("properties"); - - b.Property("PropertiesRaw") - .HasColumnType("text") - .HasColumnName("properties_raw"); - - b.HasKey("Id") - .HasName("pk_xchange_promoted_properties"); - - b.HasIndex("PropertiesRaw") - .HasDatabaseName("ix_xchange_promoted_properties_properties_raw"); - - b.ToTable("xchange_promoted_properties", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => - { - b.Property("Id") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("id"); - - b.Property("Exception") - .HasColumnType("text") - .HasColumnName("exception"); - - b.Property("FinishedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("finished_on"); - - b.Property("OutputBad") - .HasColumnType("boolean") - .HasColumnName("output_bad"); - - b.Property("OutputContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("output_content_type"); - - b.Property("OutputHash") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("output_hash"); - - b.Property("OutputName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("output_name"); - - b.Property("OutputSize") - .HasColumnType("integer") - .HasColumnName("output_size"); - - b.Property("ResponseBad") - .HasColumnType("boolean") - .HasColumnName("response_bad"); - - b.Property("ResponseContentType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("response_content_type"); - - b.Property("ResponseHash") - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("response_hash"); - - b.Property("ResponseName") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("response_name"); - - b.Property("ResponseSize") - .HasColumnType("integer") - .HasColumnName("response_size"); - - b.Property("ResponseXchangeId") - .HasColumnType("text") - .HasColumnName("response_xchange_id"); - - b.Property("Success") - .HasColumnType("boolean") - .HasColumnName("success"); - - b.HasKey("Id") - .HasName("pk_xchange_result"); - - b.ToTable("xchange_result", "infolink"); - }); - - modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => - { - b.Property("IsRunning") - .HasColumnType("boolean") - .HasColumnName("is_running"); - - b.ToTable("running_result", "infolink"); - - b.ToView(null, (string)null); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => - { - b.HasOne("SW.Bitween.Domain.Accounts.Account", null) - .WithMany() - .HasForeignKey("AccountId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_refresh_tokens_accounts_account_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => - { - b.HasOne("SW.Bitween.Domain.Document", "Document") - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_document_trail_document_document_id"); - - b.Navigation("Document"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_subscription_subscription_id"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => - { - b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") - .WithMany("Partners") - .HasForeignKey("ApiGatewayId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); - - b.HasOne("SW.Bitween.Domain.Partner", "Partner") - .WithMany() - .HasForeignKey("PartnerId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); - - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); - - b.Navigation("ApiGateway"); - - b.Navigation("Partner"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => - { - b1.Property("PartnerId") - .HasColumnType("integer") - .HasColumnName("partner_id"); - - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); - - b1.Property("Key") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("key"); - - b1.Property("Name") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)") - .HasColumnName("name"); - - b1.HasKey("PartnerId", "Id") - .HasName("pk_partner_api_credential"); - - b1.HasIndex("Key") - .IsUnique() - .HasDatabaseName("ix_partner_api_credential_key"); - - b1.ToTable("partner_api_credential", "infolink"); - - b1.WithOwner() - .HasForeignKey("PartnerId") - .HasConstraintName("fk_partner_api_credential_partner_partner_id"); - - b1.HasData( - new - { - PartnerId = 1, - Id = 1, - Key = "7facc758283844b49cc4ffd26a75b1de", - Name = "default" - }); - }); - - b.Navigation("ApiCredentials"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", null) - .WithMany() - .HasForeignKey("AggregationForId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_aggregation_for"); - - b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") - .WithMany() - .HasForeignKey("CategoryId") - .HasConstraintName("fk_subscription_subscription_category_category_id"); - - b.HasOne("SW.Bitween.Domain.Document", null) - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_subscription_document_document_id"); - - b.HasOne("SW.Bitween.Domain.Partner", null) - .WithMany("Subscriptions") - .HasForeignKey("PartnerId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_partner_partner_id"); - - b.HasOne("SW.Bitween.Domain.Subscription", null) - .WithMany() - .HasForeignKey("ResponseSubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .HasConstraintName("fk_subscription_response_subscriber"); - - b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") - .WithMany() - .HasForeignKey("WorkGroupId") - .HasConstraintName("fk_subscription_work_group_work_group_id"); - - b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => - { - b1.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); - - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); - - b1.Property("Backwards") - .HasColumnType("boolean") - .HasColumnName("backwards"); - - b1.Property("On") - .HasColumnType("bigint") - .HasColumnName("on"); - - b1.Property("Recurrence") - .HasColumnType("smallint") - .HasColumnName("recurrence"); - - b1.HasKey("SubscriptionId", "Id") - .HasName("pk_subscription_schedule"); - - b1.ToTable("subscription_schedule", "infolink"); - - b1.WithOwner() - .HasForeignKey("SubscriptionId") - .HasConstraintName("fk_subscription_schedule_subscription_subscription_id"); - }); - - b.Navigation("Category"); - - b.Navigation("Schedules"); - - b.Navigation("WorkGroup"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_subscription_trail_subscription_subscription_id"); - - b.Navigation("Subscription"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => - { - b.HasOne("SW.Bitween.Domain.Document", null) - .WithMany() - .HasForeignKey("DocumentId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_xchange_document_document_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_aggregation_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_delivery_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_promoted_properties_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => - { - b.HasOne("SW.Bitween.Domain.Xchange", null) - .WithOne() - .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_xchange_result_xchange_id"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.Navigation("Partners"); - }); - - modelBuilder.Entity("SW.Bitween.Domain.Partner", b => - { - b.Navigation("Subscriptions"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs deleted file mode 100644 index b2a52263..00000000 --- a/SW.Bitween.PgSql/Migrations/20260211121819_AddApiGateway.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace SW.Bitween.PgSql.Migrations -{ - /// - public partial class AddApiGateway : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn>( - name: "additional_values", - schema: "infolink", - table: "partner", - type: "jsonb", - nullable: true); - - migrationBuilder.CreateTable( - name: "api_gateway", - schema: "infolink", - columns: table => new - { - id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), - subscription_id = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_api_gateway", x => x.id); - table.ForeignKey( - name: "fk_api_gateway_subscription_subscription_id", - column: x => x.subscription_id, - principalSchema: "infolink", - principalTable: "subscription", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "api_gateway_partner", - schema: "infolink", - columns: table => new - { - api_gateway_id = table.Column(type: "integer", nullable: false), - partner_id = table.Column(type: "integer", nullable: false), - subscription_id = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_api_gateway_partner", x => new { x.api_gateway_id, x.partner_id, x.subscription_id }); - table.ForeignKey( - name: "fk_api_gateway_partner_api_gateway_api_gateway_id", - column: x => x.api_gateway_id, - principalSchema: "infolink", - principalTable: "api_gateway", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_api_gateway_partner_partner_partner_id", - column: x => x.partner_id, - principalSchema: "infolink", - principalTable: "partner", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "fk_api_gateway_partner_subscription_subscription_id", - column: x => x.subscription_id, - principalSchema: "infolink", - principalTable: "subscription", - principalColumn: "id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.UpdateData( - schema: "infolink", - table: "partner", - keyColumn: "id", - keyValue: 1, - column: "additional_values", - value: null); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_subscription_id", - schema: "infolink", - table: "api_gateway", - column: "subscription_id"); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_partner_partner_id", - schema: "infolink", - table: "api_gateway_partner", - column: "partner_id"); - - migrationBuilder.CreateIndex( - name: "ix_api_gateway_partner_subscription_id", - schema: "infolink", - table: "api_gateway_partner", - column: "subscription_id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "api_gateway_partner", - schema: "infolink"); - - migrationBuilder.DropTable( - name: "api_gateway", - schema: "infolink"); - - migrationBuilder.DropColumn( - name: "additional_values", - schema: "infolink", - table: "partner"); - } - } -} diff --git a/SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs b/SW.Bitween.PgSql/Migrations/20260211130000_AddApiGateway.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs b/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs index fd12606c..e0184d09 100644 --- a/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs +++ b/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs @@ -252,6 +252,60 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("document_trail", "infolink"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_api_gateway"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_subscription_id"); + + b.ToTable("api_gateway", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("integer") + .HasColumnName("api_gateway_id"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") + .HasName("pk_api_gateway_partner"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_api_gateway_partner_partner_id"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_partner_subscription_id"); + + b.ToTable("api_gateway_partner", "infolink"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => { b.Property("Id") @@ -350,6 +404,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property>("AdditionalValues") + .HasColumnType("jsonb") + .HasColumnName("additional_values"); + b.Property("Name") .IsRequired() .HasMaxLength(200) @@ -960,6 +1018,48 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Document"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => @@ -1154,6 +1254,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasConstraintName("fk_xchange_result_xchange_id"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.Navigation("Subscriptions"); diff --git a/SW.Bitween.Sdk/Model/ApiGateway.cs b/SW.Bitween.Sdk/Model/ApiGateway.cs index e69de29b..dfb8a562 100644 --- a/SW.Bitween.Sdk/Model/ApiGateway.cs +++ b/SW.Bitween.Sdk/Model/ApiGateway.cs @@ -0,0 +1,37 @@ +using SW.PrimitiveTypes; +using System.Collections.Generic; + +namespace SW.Bitween.Model +{ + public class ApiGatewayCreate : IName + { + public string Name { get; set; } + public string UrlName { get; set; } + } + + public class ApiGatewayRow : ApiGatewayUpdate + { + public int Id { get; set; } + public int? PartnersCount { get; set; } + } + + public class ApiGatewayUpdate : ApiGatewayCreate + { + public ICollection Partners { get; set; } + } + + public class ApiGatewayPartnerDto + { + public int PartnerId { get; set; } + public int SubscriptionId { get; set; } + public string PartnerName { get; set; } + public string SubscriptionName { get; set; } + } + + public class ApiGatewayPartnerCreate + { + public int PartnerId { get; set; } + public int SubscriptionId { get; set; } + } +} + diff --git a/SW.Bitween.Sdk/Model/Subscription.cs b/SW.Bitween.Sdk/Model/Subscription.cs index 7dae4617..fa9ca9e5 100644 --- a/SW.Bitween.Sdk/Model/Subscription.cs +++ b/SW.Bitween.Sdk/Model/Subscription.cs @@ -10,7 +10,8 @@ public enum SubscriptionType Internal = 1, ApiCall = 2, Receiving = 4, - Aggregation = 8 + Aggregation = 8, + GatewayApiCall = 16, } public class SubscriptionReceiveNow @@ -37,25 +38,27 @@ public class SearchSubscriptionTrailModel public int SubscriptionId { get; set; } } - public class SubscriptionCreate : IName + public abstract class SubscriptionCreateUpdateBase : IName { public string Name { get; set; } public int DocumentId { get; set; } - public SubscriptionType Type { get; set; } public int? PartnerId { get; set; } public int? AggregationForId { get; set; } } + + public class SubscriptionCreate :SubscriptionCreateUpdateBase + { + public SubscriptionType Type { get; set; } + } - public class SubscriptionSearch : SubscriptionUpdate + public class SubscriptionSearch : SubscriptionGet { public int Id { get; set; } public string DocumentName { get; set; } public bool? IsRunning { get; set; } - public string CategoryCode { get; set; } - public string CategoryDescription { get; set; } } - public class SubscriptionUpdate : SubscriptionCreate + public class SubscriptionUpdate : SubscriptionCreateUpdateBase { public string HandlerId { get; set; } public string MapperId { get; set; } @@ -88,4 +91,9 @@ public class SubscriptionUpdate : SubscriptionCreate public string CategoryCode { get; set; } public string CategoryDescription { get; set; } } + + public class SubscriptionGet : SubscriptionUpdate + { + public SubscriptionType Type { get; set; } + } } \ No newline at end of file diff --git a/SW.Bitween.Web/Properties/launchSettings.json b/SW.Bitween.Web/Properties/launchSettings.json index 00b5d594..61599420 100644 --- a/SW.Bitween.Web/Properties/launchSettings.json +++ b/SW.Bitween.Web/Properties/launchSettings.json @@ -1,3 +1,6 @@ + + + { "iisSettings": { "windowsAuthentication": false, @@ -15,14 +18,58 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "SW.Bitween.Web": { + "TraxisDev": { "commandName": "Project", "launchBrowser": false, - "launchUrl": "https://localhost:5001", + "launchUrl": "https://localhost:5003", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "SwLogger__LoggingLevel": "2", + "ConnectionStrings__InfolinkDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", + "ConnectionStrings__BitweenDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", + "CloudFiles__AccessKeyId": "XAZGFKBXRE6C3GL723JA", + "CloudFiles__SecretAccessKey": "TVSfjjr7jBLB4SvT/6+9/q1CPv4EQJKOGo1hHF4u+zI", + "CloudFiles__BucketName": "traxis", + "CloudFiles__ServiceUrl": "https://nyc3.digitaloceanspaces.com", + "ConnectionStrings__RabbitMQ" : "amqps://ivtycyae:jRsc0VsYbm1nH37DlO7YjOHg5FKH2UbQ@eager-ivory-wasp.rmq.cloudamqp.com/ivtycyae", + "Infolink__DatabaseType": "PgSql", + "Infolink__AdminCredentials": "1:1", + "InfolinkClient__BaseUrl": "https://localhost:5003/api/", + "Bitween__DatabaseType": "PgSql", + "Bitween__AdminCredentials": "1:1", + "Bitween__BaseUrl": "https://localhost:5003/api/", + "Token__Key": "6547647654764764767657658658758765876532542", + "Token__Issuer": "local", + "Token__Audience": "local" + }, + "applicationUrl": "https://localhost:5002;http://localhost:5003" + }, + "PmmDev": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "https://localhost:5003", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "SwLogger__LoggingLevel": "1", + "ConnectionStrings__InfolinkDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", + "ConnectionStrings__BitweenDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", + "CloudFiles__AccessKeyId": "R3LNFRKWMAC4OCCRICS5", + "CloudFiles__SecretAccessKey": "YPyyTdxs+lZMQEtYIDRK9lkIzjJrCKXinE3OfKEfc7k", + "CloudFiles__BucketName": "sf9", + "CloudFiles__ServiceUrl": "https://fra1.digitaloceanspaces.com", + "ConnectionStrings__RabbitMQ" : "amqps://veebicsq:nw98MHANGkxAMgwOm57ALalyanCAwW2f@cow.rmq2.cloudamqp.com/veebicsq", + "Infolink__DatabaseType": "PgSql", + "Infolink__AdminCredentials": "1:1", + "InfolinkClient__BaseUrl": "https://localhost:5003/api/", + "Bitween__DatabaseType": "PgSql", + "Bitween__AdminCredentials": "1:1", + "Bitween__BaseUrl": "https://localhost:5003/api/", + "Token__Key": "6547647654764764767657658658758765876532542", + "Token__Issuer": "local", + "Token__Audience": "local" }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" + "applicationUrl": "https://localhost:5002;http://localhost:5003" } + } } \ No newline at end of file diff --git a/SW.Bitween.Web/Startup.cs b/SW.Bitween.Web/Startup.cs index 1b136cfe..8944175a 100644 --- a/SW.Bitween.Web/Startup.cs +++ b/SW.Bitween.Web/Startup.cs @@ -221,7 +221,7 @@ public void ConfigureServices(IServiceCollection services) connectionString += ";Authentication=Active Directory Default"; } } - + c.UseSqlServer(connectionString, b => { b.MigrationsAssembly(typeof(MsSql.DbType).Assembly.FullName); }); } diff --git a/SW.Bitween.Web/appsettings.Migration.json b/SW.Bitween.Web/appsettings.Migration.json new file mode 100644 index 00000000..3415bc9d --- /dev/null +++ b/SW.Bitween.Web/appsettings.Migration.json @@ -0,0 +1,8 @@ +{ + "ConnectionStrings": { + "BitweenDb": "Server=localhost;Database=bitween_migration;User=root;Password=password;" + }, + "Bitween": { + "DatabaseType": "MySql" + } +} diff --git a/SW.Bitween.Web/appsettings.json b/SW.Bitween.Web/appsettings.json index a2822b84..83aaa620 100644 --- a/SW.Bitween.Web/appsettings.json +++ b/SW.Bitween.Web/appsettings.json @@ -1,6 +1,12 @@ { "ASPNETCORE_ENVIRONMENT":"Development", "AllowedHosts": "*", + "ConnectionStrings": { + "BitweenDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", + "BitweenDb_Postgresql": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", + "InfolinkDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", + "RabbitMQ": "amqps://ivtycyae:jRsc0VsYbm1nH37DlO7YjOHg5FKH2UbQ@eager-ivory-wasp.rmq.cloudamqp.com/ivtycyae" + }, "Theme": { "LoginLogo": "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/Graphics/s9.png", "BitweenLogo": "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/Graphics/BitweenFull.svg", @@ -18,5 +24,23 @@ }, "SWLogger": { "LoggingLevel": 2 + }, + "CloudFiles": { + "AccessKeyId": "XAZGFKBXRE6C3GL723JA", + "SecretAccessKey": "TVSfjjr7jBLB4SvT/6+9/q1CPv4EQJKOGo1hHF4u+zI", + "BucketName": "traxis", + "ServiceUrl": "https://nyc3.digitaloceanspaces.com" + }, + "Bitween": { + "DatabaseType": "PgSql", + "AdminCredentials": "1:1" + }, + "InfolinkClient": { + "BaseUrl": "http://localhost:5000/api/" + }, + "Token": { + "Key": "6547647654764764767657658658758765876532542", + "Issuer": "local", + "Audience": "local" } -} \ No newline at end of file +} diff --git a/SW.Bus.RabbitMqExtensions/.DS_Store b/SW.Bus.RabbitMqExtensions/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8badee4dd3435aa1e8a4076e0da991dfa1c1857e GIT binary patch literal 6148 zcmeHKJ5B>Z47HOKD5+^^OTM5aqFp@vaHrk1xxYtusz$R*ORVy^@DeF-N_1;a$JLox4n9N@zQ#_ z^`2+%AD@p_FUQqQTW{Cft2a8VA4AA-aR!_LXTTZQI|F(fQE{~QN?yPja0U*I0XZK6 zhF~;Iiuvfklv@Dc1m-NzrI(PHU>FUPB0Lb*P@smgwHU17um{^04U?jV6I=7aR{6Vm z;ana0L*7mt6@7LFoPj{Ndc9s8nTQj{!W&*4D-5Xgl1s9~#XLlV_H)UC^n+l%%;{5IPWbu5~XO};)o2&k+JA@%$gN!#xzU=9= zWBU2FtC#nW2g?`Z>ZZ%L&nEc}AF-&LE8q&a07s<@}%Fml-Vb(-41h1zdrDrT}N}=ipJUMFv u+5$~X=Cv|_VVqn7@F4rhaU_)wGRC~ZuvL^*#2>?fei4X*c;^cIf&!m_TSV*t literal 0 HcmV?d00001 From e44b5b3e3d84c65ab66f50b0b72989e04d1219b2 Mon Sep 17 00:00:00 2001 From: Muhannad Al-Khatib Date: Tue, 17 Feb 2026 18:47:38 +0300 Subject: [PATCH 05/10] Add GlobalAdapterValuesSet entity and CRUD operations for API Gateway integration --- .../Controllers/GatewayController.cs | 5 +- SW.Bitween.Api/Data/BitweenDbContext.cs | 9 +- SW.Bitween.Api/Domain/Gateway/ApiGateway.cs | 7 +- .../Domain/Gateway/ApiGatewayPartner.cs | 9 +- .../GlobalAdapterValuesSet.cs | 11 + SW.Bitween.Api/Domain/Xchange/Xchange.cs | 18 +- SW.Bitween.Api/Helpers/StartupValuesFiller.cs | 77 +- SW.Bitween.Api/Interfaces/IInfolinkCache.cs | 3 + .../GlobalAdapterValuesSets/Create.cs | 52 + .../GlobalAdapterValuesSets/Delete.cs | 34 + .../GlobalAdapterValuesSets/Search.cs | 44 + .../GlobalAdapterValuesSets/Update.cs | 45 + .../Services/Caching/InMemoryInfolinkCache.cs | 24 + SW.Bitween.Api/Services/XchangeService.cs | 4 +- ...0741_ApiGateWayAndGlobalValues.Designer.cs | 1107 ++++++++++++++ ...0260217150741_ApiGateWayAndGlobalValues.cs | 120 ++ .../BitweenDbContextModelSnapshot.cs | 125 +- ...1131_ApiGateWayAndGlobalValues.Designer.cs | 1104 ++++++++++++++ ...0260217151131_ApiGateWayAndGlobalValues.cs | 134 ++ .../BitweenDbContextModelSnapshot.cs | 125 +- SW.Bitween.PgSql/BitweenDbContext.cs | 6 + .../DesignTimeDbContextFactory.cs | 0 ...2930_ApiGateWayAndGlobalValues.Designer.cs | 1315 +++++++++++++++++ ...0260217152930_ApiGateWayAndGlobalValues.cs | 136 ++ .../BitweenDbContextModelSnapshot.cs | 83 +- SW.Bitween.PgSql/SW.Bitween.PgSql.csproj | 6 + .../Model/GlobalAdapterValuesSet.cs | 25 + 27 files changed, 4577 insertions(+), 51 deletions(-) create mode 100644 SW.Bitween.Api/Domain/GlobalAdapterValue/GlobalAdapterValuesSet.cs create mode 100644 SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs create mode 100644 SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Delete.cs create mode 100644 SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Search.cs create mode 100644 SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Update.cs create mode 100644 SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.Designer.cs create mode 100644 SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.cs create mode 100644 SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.Designer.cs create mode 100644 SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.cs create mode 100644 SW.Bitween.PgSql/DesignTimeDbContextFactory.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.Designer.cs create mode 100644 SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.cs create mode 100644 SW.Bitween.Sdk/Model/GlobalAdapterValuesSet.cs diff --git a/SW.Bitween.Api/Controllers/GatewayController.cs b/SW.Bitween.Api/Controllers/GatewayController.cs index 58a5c5ad..1c835983 100644 --- a/SW.Bitween.Api/Controllers/GatewayController.cs +++ b/SW.Bitween.Api/Controllers/GatewayController.cs @@ -26,7 +26,7 @@ public class GatewayController( [HttpPost("{gatewayApiName}/sync")] public Task PostSync([FromRoute] string gatewayApiName) { - return ProcessAsync(gatewayApiName, resultSync: false); + return ProcessAsync(gatewayApiName, resultSync: true); } [HttpPost("{gatewayApiName}/async")] @@ -37,6 +37,7 @@ public Task PostAsync([FromRoute] string gatewayApiName) private async Task ProcessAsync([FromRoute] string gatewayApiName, bool resultSync) { + var globalAdapterValuesSet = await cache.ListGlobalAdapterValuesSetsAsync(); var apiGateway = await dbContext.Set() .Include(ag => ag.Partners) .ThenInclude(agp => agp.Partner) @@ -61,7 +62,7 @@ private async Task ProcessAsync([FromRoute] string gatewayApiName var xchangeFile = new XchangeFile(json); var validatorProperties = subscription.ValidatorProperties.ToDictionary() - .Fill(partner.AdapterProperties, Partner.TemplateVariableNamePrefix); + .Fill(partner, globalAdapterValuesSet); await xchangeService.RunValidator(subscription.ValidatorId, validatorProperties, xchangeFile); diff --git a/SW.Bitween.Api/Data/BitweenDbContext.cs b/SW.Bitween.Api/Data/BitweenDbContext.cs index 2b6eb602..bad5f545 100644 --- a/SW.Bitween.Api/Data/BitweenDbContext.cs +++ b/SW.Bitween.Api/Data/BitweenDbContext.cs @@ -115,12 +115,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Restrict); }); + modelBuilder.Entity(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(b => { b.ToTable("Partners"); b.Metadata.SetNavigationAccessMode(PropertyAccessMode.Field); b.Property(p => p.Name).IsRequired().IsUnicode(false).HasMaxLength(200); - b.Property(p => p.AdditionalValues).StoreAsJson(); + 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 => diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs index 3aa0bc3b..7f304f7f 100644 --- a/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs +++ b/SW.Bitween.Api/Domain/Gateway/ApiGateway.cs @@ -1,11 +1,16 @@ +using System; using System.Collections.Generic; using SW.PrimitiveTypes; namespace SW.Bitween.Domain.Gateway; -public class ApiGateway : BaseEntity +public class ApiGateway : BaseEntity,IAudited { public string Name { get; set; } public string UrlName { get; set; } public ICollection Partners { get; set; } + public DateTime CreatedOn { get; set; } + public string CreatedBy { get; set; } + public DateTime? ModifiedOn { get; set; } + public string ModifiedBy { get; set; } } \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs index 1811befb..f82b51da 100644 --- a/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs +++ b/SW.Bitween.Api/Domain/Gateway/ApiGatewayPartner.cs @@ -1,6 +1,9 @@ +using System; +using SW.PrimitiveTypes; + namespace SW.Bitween.Domain.Gateway; -public class ApiGatewayPartner +public class ApiGatewayPartner: IAudited { public ApiGateway ApiGateway { get; set; } public int ApiGatewayId { get; set; } @@ -8,4 +11,8 @@ public class ApiGatewayPartner 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; } } \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/GlobalAdapterValue/GlobalAdapterValuesSet.cs b/SW.Bitween.Api/Domain/GlobalAdapterValue/GlobalAdapterValuesSet.cs new file mode 100644 index 00000000..15e6aadd --- /dev/null +++ b/SW.Bitween.Api/Domain/GlobalAdapterValue/GlobalAdapterValuesSet.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Domain; + +public class GlobalAdapterValuesSet:BaseEntity +{ + public string Name { get; set; } + public Dictionary Values { get; set; } +} \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Xchange/Xchange.cs b/SW.Bitween.Api/Domain/Xchange/Xchange.cs index 7506d847..f8363be4 100644 --- a/SW.Bitween.Api/Domain/Xchange/Xchange.cs +++ b/SW.Bitween.Api/Domain/Xchange/Xchange.cs @@ -40,7 +40,7 @@ public Xchange(int documentId, IWorkGroup workGroup, XchangeFile file, string[] } public Xchange(Subscription subscription, XchangeFile file, string[] references = null, - string correlationId = null, Partner gatewayPartner = null) : + string correlationId = null, Partner gatewayPartner = null,GlobalAdapterValuesSet[] globalAdapterValuesSets = null) : this(subscription.DocumentId, subscription.WorkGroup, file, references, subscription.Type) { SubscriptionId = subscription.Id; @@ -48,19 +48,11 @@ public Xchange(Subscription subscription, XchangeFile file, string[] references HandlerId = subscription.HandlerId; ResponseSubscriptionId = subscription.ResponseSubscriptionId; ResponseMessageTypeName = subscription.ResponseMessageTypeName; + MapperProperties = subscription.MapperProperties.ToDictionary().Fill(gatewayPartner,globalAdapterValuesSets); + HandlerProperties = subscription.HandlerProperties.ToDictionary() + .Fill(gatewayPartner, globalAdapterValuesSets); CorrelationId = correlationId; - if (gatewayPartner != null) - { - MapperProperties = subscription.MapperProperties.ToDictionary().Fill(gatewayPartner.AdapterProperties, - Partner.TemplateVariableNamePrefix); - HandlerProperties = subscription.HandlerProperties.ToDictionary().Fill(gatewayPartner.AdapterProperties, - Partner.TemplateVariableNamePrefix); - } - else - { - MapperProperties = subscription.MapperProperties; - HandlerProperties = subscription.HandlerProperties; - } + } //retry xchange diff --git a/SW.Bitween.Api/Helpers/StartupValuesFiller.cs b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs index af803dd4..d550f1fd 100644 --- a/SW.Bitween.Api/Helpers/StartupValuesFiller.cs +++ b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs @@ -1,33 +1,92 @@ using System; using System.Collections.Generic; using System.Linq; +using SW.Bitween.Domain; namespace SW.Bitween; public static class StartupValuesFiller { - //{{partner.XY}} => input["XY"] + public static Dictionary Fill(this IDictionary inputTemplated, + Partner partner, GlobalAdapterValuesSet[] globals) + { + // First fill globals templates + var afterGlobals = inputTemplated.Fill(globals ?? []); + + // Then fill partner templates using AdapterProperties + var result = afterGlobals.Fill(partner.AdapterProperties ?? new Dictionary(), Partner.TemplateVariableNamePrefix); + + return result; + } + //{{partner.XY}} => input["XY"] + private static Dictionary Fill(this IDictionary inputTemplated, Dictionary input, string variableNamePrefix) { - var result = new Dictionary(); var prefix = $"{{{{{variableNamePrefix}."; // {{partner. + return FillTemplates(inputTemplated, prefix, (content) => + { + // Simple case: extract variable name and look up in input dictionary + // Look up in input dictionary (case-insensitive) + return input.FirstOrDefault(i => + i.Key.Equals(content, StringComparison.OrdinalIgnoreCase)).Value; + }); + } + + private static Dictionary Fill(this IDictionary inputTemplated, + GlobalAdapterValuesSet[] globals) + { + var prefix = "{{globals."; // {{globals. + + return FillTemplates(inputTemplated, prefix, (content) => + { + // Complex case: split into global ID and key name + var parts = content.Split('.', 2); + if (parts.Length != 2) + { + return null; // Keep original if format is invalid + } + + var globalId = parts[0]; + var keyName = parts[1]; + + // Find the matching global adapter values set + var globalSet = globals.FirstOrDefault(g => + g.Id.Equals(globalId, StringComparison.OrdinalIgnoreCase)); + + if (globalSet == null) + { + return null; // Keep original if global set not found + } + + // Look up the key in the Values dictionary (case-insensitive) + return globalSet.Values.FirstOrDefault(v => + v.Key.Equals(keyName, StringComparison.OrdinalIgnoreCase)).Value; + }); + } + + private static Dictionary FillTemplates( + IDictionary inputTemplated, + string prefix, + Func resolver) + { + var result = new Dictionary(); + foreach (var kvp in inputTemplated) { var value = kvp.Value; - // Check if value is a template like {{partner.XY}} + // Check if value is a template like {{prefix...}} if (value != null && value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && value.EndsWith("}}")) { - // Extract the variable name (e.g., "XY" from "{{partner.XY}}") - var variableName = value.Substring(prefix.Length, value.Length - prefix.Length - 2); + // Extract the content between prefix and }} + var content = value.Substring(prefix.Length, value.Length - prefix.Length - 2); - // Look up in input dictionary (case-insensitive) - var inputValue = input.FirstOrDefault(i => - i.Key.Equals(variableName, StringComparison.OrdinalIgnoreCase)).Value; + // Use the resolver to get the actual value + var resolvedValue = resolver(content); - result[kvp.Key] = inputValue ?? value; // Use original if not found + result[kvp.Key] = resolvedValue ?? value; // Use original if not found } else { diff --git a/SW.Bitween.Api/Interfaces/IInfolinkCache.cs b/SW.Bitween.Api/Interfaces/IInfolinkCache.cs index 9f941e79..36fff4fe 100644 --- a/SW.Bitween.Api/Interfaces/IInfolinkCache.cs +++ b/SW.Bitween.Api/Interfaces/IInfolinkCache.cs @@ -19,4 +19,7 @@ public interface IInfolinkCache Task ListWorkGroupsAsync(); Task WorkGroupByIdAsync(int workGroupId); Task WorkGroupBySubscriptionIdAsync(int subscriptionId); + + Task GlobalAdapterValuesSetById (string globalAdapterValuesSetId); + Task ListGlobalAdapterValuesSetsAsync(); } \ No newline at end of file diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs new file mode 100644 index 00000000..b0630f43 --- /dev/null +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using SW.Bitween.Domain; +using SW.Bitween.Domain.Accounts; +using SW.Bitween.Model; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Resources.GlobalAdapterValuesSets +{ + public class Create : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Create(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(GlobalAdapterValuesSetCreate request) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var exists = await _dbContext.Set().AnyAsync(x => x.Id == request.Id); + if (exists) + throw new SWValidationException("ID_EXISTS", $"GlobalAdapterValuesSet with id '{request.Id}' already exists"); + + var entity = new GlobalAdapterValuesSet + { + Id = request.Id, + Name = request.Name, + Values = request.Values + }; + + _dbContext.Add(entity); + await _dbContext.SaveChangesAsync(); + return entity.Id; + } + + private class Validate : AbstractValidator + { + public Validate() + { + RuleFor(i => i.Id).NotEmpty(); + RuleFor(i => i.Name).NotEmpty(); + RuleFor(i => i.Values).NotNull(); + } + } + } +} diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Delete.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Delete.cs new file mode 100644 index 00000000..6a8e557a --- /dev/null +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Delete.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using SW.Bitween.Domain; +using SW.Bitween.Domain.Accounts; +using SW.Bitween.Model; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Resources.GlobalAdapterValuesSets +{ + [HandlerName("delete")] + public class Delete : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Delete(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(string key, DeleteGlobalAdapterValuesSetModel _) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var entity = await _dbContext.Set().FindAsync(key); + if (entity is null) + throw new SWValidationException("NOT_FOUND", $"GlobalAdapterValuesSet with id {key} was not found"); + + _dbContext.Remove(entity); + await _dbContext.SaveChangesAsync(); + return null; + } + } +} diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Search.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Search.cs new file mode 100644 index 00000000..9f3d1f73 --- /dev/null +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Search.cs @@ -0,0 +1,44 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using SW.Bitween.Domain; +using SW.Bitween.Model; +using SW.EfCoreExtensions; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Resources.GlobalAdapterValuesSets +{ + public class Search : ISearchyHandler + { + private readonly BitweenDbContext _dbContext; + + public Search(BitweenDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(SearchyRequest searchyRequest, bool lookup = false, string searchPhrase = null) + { + var query = from item in _dbContext.Set() + select new GlobalAdapterValuesSetRow + { + Id = item.Id, + Name = item.Name, + Values = item.Values + }; + + query = query.AsNoTracking(); + + if (lookup) + { + return await query.Search(searchyRequest.Conditions).ToDictionaryAsync(k => k.Id, v => v.Name); + } + + return new SearchyResponse + { + TotalCount = await query.Search(searchyRequest.Conditions).CountAsync(), + Result = await query.Search(searchyRequest.Conditions, searchyRequest.Sorts, searchyRequest.PageSize, searchyRequest.PageIndex).ToListAsync() + }; + } + } +} diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Update.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Update.cs new file mode 100644 index 00000000..43090d1b --- /dev/null +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Update.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using FluentValidation; +using SW.Bitween.Domain; +using SW.Bitween.Domain.Accounts; +using SW.Bitween.Model; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Resources.GlobalAdapterValuesSets +{ + public class Update : ICommandHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Update(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(string key, GlobalAdapterValuesSetUpdate request) + { + _requestContext.EnsureAccess(AccountRole.Admin, AccountRole.Member); + + var entity = await _dbContext.Set().FindAsync(key); + if (entity is null) + throw new SWValidationException("NOT_FOUND", $"GlobalAdapterValuesSet with id {key} was not found"); + + entity.Name = request.Name; + entity.Values = request.Values; + + await _dbContext.SaveChangesAsync(); + return null; + } + + private class Validate : AbstractValidator + { + public Validate() + { + RuleFor(i => i.Name).NotEmpty(); + RuleFor(i => i.Values).NotNull(); + } + } + } +} diff --git a/SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs b/SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs index bf8022bb..05879daa 100644 --- a/SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs +++ b/SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs @@ -39,12 +39,14 @@ private async Task Load() var cachedDocuments = await repo.Set().AsNoTracking().ToArrayAsync(); var cachedNotifiers = await repo.Set().Where(i => !i.Inactive).AsNoTracking().ToArrayAsync(); var cachedWorkGroups = await repo.Set().AsNoTracking().ToArrayAsync(); + var cachedGlobalValues = await repo.Set().AsNoTracking().ToArrayAsync(); var span = TimeSpan.FromMinutes(10); _cache.Set(nameof(Document), cachedDocuments, span); _cache.Set(nameof(Subscription), cachedSubscriptions, span); _cache.Set(nameof(Notifier), cachedNotifiers, span); _cache.Set(nameof(WorkGroup), cachedWorkGroups, span); + _cache.Set(nameof(GlobalAdapterValuesSet), cachedGlobalValues, span); } public async Task ListSubscriptionsByDocumentAsync(int documentId) @@ -142,6 +144,28 @@ public async Task WorkGroupBySubscriptionIdAsync(int subscriptionId) return await WorkGroupByIdAsync(subscription.WorkGroupId.Value); } + public async Task GlobalAdapterValuesSetById(string globalAdapterValuesSetId) + { + if (!_cache.TryGetValue(nameof(GlobalAdapterValuesSet), out GlobalAdapterValuesSet[] cachedGlobalValues)) + { + await Load(); + return _cache.Get(nameof(GlobalAdapterValuesSet)).FirstOrDefault(gav => gav.Id == globalAdapterValuesSetId); + } + + return cachedGlobalValues.FirstOrDefault(gav => gav.Id == globalAdapterValuesSetId); + } + + public async Task ListGlobalAdapterValuesSetsAsync() + { + if (!_cache.TryGetValue(nameof(GlobalAdapterValuesSet), out GlobalAdapterValuesSet[] cachedGlobalValues)) + { + await Load(); + return _cache.Get(nameof(GlobalAdapterValuesSet)); + } + + return cachedGlobalValues; + } + public void Revoke() { _cache.Remove(nameof(Subscription)); diff --git a/SW.Bitween.Api/Services/XchangeService.cs b/SW.Bitween.Api/Services/XchangeService.cs index eff0d4c4..c27ccab2 100644 --- a/SW.Bitween.Api/Services/XchangeService.cs +++ b/SW.Bitween.Api/Services/XchangeService.cs @@ -108,9 +108,9 @@ public async Task CreateXchange(Document document, WorkGroup workGroup, } public async Task CreateXchange(Subscription subscription, XchangeFile file, - string[] references = null, string correlationId = null, Partner gatewayPartner = null) + string[] references = null, string correlationId = null, Partner gatewayPartner = null,GlobalAdapterValuesSet[] globalAdapterValuesSets = null) { - var xchange = new Xchange(subscription, file, references, correlationId); + var xchange = new Xchange(subscription, file, references, correlationId, gatewayPartner,globalAdapterValuesSets); await AddFile(xchange.Id, XchangeFileType.Input, file); _dbContext.Add(xchange); return xchange; diff --git a/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.Designer.cs b/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.Designer.cs new file mode 100644 index 00000000..8ba0bd6b --- /dev/null +++ b/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.Designer.cs @@ -0,0 +1,1107 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SW.Bitween; + +#nullable disable + +namespace SW.Bitween.MsSql.Migrations +{ + [DbContext(typeof(BitweenDbContext))] + [Migration("20260217150741_ApiGateWayAndGlobalValues")] + partial class ApiGateWayAndGlobalValues + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.23") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("Deleted") + .HasColumnType("bit"); + + b.Property("Disabled") + .HasColumnType("bit"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Email") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("EmailProvider") + .HasColumnType("tinyint"); + + b.Property("LoginMethods") + .HasColumnType("tinyint"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.Property("Password") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("Phone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)"); + + b.Property("Role") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.ToTable("Accounts", (string)null); + + b.HasData( + new + { + Id = 9999, + CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), + Deleted = false, + Disabled = false, + DisplayName = "Admin", + Email = "admin@Bitween.systems", + EmailProvider = (byte)0, + LoginMethods = (byte)2, + Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", + Role = 0 + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("LoginMethod") + .HasColumnType("tinyint"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.ToTable("RefreshTokens", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Document", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("BusEnabled") + .HasColumnType("bit"); + + b.Property("BusMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("DisregardsUnfilteredMessages") + .HasColumnType("bit"); + + b.Property("DocumentFormat") + .HasColumnType("int"); + + b.Property("DuplicateInterval") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("PromotedProperties") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BusMessageTypeName") + .IsUnique() + .HasFilter("[BusMessageTypeName] IS NOT NULL"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Documents", (string)null); + + b.HasData( + new + { + Id = 10001, + BusEnabled = false, + DocumentFormat = 0, + DuplicateInterval = 0, + Name = "Aggregation Document", + PromotedProperties = "{}" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("StateAfter") + .HasColumnType("nvarchar(max)"); + + b.Property("StateBefore") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedOn"); + + b.HasIndex("DocumentId"); + + b.ToTable("DocumentTrail"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("UrlName") + .IsUnique(); + + b.ToTable("ApiGateways", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("int"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("ApiGatewayPartners", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Values") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GlobalAdapterValuesSets", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Inactive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RunOnBadResult") + .HasColumnType("bit"); + + b.Property("RunOnFailedResult") + .HasColumnType("bit"); + + b.Property("RunOnSubscriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("RunOnSuccessfulResult") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("Notifiers", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BadData") + .HasColumnType("bit"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .HasColumnType("nvarchar(max)"); + + b.Property("References") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("OnHoldXchanges", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AdapterProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Partners", (string)null); + + b.HasData( + new + { + Id = 1, + Name = "SYSTEM" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AggregateOn") + .HasColumnType("datetime2"); + + b.Property("AggregationForId") + .HasColumnType("int"); + + b.Property("AggregationTarget") + .HasColumnType("tinyint"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ConsecutiveFailures") + .HasColumnType("int"); + + b.Property("DocumentFilter") + .HasColumnType("nvarchar(max)"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Inactive") + .HasColumnType("bit"); + + b.Property("IsRunning") + .HasColumnType("bit"); + + b.Property("LastException") + .HasColumnType("nvarchar(max)"); + + b.Property("MapperId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("MapperProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("MatchExpression") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("PausedOn") + .HasColumnType("datetime2"); + + b.Property("ReceiveOn") + .HasColumnType("datetime2"); + + b.Property("ReceiverId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ReceiverProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("int"); + + b.Property("Temporary") + .HasColumnType("bit"); + + b.Property("Type") + .HasColumnType("tinyint"); + + b.Property("ValidatorId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ValidatorProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("WorkGroupId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AggregationForId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DocumentId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("ResponseSubscriptionId"); + + b.HasIndex("WorkGroupId"); + + b.ToTable("Subscriptions", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique() + .HasFilter("[Code] IS NOT NULL"); + + b.ToTable("SubscriptionCategory"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("StateAfter") + .HasColumnType("nvarchar(max)"); + + b.Property("StateBefore") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CreatedOn"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionTrail"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BusMessageName") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Options") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("CorrelationId") + .HasColumnType("nvarchar(max)"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("InputContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("InputHash") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("InputName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("InputSize") + .HasColumnType("int"); + + b.Property("MapperId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("MapperProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("References") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("int"); + + b.Property("RetryFor") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("StartedOn") + .HasColumnType("datetime2"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("InputHash"); + + b.HasIndex("RetryFor"); + + b.HasIndex("StartedOn"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("Xchanges", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("AggregatedOn") + .HasColumnType("datetime2"); + + b.Property("AggregationXchangeId") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AggregationXchangeId"); + + b.ToTable("XchangeAggregations", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("DeliveredOn") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("DeliveredOn"); + + b.ToTable("XchangeDeliveries", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("nvarchar(max)"); + + b.Property("FinishedOn") + .HasColumnType("datetime2"); + + b.Property("NotifierId") + .HasColumnType("int"); + + b.Property("NotifierName") + .HasColumnType("nvarchar(max)"); + + b.Property("Success") + .HasColumnType("bit"); + + b.Property("XchangeId") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.ToTable("XchangeNotifications", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("Hits") + .HasMaxLength(2000) + .IsUnicode(false) + .HasColumnType("varchar(2000)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("PropertiesRaw") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("PropertiesRaw"); + + b.ToTable("XchangePromotedProperties", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("Exception") + .HasColumnType("nvarchar(max)"); + + b.Property("FinishedOn") + .HasColumnType("datetime2"); + + b.Property("OutputBad") + .HasColumnType("bit"); + + b.Property("OutputContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("OutputHash") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("OutputName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OutputSize") + .HasColumnType("int"); + + b.Property("ResponseBad") + .HasColumnType("bit"); + + b.Property("ResponseContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ResponseHash") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("ResponseName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ResponseSize") + .HasColumnType("int"); + + b.Property("ResponseXchangeId") + .HasColumnType("nvarchar(max)"); + + b.Property("Success") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.ToTable("XchangeResults", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => + { + b.Property("IsRunning") + .HasColumnType("bit"); + + b.ToTable((string)null); + + b.ToView(null, (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.HasOne("SW.Bitween.Domain.Accounts.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.HasOne("SW.Bitween.Domain.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => + { + b1.Property("PartnerId") + .HasColumnType("int"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("Key") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b1.HasKey("PartnerId", "Id"); + + b1.HasIndex("Key") + .IsUnique(); + + b1.ToTable("PartnerApiCredentials", (string)null); + + b1.WithOwner() + .HasForeignKey("PartnerId"); + + b1.HasData( + new + { + PartnerId = 1, + Id = 1, + Key = "7facc758283844b49cc4ffd26a75b1de", + Name = "default" + }); + }); + + b.Navigation("ApiCredentials"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("AggregationForId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_Subscriptions_AggFor"); + + b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId"); + + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", null) + .WithMany("Subscriptions") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("ResponseSubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_Subscriptions_RespSub"); + + b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") + .WithMany() + .HasForeignKey("WorkGroupId"); + + b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => + { + b1.Property("SubscriptionId") + .HasColumnType("int"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b1.Property("Id")); + + b1.Property("Backwards") + .HasColumnType("bit"); + + b1.Property("On") + .HasColumnType("bigint"); + + b1.Property("Recurrence") + .HasColumnType("tinyint"); + + b1.HasKey("SubscriptionId", "Id"); + + b1.ToTable("SubscriptionSchedules", (string)null); + + b1.WithOwner() + .HasForeignKey("SubscriptionId"); + }); + + b.Navigation("Category"); + + b.Navigation("Schedules"); + + b.Navigation("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.cs b/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.cs new file mode 100644 index 00000000..aed0d9df --- /dev/null +++ b/SW.Bitween.MsSql/Migrations/20260217150741_ApiGateWayAndGlobalValues.cs @@ -0,0 +1,120 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SW.Bitween.MsSql.Migrations +{ + /// + public partial class ApiGateWayAndGlobalValues : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AdapterProperties", + table: "Partners", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.CreateTable( + name: "ApiGateways", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + UrlName = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + CreatedOn = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + ModifiedOn = table.Column(type: "datetime2", nullable: true), + ModifiedBy = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiGateways", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "GlobalAdapterValuesSets", + columns: table => new + { + Id = table.Column(type: "varchar(200)", unicode: false, maxLength: 200, nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + Values = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_GlobalAdapterValuesSets", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ApiGatewayPartners", + columns: table => new + { + ApiGatewayId = table.Column(type: "int", nullable: false), + PartnerId = table.Column(type: "int", nullable: false), + SubscriptionId = table.Column(type: "int", nullable: false), + CreatedOn = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + ModifiedOn = table.Column(type: "datetime2", nullable: true), + ModifiedBy = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiGatewayPartners", x => new { x.ApiGatewayId, x.PartnerId, x.SubscriptionId }); + table.ForeignKey( + name: "FK_ApiGatewayPartners_ApiGateways_ApiGatewayId", + column: x => x.ApiGatewayId, + principalTable: "ApiGateways", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ApiGatewayPartners_Partners_PartnerId", + column: x => x.PartnerId, + principalTable: "Partners", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ApiGatewayPartners_Subscriptions_SubscriptionId", + column: x => x.SubscriptionId, + principalTable: "Subscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_ApiGatewayPartners_PartnerId", + table: "ApiGatewayPartners", + column: "PartnerId"); + + migrationBuilder.CreateIndex( + name: "IX_ApiGatewayPartners_SubscriptionId", + table: "ApiGatewayPartners", + column: "SubscriptionId"); + + migrationBuilder.CreateIndex( + name: "IX_ApiGateways_UrlName", + table: "ApiGateways", + column: "UrlName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiGatewayPartners"); + + migrationBuilder.DropTable( + name: "GlobalAdapterValuesSets"); + + migrationBuilder.DropTable( + name: "ApiGateways"); + + migrationBuilder.DropColumn( + name: "AdapterProperties", + table: "Partners"); + } + } +} diff --git a/SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs b/SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs index aca44dbd..d8c13c42 100644 --- a/SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs +++ b/SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("ProductVersion", "8.0.23") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -211,6 +211,94 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DocumentTrail"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("UrlName") + .IsUnique(); + + b.ToTable("ApiGateways", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("int"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedOn") + .HasColumnType("datetime2"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("ApiGatewayPartners", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Values") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("GlobalAdapterValuesSets", (string)null); + }); + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => { b.Property("Id") @@ -291,6 +379,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("AdapterProperties") + .HasColumnType("nvarchar(max)"); + b.Property("Name") .IsRequired() .HasMaxLength(200) @@ -800,6 +891,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Document"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => @@ -971,6 +1089,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.Navigation("Subscriptions"); diff --git a/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.Designer.cs b/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.Designer.cs new file mode 100644 index 00000000..efc157e5 --- /dev/null +++ b/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.Designer.cs @@ -0,0 +1,1104 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SW.Bitween; + +#nullable disable + +namespace SW.Bitween.MySql.Migrations +{ + [DbContext(typeof(BitweenDbContext))] + [Migration("20260217151131_ApiGateWayAndGlobalValues")] + partial class ApiGateWayAndGlobalValues + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.23") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Email") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("EmailProvider") + .HasColumnType("tinyint unsigned"); + + b.Property("LoginMethods") + .HasColumnType("tinyint unsigned"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.Property("Password") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("Phone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)"); + + b.Property("Role") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.ToTable("Accounts", (string)null); + + b.HasData( + new + { + Id = 9999, + CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), + Deleted = false, + Disabled = false, + DisplayName = "Admin", + Email = "admin@Bitween.systems", + EmailProvider = (byte)0, + LoginMethods = (byte)2, + Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", + Role = 0 + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("LoginMethod") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.ToTable("RefreshTokens", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Document", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("BusEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("BusMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("DisregardsUnfilteredMessages") + .HasColumnType("tinyint(1)"); + + b.Property("DocumentFormat") + .HasColumnType("int"); + + b.Property("DuplicateInterval") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("PromotedProperties") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("BusMessageTypeName") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Documents", (string)null); + + b.HasData( + new + { + Id = 10001, + BusEnabled = false, + DocumentFormat = 0, + DuplicateInterval = 0, + Name = "Aggregation Document", + PromotedProperties = "{}" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("StateAfter") + .HasColumnType("longtext"); + + b.Property("StateBefore") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedOn"); + + b.HasIndex("DocumentId"); + + b.ToTable("DocumentTrail"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("UrlName") + .IsUnique(); + + b.ToTable("ApiGateways", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("int"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("ApiGatewayPartners", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Values") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("GlobalAdapterValuesSets", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("longtext"); + + b.Property("Inactive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RunOnBadResult") + .HasColumnType("tinyint(1)"); + + b.Property("RunOnFailedResult") + .HasColumnType("tinyint(1)"); + + b.Property("RunOnSubscriptions") + .HasColumnType("longtext"); + + b.Property("RunOnSuccessfulResult") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Notifiers", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BadData") + .HasColumnType("tinyint(1)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("FileName") + .HasColumnType("longtext"); + + b.Property("References") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("OnHoldXchanges", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AdapterProperties") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.ToTable("Partners", (string)null); + + b.HasData( + new + { + Id = 1, + Name = "SYSTEM" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AggregateOn") + .HasColumnType("datetime(6)"); + + b.Property("AggregationForId") + .HasColumnType("int"); + + b.Property("AggregationTarget") + .HasColumnType("tinyint unsigned"); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ConsecutiveFailures") + .HasColumnType("int"); + + b.Property("DocumentFilter") + .HasColumnType("longtext"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("longtext"); + + b.Property("Inactive") + .HasColumnType("tinyint(1)"); + + b.Property("IsRunning") + .HasColumnType("tinyint(1)"); + + b.Property("LastException") + .HasColumnType("longtext"); + + b.Property("MapperId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("MapperProperties") + .HasColumnType("longtext"); + + b.Property("MatchExpression") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("PausedOn") + .HasColumnType("datetime(6)"); + + b.Property("ReceiveOn") + .HasColumnType("datetime(6)"); + + b.Property("ReceiverId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ReceiverProperties") + .HasColumnType("longtext"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("int"); + + b.Property("Temporary") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("ValidatorId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ValidatorProperties") + .HasColumnType("longtext"); + + b.Property("WorkGroupId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AggregationForId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("DocumentId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("ResponseSubscriptionId"); + + b.HasIndex("WorkGroupId"); + + b.ToTable("Subscriptions", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Code") + .HasColumnType("varchar(255)"); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("SubscriptionCategory"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Code") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("StateAfter") + .HasColumnType("longtext"); + + b.Property("StateBefore") + .HasColumnType("longtext"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CreatedOn"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionTrail"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BusMessageName") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Options") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("CorrelationId") + .HasColumnType("longtext"); + + b.Property("DocumentId") + .HasColumnType("int"); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("HandlerProperties") + .HasColumnType("longtext"); + + b.Property("InputContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("InputHash") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("InputName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("InputSize") + .HasColumnType("int"); + + b.Property("MapperId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("MapperProperties") + .HasColumnType("longtext"); + + b.Property("References") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("int"); + + b.Property("RetryFor") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("StartedOn") + .HasColumnType("datetime(6)"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("InputHash"); + + b.HasIndex("RetryFor"); + + b.HasIndex("StartedOn"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("Xchanges", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("AggregatedOn") + .HasColumnType("datetime(6)"); + + b.Property("AggregationXchangeId") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AggregationXchangeId"); + + b.ToTable("XchangeAggregations", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("DeliveredOn") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DeliveredOn"); + + b.ToTable("XchangeDeliveries", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("longtext"); + + b.Property("FinishedOn") + .HasColumnType("datetime(6)"); + + b.Property("NotifierId") + .HasColumnType("int"); + + b.Property("NotifierName") + .HasColumnType("longtext"); + + b.Property("Success") + .HasColumnType("tinyint(1)"); + + b.Property("XchangeId") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.ToTable("XchangeNotifications", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("Hits") + .HasMaxLength(2000) + .IsUnicode(false) + .HasColumnType("varchar(2000)"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("PropertiesRaw") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("PropertiesRaw"); + + b.ToTable("XchangePromotedProperties", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("Exception") + .HasColumnType("longtext"); + + b.Property("FinishedOn") + .HasColumnType("datetime(6)"); + + b.Property("OutputBad") + .HasColumnType("tinyint(1)"); + + b.Property("OutputContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("OutputHash") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("OutputName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("OutputSize") + .HasColumnType("int"); + + b.Property("ResponseBad") + .HasColumnType("tinyint(1)"); + + b.Property("ResponseContentType") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("ResponseHash") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)"); + + b.Property("ResponseName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ResponseSize") + .HasColumnType("int"); + + b.Property("ResponseXchangeId") + .HasColumnType("longtext"); + + b.Property("Success") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("XchangeResults", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => + { + b.Property("IsRunning") + .HasColumnType("tinyint(1)"); + + b.ToTable((string)null); + + b.ToView(null, (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.HasOne("SW.Bitween.Domain.Accounts.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.HasOne("SW.Bitween.Domain.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => + { + b1.Property("PartnerId") + .HasColumnType("int"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b1.Property("Id")); + + b1.Property("Key") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b1.HasKey("PartnerId", "Id"); + + b1.HasIndex("Key") + .IsUnique(); + + b1.ToTable("PartnerApiCredentials", (string)null); + + b1.WithOwner() + .HasForeignKey("PartnerId"); + + b1.HasData( + new + { + PartnerId = 1, + Id = 1, + Key = "7facc758283844b49cc4ffd26a75b1de", + Name = "default" + }); + }); + + b.Navigation("ApiCredentials"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("AggregationForId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_Subscriptions_AggFor"); + + b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId"); + + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", null) + .WithMany("Subscriptions") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("ResponseSubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_Subscriptions_RespSub"); + + b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") + .WithMany() + .HasForeignKey("WorkGroupId"); + + b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => + { + b1.Property("SubscriptionId") + .HasColumnType("int"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b1.Property("Id")); + + b1.Property("Backwards") + .HasColumnType("tinyint(1)"); + + b1.Property("On") + .HasColumnType("bigint"); + + b1.Property("Recurrence") + .HasColumnType("tinyint unsigned"); + + b1.HasKey("SubscriptionId", "Id"); + + b1.ToTable("SubscriptionSchedules", (string)null); + + b1.WithOwner() + .HasForeignKey("SubscriptionId"); + }); + + b.Navigation("Category"); + + b.Navigation("Schedules"); + + b.Navigation("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.cs b/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.cs new file mode 100644 index 00000000..8a0bb921 --- /dev/null +++ b/SW.Bitween.MySql/Migrations/20260217151131_ApiGateWayAndGlobalValues.cs @@ -0,0 +1,134 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SW.Bitween.MySql.Migrations +{ + /// + public partial class ApiGateWayAndGlobalValues : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AdapterProperties", + table: "Partners", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ApiGateways", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UrlName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedOn = table.Column(type: "datetime(6)", nullable: false), + CreatedBy = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ModifiedOn = table.Column(type: "datetime(6)", nullable: true), + ModifiedBy = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ApiGateways", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "GlobalAdapterValuesSets", + columns: table => new + { + Id = table.Column(type: "varchar(200)", unicode: false, maxLength: 200, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Values = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_GlobalAdapterValuesSets", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ApiGatewayPartners", + columns: table => new + { + ApiGatewayId = table.Column(type: "int", nullable: false), + PartnerId = table.Column(type: "int", nullable: false), + SubscriptionId = table.Column(type: "int", nullable: false), + CreatedOn = table.Column(type: "datetime(6)", nullable: false), + CreatedBy = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ModifiedOn = table.Column(type: "datetime(6)", nullable: true), + ModifiedBy = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ApiGatewayPartners", x => new { x.ApiGatewayId, x.PartnerId, x.SubscriptionId }); + table.ForeignKey( + name: "FK_ApiGatewayPartners_ApiGateways_ApiGatewayId", + column: x => x.ApiGatewayId, + principalTable: "ApiGateways", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ApiGatewayPartners_Partners_PartnerId", + column: x => x.PartnerId, + principalTable: "Partners", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ApiGatewayPartners_Subscriptions_SubscriptionId", + column: x => x.SubscriptionId, + principalTable: "Subscriptions", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_ApiGatewayPartners_PartnerId", + table: "ApiGatewayPartners", + column: "PartnerId"); + + migrationBuilder.CreateIndex( + name: "IX_ApiGatewayPartners_SubscriptionId", + table: "ApiGatewayPartners", + column: "SubscriptionId"); + + migrationBuilder.CreateIndex( + name: "IX_ApiGateways_UrlName", + table: "ApiGateways", + column: "UrlName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiGatewayPartners"); + + migrationBuilder.DropTable( + name: "GlobalAdapterValuesSets"); + + migrationBuilder.DropTable( + name: "ApiGateways"); + + migrationBuilder.DropColumn( + name: "AdapterProperties", + table: "Partners"); + } + } +} diff --git a/SW.Bitween.MySql/Migrations/BitweenDbContextModelSnapshot.cs b/SW.Bitween.MySql/Migrations/BitweenDbContextModelSnapshot.cs index c9560dbd..b40d5d53 100644 --- a/SW.Bitween.MySql/Migrations/BitweenDbContextModelSnapshot.cs +++ b/SW.Bitween.MySql/Migrations/BitweenDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("ProductVersion", "8.0.23") .HasAnnotation("Relational:MaxIdentifierLength", 64); MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); @@ -209,6 +209,94 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DocumentTrail"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("UrlName") + .IsUnique(); + + b.ToTable("ApiGateways", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("int"); + + b.Property("PartnerId") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("CreatedBy") + .HasColumnType("longtext"); + + b.Property("CreatedOn") + .HasColumnType("datetime(6)"); + + b.Property("ModifiedBy") + .HasColumnType("longtext"); + + b.Property("ModifiedOn") + .HasColumnType("datetime(6)"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId"); + + b.HasIndex("PartnerId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("ApiGatewayPartners", (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("varchar(200)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Values") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("GlobalAdapterValuesSets", (string)null); + }); + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => { b.Property("Id") @@ -289,6 +377,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("AdapterProperties") + .HasColumnType("longtext"); + b.Property("Name") .IsRequired() .HasMaxLength(200) @@ -797,6 +888,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Document"); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => @@ -968,6 +1086,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => { b.Navigation("Subscriptions"); diff --git a/SW.Bitween.PgSql/BitweenDbContext.cs b/SW.Bitween.PgSql/BitweenDbContext.cs index ce23e2c8..fec2da8a 100644 --- a/SW.Bitween.PgSql/BitweenDbContext.cs +++ b/SW.Bitween.PgSql/BitweenDbContext.cs @@ -140,6 +140,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired().OnDelete(DeleteBehavior.Restrict); }); + modelBuilder.Entity(gav => + { + gav.ToTable("global_adapter_values_set"); + gav.HasKey(i => i.Id); + gav.Property(p => p.Values).HasColumnType("jsonb"); + }); modelBuilder.Entity(b => { //b.ToTable("Subscriptions"); diff --git a/SW.Bitween.PgSql/DesignTimeDbContextFactory.cs b/SW.Bitween.PgSql/DesignTimeDbContextFactory.cs new file mode 100644 index 00000000..e69de29b diff --git a/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.Designer.cs b/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.Designer.cs new file mode 100644 index 00000000..72e3e041 --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.Designer.cs @@ -0,0 +1,1315 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using SW.Bitween.Model; +using SW.Bitween.PgSql; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + [DbContext(typeof(BitweenDbContext))] + [Migration("20260217152930_ApiGateWayAndGlobalValues")] + partial class ApiGateWayAndGlobalValues + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("infolink") + .HasAnnotation("ProductVersion", "8.0.23") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("Disabled") + .HasColumnType("boolean") + .HasColumnName("disabled"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("display_name"); + + b.Property("Email") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("email"); + + b.Property("EmailProvider") + .HasColumnType("smallint") + .HasColumnName("email_provider"); + + b.Property("LoginMethods") + .HasColumnType("smallint") + .HasColumnName("login_methods"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.Property("Password") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("character varying(500)") + .HasColumnName("password"); + + b.Property("Phone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character varying(20)") + .HasColumnName("phone"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.HasKey("Id") + .HasName("pk_accounts"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_accounts_email"); + + b.ToTable("Accounts", "infolink"); + + b.HasData( + new + { + Id = 9999, + CreatedOn = new DateTime(2021, 12, 31, 22, 0, 0, 0, DateTimeKind.Utc), + Deleted = false, + Disabled = false, + DisplayName = "Admin", + Email = "admin@Bitween.systems", + EmailProvider = (byte)0, + LoginMethods = (byte)2, + Password = "$SWHASH$V1$10000$VQCi48eitH4Ml5juvBMOFZrMdQwBbhuIQVXe6RR7qJdDF2bJ", + Role = 0 + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.Property("Id") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AccountId") + .HasColumnType("integer") + .HasColumnName("account_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("LoginMethod") + .HasColumnType("smallint") + .HasColumnName("login_method"); + + b.HasKey("Id") + .HasName("pk_refresh_tokens"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_refresh_tokens_account_id"); + + b.ToTable("RefreshTokens", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Document", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("BusEnabled") + .HasColumnType("boolean") + .HasColumnName("bus_enabled"); + + b.Property("BusMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("bus_message_type_name"); + + b.Property("DisregardsUnfilteredMessages") + .HasColumnType("boolean") + .HasColumnName("disregards_unfiltered_messages"); + + b.Property("DocumentFormat") + .HasColumnType("integer") + .HasColumnName("document_format"); + + b.Property("DuplicateInterval") + .HasColumnType("integer") + .HasColumnName("duplicate_interval"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PromotedProperties") + .HasColumnType("jsonb") + .HasColumnName("promoted_properties"); + + b.HasKey("Id") + .HasName("pk_document"); + + b.HasIndex("BusMessageTypeName") + .IsUnique() + .HasDatabaseName("ix_document_bus_message_type_name"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_document_name"); + + b.ToTable("document", "infolink"); + + b.HasData( + new + { + Id = 10001, + BusEnabled = false, + DocumentFormat = 0, + DuplicateInterval = 0, + Name = "Aggregation Document", + PromotedProperties = "{}" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.HasKey("Id") + .HasName("pk_document_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_document_trail_created_on"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_document_trail_document_id"); + + b.ToTable("document_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("url_name"); + + b.HasKey("Id") + .HasName("pk_api_gateway"); + + b.HasIndex("UrlName") + .IsUnique() + .HasDatabaseName("ix_api_gateway_url_name"); + + b.ToTable("api_gateway", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.Property("ApiGatewayId") + .HasColumnType("integer") + .HasColumnName("api_gateway_id"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") + .HasName("pk_api_gateway_partner"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_api_gateway_partner_partner_id"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_api_gateway_partner_subscription_id"); + + b.ToTable("api_gateway_partner", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property>("Values") + .HasColumnType("jsonb") + .HasColumnName("values"); + + b.HasKey("Id") + .HasName("pk_global_adapter_values_set"); + + b.ToTable("global_adapter_values_set", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("HandlerId") + .HasMaxLength(200) + .IsUnicode(false) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property("HandlerProperties") + .HasColumnType("text") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("RunOnBadResult") + .HasColumnType("boolean") + .HasColumnName("run_on_bad_result"); + + b.Property("RunOnFailedResult") + .HasColumnType("boolean") + .HasColumnName("run_on_failed_result"); + + b.Property("RunOnSubscriptions") + .HasColumnType("integer[]") + .HasColumnName("run_on_subscriptions"); + + b.Property("RunOnSuccessfulResult") + .HasColumnType("boolean") + .HasColumnName("run_on_successful_result"); + + b.HasKey("Id") + .HasName("pk_notifier"); + + b.ToTable("notifier", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.OnHoldXchange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BadData") + .HasColumnType("boolean") + .HasColumnName("bad_data"); + + b.Property("Data") + .HasColumnType("text") + .HasColumnName("data"); + + b.Property("FileName") + .HasColumnType("text") + .HasColumnName("file_name"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_on_hold_xchange"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_on_hold_xchange_subscription_id"); + + b.ToTable("on_hold_xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property>("AdapterProperties") + .HasColumnType("jsonb") + .HasColumnName("adapter_properties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_partner"); + + b.ToTable("partner", "infolink"); + + b.HasData( + new + { + Id = 1, + Name = "SYSTEM" + }); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AggregateOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregate_on"); + + b.Property("AggregationForId") + .HasColumnType("integer") + .HasColumnName("aggregation_for_id"); + + b.Property("AggregationTarget") + .HasColumnType("smallint") + .HasColumnName("aggregation_target"); + + b.Property("CategoryId") + .HasColumnType("integer") + .HasColumnName("category_id"); + + b.Property("ConsecutiveFailures") + .HasColumnType("integer") + .HasColumnName("consecutive_failures"); + + b.Property>("DocumentFilter") + .HasColumnType("jsonb") + .HasColumnName("document_filter"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("Inactive") + .HasColumnType("boolean") + .HasColumnName("inactive"); + + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.Property("LastException") + .HasColumnType("text") + .HasColumnName("last_exception"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("MatchExpression") + .HasColumnType("text") + .HasColumnName("match_expression"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b.Property("PausedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("paused_on"); + + b.Property("ReceiveOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("receive_on"); + + b.Property("ReceiverId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("receiver_id"); + + b.Property>("ReceiverProperties") + .HasColumnType("jsonb") + .HasColumnName("receiver_properties"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("Temporary") + .HasColumnType("boolean") + .HasColumnName("temporary"); + + b.Property("Type") + .HasColumnType("smallint") + .HasColumnName("type"); + + b.Property("ValidatorId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("validator_id"); + + b.Property>("ValidatorProperties") + .HasColumnType("jsonb") + .HasColumnName("validator_properties"); + + b.Property("WorkGroupId") + .HasColumnType("integer") + .HasColumnName("work_group_id"); + + b.HasKey("Id") + .HasName("pk_subscription"); + + b.HasIndex("AggregationForId") + .HasDatabaseName("ix_subscription_aggregation_for_id"); + + b.HasIndex("CategoryId") + .HasDatabaseName("ix_subscription_category_id"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_subscription_document_id"); + + b.HasIndex("PartnerId") + .HasDatabaseName("ix_subscription_partner_id"); + + b.HasIndex("ResponseSubscriptionId") + .HasDatabaseName("ix_subscription_response_subscription_id"); + + b.HasIndex("WorkGroupId") + .HasDatabaseName("ix_subscription_work_group_id"); + + b.ToTable("subscription", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .HasColumnType("text") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + + b.HasKey("Id") + .HasName("pk_subscription_category"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_subscription_category_code"); + + b.ToTable("subscription_category", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Code") + .HasColumnType("integer") + .HasColumnName("code"); + + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("StateAfter") + .HasColumnType("text") + .HasColumnName("state_after"); + + b.Property("StateBefore") + .HasColumnType("text") + .HasColumnName("state_before"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_subscription_trail"); + + b.HasIndex("CreatedOn") + .HasDatabaseName("ix_subscription_trail_created_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_subscription_trail_subscription_id"); + + b.ToTable("subscription_trail", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.WorkGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BusMessageName") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("character varying(100)") + .HasColumnName("bus_message_name"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Options") + .HasColumnType("jsonb") + .HasColumnName("options"); + + b.HasKey("Id") + .HasName("pk_work_group"); + + b.ToTable("work_group", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("CorrelationId") + .HasColumnType("text") + .HasColumnName("correlation_id"); + + b.Property("DocumentId") + .HasColumnType("integer") + .HasColumnName("document_id"); + + b.Property("HandlerId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("handler_id"); + + b.Property>("HandlerProperties") + .HasColumnType("jsonb") + .HasColumnName("handler_properties"); + + b.Property("InputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_content_type"); + + b.Property("InputHash") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("input_hash"); + + b.Property("InputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("input_name"); + + b.Property("InputSize") + .HasColumnType("integer") + .HasColumnName("input_size"); + + b.Property("MapperId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mapper_id"); + + b.Property>("MapperProperties") + .HasColumnType("jsonb") + .HasColumnName("mapper_properties"); + + b.Property("References") + .HasColumnType("text[]") + .HasColumnName("references"); + + b.Property("ResponseMessageTypeName") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("response_message_type_name"); + + b.Property("ResponseSubscriptionId") + .HasColumnType("integer") + .HasColumnName("response_subscription_id"); + + b.Property("RetryFor") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("retry_for"); + + b.Property("StartedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_on"); + + b.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b.HasKey("Id") + .HasName("pk_xchange"); + + b.HasIndex("DocumentId") + .HasDatabaseName("ix_xchange_document_id"); + + b.HasIndex("InputHash") + .HasDatabaseName("ix_xchange_input_hash"); + + b.HasIndex("RetryFor") + .HasDatabaseName("ix_xchange_retry_for"); + + b.HasIndex("StartedOn") + .HasDatabaseName("ix_xchange_started_on"); + + b.HasIndex("SubscriptionId") + .HasDatabaseName("ix_xchange_subscription_id"); + + b.ToTable("xchange", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("AggregatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("aggregated_on"); + + b.Property("AggregationXchangeId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("aggregation_xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_aggregation"); + + b.HasIndex("AggregationXchangeId") + .HasDatabaseName("ix_xchange_aggregation_aggregation_xchange_id"); + + b.ToTable("xchange_aggregation", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("DeliveredOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("delivered_on"); + + b.HasKey("Id") + .HasName("pk_xchange_delivery"); + + b.HasIndex("DeliveredOn") + .HasDatabaseName("ix_xchange_delivery_delivered_on"); + + b.ToTable("xchange_delivery", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("NotifierId") + .HasColumnType("integer") + .HasColumnName("notifier_id"); + + b.Property("NotifierName") + .HasColumnType("text") + .HasColumnName("notifier_name"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.Property("XchangeId") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("character varying(50)") + .HasColumnName("xchange_id"); + + b.HasKey("Id") + .HasName("pk_xchange_notification"); + + b.ToTable("xchange_notification", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Hits") + .HasColumnType("integer[]") + .HasColumnName("hits"); + + b.Property>("Properties") + .HasColumnType("jsonb") + .HasColumnName("properties"); + + b.Property("PropertiesRaw") + .HasColumnType("text") + .HasColumnName("properties_raw"); + + b.HasKey("Id") + .HasName("pk_xchange_promoted_properties"); + + b.HasIndex("PropertiesRaw") + .HasDatabaseName("ix_xchange_promoted_properties_properties_raw"); + + b.ToTable("xchange_promoted_properties", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("id"); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("FinishedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("finished_on"); + + b.Property("OutputBad") + .HasColumnType("boolean") + .HasColumnName("output_bad"); + + b.Property("OutputContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_content_type"); + + b.Property("OutputHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("output_hash"); + + b.Property("OutputName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("output_name"); + + b.Property("OutputSize") + .HasColumnType("integer") + .HasColumnName("output_size"); + + b.Property("ResponseBad") + .HasColumnType("boolean") + .HasColumnName("response_bad"); + + b.Property("ResponseContentType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_content_type"); + + b.Property("ResponseHash") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("response_hash"); + + b.Property("ResponseName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("response_name"); + + b.Property("ResponseSize") + .HasColumnType("integer") + .HasColumnName("response_size"); + + b.Property("ResponseXchangeId") + .HasColumnType("text") + .HasColumnName("response_xchange_id"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_xchange_result"); + + b.ToTable("xchange_result", "infolink"); + }); + + modelBuilder.Entity("SW.Bitween.RunFlagUpdater+RunningResult", b => + { + b.Property("IsRunning") + .HasColumnType("boolean") + .HasColumnName("is_running"); + + b.ToTable("running_result", "infolink"); + + b.ToView(null, (string)null); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Accounts.RefreshToken", b => + { + b.HasOne("SW.Bitween.Domain.Accounts.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_refresh_tokens_accounts_account_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.DocumentTrail", b => + { + b.HasOne("SW.Bitween.Domain.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_document_trail_document_document_id"); + + b.Navigation("Document"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => + { + b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") + .WithMany("Partners") + .HasForeignKey("ApiGatewayId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_api_gateway_api_gateway_id"); + + b.HasOne("SW.Bitween.Domain.Partner", "Partner") + .WithMany() + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_api_gateway_partner_subscription_subscription_id"); + + b.Navigation("ApiGateway"); + + b.Navigation("Partner"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.OwnsMany("SW.Bitween.Domain.ApiCredential", "ApiCredentials", b1 => + { + b1.Property("PartnerId") + .HasColumnType("integer") + .HasColumnName("partner_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Key") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("key"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("name"); + + b1.HasKey("PartnerId", "Id") + .HasName("pk_partner_api_credential"); + + b1.HasIndex("Key") + .IsUnique() + .HasDatabaseName("ix_partner_api_credential_key"); + + b1.ToTable("partner_api_credential", "infolink"); + + b1.WithOwner() + .HasForeignKey("PartnerId") + .HasConstraintName("fk_partner_api_credential_partner_partner_id"); + + b1.HasData( + new + { + PartnerId = 1, + Id = 1, + Key = "7facc758283844b49cc4ffd26a75b1de", + Name = "default" + }); + }); + + b.Navigation("ApiCredentials"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Subscription", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("AggregationForId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_aggregation_for"); + + b.HasOne("SW.Bitween.Domain.SubscriptionCategory", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .HasConstraintName("fk_subscription_subscription_category_category_id"); + + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_subscription_document_document_id"); + + b.HasOne("SW.Bitween.Domain.Partner", null) + .WithMany("Subscriptions") + .HasForeignKey("PartnerId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_partner_partner_id"); + + b.HasOne("SW.Bitween.Domain.Subscription", null) + .WithMany() + .HasForeignKey("ResponseSubscriptionId") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_subscription_response_subscriber"); + + b.HasOne("SW.Bitween.Domain.WorkGroup", "WorkGroup") + .WithMany() + .HasForeignKey("WorkGroupId") + .HasConstraintName("fk_subscription_work_group_work_group_id"); + + b.OwnsMany("SW.Bitween.Domain.Schedule", "Schedules", b1 => + { + b1.Property("SubscriptionId") + .HasColumnType("integer") + .HasColumnName("subscription_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Backwards") + .HasColumnType("boolean") + .HasColumnName("backwards"); + + b1.Property("On") + .HasColumnType("bigint") + .HasColumnName("on"); + + b1.Property("Recurrence") + .HasColumnType("smallint") + .HasColumnName("recurrence"); + + b1.HasKey("SubscriptionId", "Id") + .HasName("pk_subscription_schedule"); + + b1.ToTable("subscription_schedule", "infolink"); + + b1.WithOwner() + .HasForeignKey("SubscriptionId") + .HasConstraintName("fk_subscription_schedule_subscription_subscription_id"); + }); + + b.Navigation("Category"); + + b.Navigation("Schedules"); + + b.Navigation("WorkGroup"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.SubscriptionTrail", b => + { + b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_subscription_trail_subscription_subscription_id"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Xchange", b => + { + b.HasOne("SW.Bitween.Domain.Document", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_xchange_document_document_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeAggregation", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeAggregation", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_aggregation_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeDelivery", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeDelivery", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_delivery_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangePromotedProperties", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangePromotedProperties", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_promoted_properties_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.XchangeResult", b => + { + b.HasOne("SW.Bitween.Domain.Xchange", null) + .WithOne() + .HasForeignKey("SW.Bitween.Domain.XchangeResult", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_xchange_result_xchange_id"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => + { + b.Navigation("Partners"); + }); + + modelBuilder.Entity("SW.Bitween.Domain.Partner", b => + { + b.Navigation("Subscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.cs b/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.cs new file mode 100644 index 00000000..86f50999 --- /dev/null +++ b/SW.Bitween.PgSql/Migrations/20260217152930_ApiGateWayAndGlobalValues.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace SW.Bitween.PgSql.Migrations +{ + /// + public partial class ApiGateWayAndGlobalValues : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn>( + name: "adapter_properties", + schema: "infolink", + table: "partner", + type: "jsonb", + nullable: true); + + migrationBuilder.CreateTable( + name: "api_gateway", + schema: "infolink", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + url_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + created_on = table.Column(type: "timestamp with time zone", nullable: false), + created_by = table.Column(type: "text", nullable: true), + modified_on = table.Column(type: "timestamp with time zone", nullable: true), + modified_by = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "global_adapter_values_set", + schema: "infolink", + columns: table => new + { + id = table.Column(type: "text", nullable: false), + name = table.Column(type: "text", nullable: true), + values = table.Column>(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_global_adapter_values_set", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "api_gateway_partner", + schema: "infolink", + columns: table => new + { + api_gateway_id = table.Column(type: "integer", nullable: false), + partner_id = table.Column(type: "integer", nullable: false), + subscription_id = table.Column(type: "integer", nullable: false), + created_on = table.Column(type: "timestamp with time zone", nullable: false), + created_by = table.Column(type: "text", nullable: true), + modified_on = table.Column(type: "timestamp with time zone", nullable: true), + modified_by = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("pk_api_gateway_partner", x => new { x.api_gateway_id, x.partner_id, x.subscription_id }); + table.ForeignKey( + name: "fk_api_gateway_partner_api_gateway_api_gateway_id", + column: x => x.api_gateway_id, + principalSchema: "infolink", + principalTable: "api_gateway", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_partner_partner_id", + column: x => x.partner_id, + principalSchema: "infolink", + principalTable: "partner", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_api_gateway_partner_subscription_subscription_id", + column: x => x.subscription_id, + principalSchema: "infolink", + principalTable: "subscription", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_url_name", + schema: "infolink", + table: "api_gateway", + column: "url_name", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_partner_id", + schema: "infolink", + table: "api_gateway_partner", + column: "partner_id"); + + migrationBuilder.CreateIndex( + name: "ix_api_gateway_partner_subscription_id", + schema: "infolink", + table: "api_gateway_partner", + column: "subscription_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "api_gateway_partner", + schema: "infolink"); + + migrationBuilder.DropTable( + name: "global_adapter_values_set", + schema: "infolink"); + + migrationBuilder.DropTable( + name: "api_gateway", + schema: "infolink"); + + migrationBuilder.DropColumn( + name: "adapter_properties", + schema: "infolink", + table: "partner"); + } + } +} diff --git a/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs b/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs index e0184d09..2625c6c7 100644 --- a/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs +++ b/SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs @@ -261,21 +261,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + b.Property("Name") .IsRequired() .HasMaxLength(200) .HasColumnType("character varying(200)") .HasColumnName("name"); - b.Property("SubscriptionId") - .HasColumnType("integer") - .HasColumnName("subscription_id"); + b.Property("UrlName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("url_name"); b.HasKey("Id") .HasName("pk_api_gateway"); - b.HasIndex("SubscriptionId") - .HasDatabaseName("ix_api_gateway_subscription_id"); + b.HasIndex("UrlName") + .IsUnique() + .HasDatabaseName("ix_api_gateway_url_name"); b.ToTable("api_gateway", "infolink"); }); @@ -290,10 +309,26 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("partner_id"); - b.Property("SubscriptionId") + b.Property("SubscriptionId") .HasColumnType("integer") .HasColumnName("subscription_id"); + b.Property("CreatedBy") + .HasColumnType("text") + .HasColumnName("created_by"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("ModifiedBy") + .HasColumnType("text") + .HasColumnName("modified_by"); + + b.Property("ModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("modified_on"); + b.HasKey("ApiGatewayId", "PartnerId", "SubscriptionId") .HasName("pk_api_gateway_partner"); @@ -306,6 +341,26 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("api_gateway_partner", "infolink"); }); + modelBuilder.Entity("SW.Bitween.Domain.GlobalAdapterValuesSet", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property>("Values") + .HasColumnType("jsonb") + .HasColumnName("values"); + + b.HasKey("Id") + .HasName("pk_global_adapter_values_set"); + + b.ToTable("global_adapter_values_set", "infolink"); + }); + modelBuilder.Entity("SW.Bitween.Domain.Notifier", b => { b.Property("Id") @@ -404,9 +459,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property>("AdditionalValues") + b.Property>("AdapterProperties") .HasColumnType("jsonb") - .HasColumnName("additional_values"); + .HasColumnName("adapter_properties"); b.Property("Name") .IsRequired() @@ -1018,18 +1073,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Document"); }); - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGateway", b => - { - b.HasOne("SW.Bitween.Domain.Subscription", "Subscription") - .WithMany() - .HasForeignKey("SubscriptionId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired() - .HasConstraintName("fk_api_gateway_subscription_subscription_id"); - - b.Navigation("Subscription"); - }); - modelBuilder.Entity("SW.Bitween.Domain.Gateway.ApiGatewayPartner", b => { b.HasOne("SW.Bitween.Domain.Gateway.ApiGateway", "ApiGateway") diff --git a/SW.Bitween.PgSql/SW.Bitween.PgSql.csproj b/SW.Bitween.PgSql/SW.Bitween.PgSql.csproj index de895838..37dc5a6a 100644 --- a/SW.Bitween.PgSql/SW.Bitween.PgSql.csproj +++ b/SW.Bitween.PgSql/SW.Bitween.PgSql.csproj @@ -16,4 +16,10 @@ + + + + + + diff --git a/SW.Bitween.Sdk/Model/GlobalAdapterValuesSet.cs b/SW.Bitween.Sdk/Model/GlobalAdapterValuesSet.cs new file mode 100644 index 00000000..3e93caf1 --- /dev/null +++ b/SW.Bitween.Sdk/Model/GlobalAdapterValuesSet.cs @@ -0,0 +1,25 @@ +using SW.PrimitiveTypes; +using System.Collections.Generic; + +namespace SW.Bitween.Model +{ + public class GlobalAdapterValuesSetCreate : IName + { + public string Id { get; set; } + public string Name { get; set; } + public Dictionary Values { get; set; } + } + + public class GlobalAdapterValuesSetRow : GlobalAdapterValuesSetUpdate + { + public string Id { get; set; } + } + + public class GlobalAdapterValuesSetUpdate : GlobalAdapterValuesSetCreate + { + } + + public class DeleteGlobalAdapterValuesSetModel + { + } +} From a93be33b68060c3431f461699e6f22a1896b0c99 Mon Sep 17 00:00:00 2001 From: AhmadAbuhussein Date: Wed, 18 Feb 2026 16:49:22 +0300 Subject: [PATCH 06/10] Add Get handler for GlobalAdapterValuesSet and modify Create response format --- .../GlobalAdapterValuesSets/Create.cs | 5 ++- .../Resources/GlobalAdapterValuesSets/Get.cs | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Get.cs diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs index b0630f43..6d131146 100644 --- a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Create.cs @@ -36,7 +36,10 @@ public async Task Handle(GlobalAdapterValuesSetCreate request) _dbContext.Add(entity); await _dbContext.SaveChangesAsync(); - return entity.Id; + return new + { + entity.Id + }; } private class Validate : AbstractValidator diff --git a/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Get.cs b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Get.cs new file mode 100644 index 00000000..57a229e1 --- /dev/null +++ b/SW.Bitween.Api/Resources/GlobalAdapterValuesSets/Get.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using SW.Bitween.Domain; +using SW.Bitween.Model; +using SW.PrimitiveTypes; + +namespace SW.Bitween.Resources.GlobalAdapterValuesSets +{ + public class Get : IGetHandler + { + private readonly BitweenDbContext _dbContext; + private readonly RequestContext _requestContext; + + public Get(BitweenDbContext dbContext, RequestContext requestContext) + { + _dbContext = dbContext; + _requestContext = requestContext; + } + + public async Task Handle(string key) + { + _requestContext.EnsureAccess(Domain.Accounts.AccountRole.Admin, Domain.Accounts.AccountRole.Member, Domain.Accounts.AccountRole.Viewer); + + var entity = await _dbContext.Set() + .AsNoTracking() + .FirstOrDefaultAsync(x => x.Id == key); + + if (entity == null) + throw new SWNotFoundException($"GlobalAdapterValuesSet with id '{key}' was not found"); + + return new GlobalAdapterValuesSetRow + { + Id = entity.Id, + Name = entity.Name, + Values = entity.Values + }; + } + } +} From c17bf89f4db37f65a347f78b06f2bc0b09fad0a0 Mon Sep 17 00:00:00 2001 From: AhmadAbuhussein Date: Wed, 18 Feb 2026 17:25:13 +0300 Subject: [PATCH 07/10] Add AdapterProperties to Partner entity and update Get and Update handlers --- SW.Bitween.Api/Resources/Partners/Get.cs | 4 +++- SW.Bitween.Api/Resources/Partners/Update.cs | 1 + SW.Bitween.Sdk/Model/Partner.cs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/SW.Bitween.Api/Resources/Partners/Get.cs b/SW.Bitween.Api/Resources/Partners/Get.cs index d4ac2740..bf96629b 100644 --- a/SW.Bitween.Api/Resources/Partners/Get.cs +++ b/SW.Bitween.Api/Resources/Partners/Get.cs @@ -38,7 +38,9 @@ async public Task Handle(int key) Type = sub.Type, DocumentId = sub.DocumentId, - }).ToList() + }).ToList(), + + AdapterProperties = partner.AdapterProperties }).AsNoTracking().SingleOrDefaultAsync(); } diff --git a/SW.Bitween.Api/Resources/Partners/Update.cs b/SW.Bitween.Api/Resources/Partners/Update.cs index 7be363f1..52d6601d 100644 --- a/SW.Bitween.Api/Resources/Partners/Update.cs +++ b/SW.Bitween.Api/Resources/Partners/Update.cs @@ -29,6 +29,7 @@ public async Task Handle(int key, PartnerUpdate model) var entity = await _dbContext.FindAsync(key); entity.SetApiCredentials(model.ApiCredentials.Select(kv => new ApiCredential(kv.Key, kv.Value))); + entity.AdapterProperties = model.AdapterProperties; _dbContext.Entry(entity).SetProperties(model); await _dbContext.SaveChangesAsync(); return null; diff --git a/SW.Bitween.Sdk/Model/Partner.cs b/SW.Bitween.Sdk/Model/Partner.cs index 8ab0df40..fa977724 100644 --- a/SW.Bitween.Sdk/Model/Partner.cs +++ b/SW.Bitween.Sdk/Model/Partner.cs @@ -20,5 +20,6 @@ public class PartnerUpdate : PartnerCreate { public ICollection ApiCredentials { get; set; } public ICollection Subscriptions { get; set; } + public Dictionary AdapterProperties { get; set; } } } From 9e625f97e6b7c21ef6d1c46f76a717a4627ee598 Mon Sep 17 00:00:00 2001 From: AhmadAbuhussein Date: Wed, 25 Feb 2026 13:24:55 +0300 Subject: [PATCH 08/10] Add handler names for AddPartner, RemovePartner, and UpdatePartner; enhance validation logic for subscription types --- .../Controllers/GatewayController.cs | 26 ++++++----- SW.Bitween.Api/Domain/Xchange/Xchange.cs | 1 + .../Extensions/InfolinkDbContextExtensions.cs | 17 +++++-- SW.Bitween.Api/Helpers/StartupValuesFiller.cs | 38 +++++++++++---- .../Resources/ApiGateways/AddPartner.cs | 1 + SW.Bitween.Api/Resources/ApiGateways/Get.cs | 33 +++++++------ .../Resources/ApiGateways/RemovePartner.cs | 1 + .../Resources/ApiGateways/UpdatePartner.cs | 1 + .../Resources/Subscriptions/Create.cs | 2 +- .../Resources/Subscriptions/Update.cs | 46 +++++++++++-------- SW.Bitween.Api/Services/XchangeService.cs | 31 +++++++------ .../HttpHandler/HttpHandler.cs | 5 +- SW.Bitween.Web/Startup.cs | 1 + 13 files changed, 128 insertions(+), 75 deletions(-) diff --git a/SW.Bitween.Api/Controllers/GatewayController.cs b/SW.Bitween.Api/Controllers/GatewayController.cs index 1c835983..7b386659 100644 --- a/SW.Bitween.Api/Controllers/GatewayController.cs +++ b/SW.Bitween.Api/Controllers/GatewayController.cs @@ -22,9 +22,8 @@ public class GatewayController( XchangeService xchangeService, BitweenOptions bitweenSettings) : ControllerBase { - [HttpPost("{gatewayApiName}/sync")] - public Task PostSync([FromRoute] string gatewayApiName) + public Task PostSync([FromRoute] string gatewayApiName) { return ProcessAsync(gatewayApiName, resultSync: true); } @@ -44,30 +43,35 @@ private async Task ProcessAsync([FromRoute] string gatewayApiName .FirstOrDefaultAsync(ag => ag.UrlName == gatewayApiName); if (apiGateway == null) - throw new SWNotFoundException($"API Gateway with URL name '{gatewayApiName}' not found"); + return NotFound(); // Resolve partner using API key - var (partner, keyName) = await dbContext.AuthorizePartner(requestContext); + 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) - throw new SWUnauthorizedException("Partner is not authorized for this API Gateway"); + 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 { $"partnerkey: {keyName}" }; - var xchangeId= await xchangeService.SubmitSubscriptionXchange(subscription.Id, xchangeFile, xchangeReferences.ToArray()); + var globalAdapterValuesSets = await dbContext.Set().ToArrayAsync(); + var xchangeId = await xchangeService.SubmitSubscriptionXchange(subscription.Id, xchangeFile, + xchangeReferences.ToArray(), partner, globalAdapterValuesSets); if (!resultSync) { return Accepted(xchangeId); @@ -80,7 +84,7 @@ await xchangeService.RunValidator(subscription.ValidatorId, validatorProperties, { waitResponse = waitResponseValue <= 0 ? 120 : waitResponseValue; } - + var currentFibTerm = 1; var previousTerm = 1; while (currentFibTerm <= waitResponse) @@ -111,14 +115,12 @@ await xchangeService.RunValidator(subscription.ValidatorId, validatorProperties, Content = response, ContentType = xchangeResult.ResponseContentType ?? MediaTypeNames.Application.Json, }; - } case false: - throw new SWValidationException("failure", "Internal processing error."); + return BadRequest(); } } return Accepted(xchangeId); } - } \ No newline at end of file diff --git a/SW.Bitween.Api/Domain/Xchange/Xchange.cs b/SW.Bitween.Api/Domain/Xchange/Xchange.cs index f8363be4..9233a37a 100644 --- a/SW.Bitween.Api/Domain/Xchange/Xchange.cs +++ b/SW.Bitween.Api/Domain/Xchange/Xchange.cs @@ -29,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) diff --git a/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs b/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs index d82e38fb..6c8530fb 100644 --- a/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs +++ b/SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs @@ -11,11 +11,21 @@ static class BitweenDbContextExtensions { public static async Task<(Partner Partner, string KeyName)> AuthorizePartner(this BitweenDbContext dbContext, RequestContext requestContext) + { + var (partnerAuthorized, partner, keyName) = await dbContext.CheckPartnerAuthorized(requestContext); + return !partnerAuthorized + ? throw new SWUnauthorizedException("Invalid or missing partner key") + : (partner, keyName); + } + + public static async Task<(bool Authorized, Partner Partner, string KeyName)> CheckPartnerAuthorized( + this BitweenDbContext dbContext, + RequestContext requestContext) { var partnerKey = requestContext.Values.Where(item => item.Name.ToLower() == "partnerkey") .Select(item => item.Value).FirstOrDefault(); if (partnerKey == null) - throw new SWUnauthorizedException(); + return (false, null, null); var partnerQuery = from partner in dbContext.Set() where partner.ApiCredentials.Any(cred => cred.Key == partnerKey) @@ -23,13 +33,12 @@ where partner.ApiCredentials.Any(cred => cred.Key == partnerKey) var par = await partnerQuery.AsNoTracking().SingleOrDefaultAsync(); if (par == null) - throw new SWUnauthorizedException(); + return (false, null, null); - return (par, par.ApiCredentials.Single(c => c.Key == partnerKey).Name); + return (true, par, par.ApiCredentials.Single(c => c.Key == partnerKey).Name); } public static IQueryable Subscriptions(this BitweenDbContext dbContext) => dbContext.Set().Include(s => s.WorkGroup); } - } \ No newline at end of file diff --git a/SW.Bitween.Api/Helpers/StartupValuesFiller.cs b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs index d550f1fd..e94cf91e 100644 --- a/SW.Bitween.Api/Helpers/StartupValuesFiller.cs +++ b/SW.Bitween.Api/Helpers/StartupValuesFiller.cs @@ -65,7 +65,7 @@ private static Dictionary Fill(this IDictionary v.Key.Equals(keyName, StringComparison.OrdinalIgnoreCase)).Value; }); } - + private static Dictionary FillTemplates( IDictionary inputTemplated, string prefix, @@ -77,20 +77,40 @@ private static Dictionary FillTemplates( { var value = kvp.Value; - // Check if value is a template like {{prefix...}} - if (value != null && value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && value.EndsWith("}}")) + if (value != null && value.Contains(prefix, StringComparison.OrdinalIgnoreCase)) { - // Extract the content between prefix and }} - var content = value.Substring(prefix.Length, value.Length - prefix.Length - 2); + var sb = new System.Text.StringBuilder(value); + var searchFrom = 0; - // Use the resolver to get the actual value - var resolvedValue = resolver(content); + while (true) + { + var current = sb.ToString(); + var start = current.IndexOf(prefix, searchFrom, StringComparison.OrdinalIgnoreCase); + if (start == -1) break; + + var end = current.IndexOf("}}", start + prefix.Length, StringComparison.Ordinal); + if (end == -1) break; + + var content = current.Substring(start + prefix.Length, end - start - prefix.Length); + var resolvedValue = resolver(content); + + if (resolvedValue != null) + { + var fullToken = current.Substring(start, end - start + 2); + sb.Replace(fullToken, resolvedValue, start, fullToken.Length); + searchFrom = start + resolvedValue.Length; + } + else + { + // Skip past this token to avoid infinite loop + searchFrom = end + 2; + } + } - result[kvp.Key] = resolvedValue ?? value; // Use original if not found + result[kvp.Key] = sb.ToString(); } else { - // Keep the original value if it's not a template result[kvp.Key] = value; } } diff --git a/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs b/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs index 310662da..78e08409 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/AddPartner.cs @@ -9,6 +9,7 @@ namespace SW.Bitween.Resources.ApiGateways { + [HandlerName(nameof(AddPartner))] public class AddPartner : ICommandHandler { private readonly BitweenDbContext _dbContext; diff --git a/SW.Bitween.Api/Resources/ApiGateways/Get.cs b/SW.Bitween.Api/Resources/ApiGateways/Get.cs index 28347b3d..0b935c0b 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/Get.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/Get.cs @@ -18,26 +18,31 @@ public Get(BitweenDbContext dbContext) public async Task Handle(int key) { - return await _dbContext.Set() + var gateway = await _dbContext.Set() .AsNoTracking() .Include(ag => ag.Partners) .ThenInclude(p => p.Partner) .Include(ag => ag.Partners) .ThenInclude(p => p.Subscription) - .Where(ag => ag.Id == key) - .Select(gateway => new ApiGatewayUpdate + .FirstOrDefaultAsync(ag => ag.Id == key); + + if (gateway == null) + throw new SWNotFoundException($"ApiGateway with id '{key}' was not found"); + + return new ApiGatewayRow + { + Id = gateway.Id, + Name = gateway.Name, + UrlName = gateway.UrlName, + PartnersCount = gateway.Partners.Count, + Partners = gateway.Partners.Select(p => new ApiGatewayPartnerDto { - Name = gateway.Name, - UrlName = gateway.UrlName, - Partners = gateway.Partners.Select(p => new ApiGatewayPartnerDto - { - PartnerId = p.PartnerId, - SubscriptionId = p.SubscriptionId, - PartnerName = p.Partner.Name, - SubscriptionName = p.Subscription.Name - }).ToList() - }) - .SingleOrDefaultAsync(); + PartnerId = p.PartnerId, + SubscriptionId = p.SubscriptionId, + PartnerName = p.Partner.Name, + SubscriptionName = p.Subscription.Name + }).ToList() + }; } } } diff --git a/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs index 07d41e76..610ac43d 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/RemovePartner.cs @@ -7,6 +7,7 @@ namespace SW.Bitween.Resources.ApiGateways { + [HandlerName(nameof(RemovePartner))] public class RemovePartner : ICommandHandler { private readonly BitweenDbContext _dbContext; diff --git a/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs b/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs index caf4a731..87405abb 100644 --- a/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs +++ b/SW.Bitween.Api/Resources/ApiGateways/UpdatePartner.cs @@ -9,6 +9,7 @@ namespace SW.Bitween.Resources.ApiGateways { + [HandlerName(nameof(UpdatePartner))] public class UpdatePartner : ICommandHandler { private readonly BitweenDbContext _dbContext; diff --git a/SW.Bitween.Api/Resources/Subscriptions/Create.cs b/SW.Bitween.Api/Resources/Subscriptions/Create.cs index 7d15bfea..c6afb528 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Create.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Create.cs @@ -61,7 +61,7 @@ public Validate() RuleFor(i => i.PartnerId).NotEqual(Partner.SystemId); RuleFor(i => i.Type).NotEqual(SubscriptionType.Unknown); - When(i => i.Type != SubscriptionType.Receiving, () => { RuleFor(i => i.PartnerId).NotEmpty(); }); + When(i => (i.Type != SubscriptionType.Receiving && i.Type != SubscriptionType.GatewayApiCall), () => { RuleFor(i => i.PartnerId).NotEmpty(); }); When(i => i.Type == SubscriptionType.Aggregation, () => { RuleFor(i => i.AggregationForId).NotEmpty(); }); diff --git a/SW.Bitween.Api/Resources/Subscriptions/Update.cs b/SW.Bitween.Api/Resources/Subscriptions/Update.cs index 1a77f310..21bc5906 100644 --- a/SW.Bitween.Api/Resources/Subscriptions/Update.cs +++ b/SW.Bitween.Api/Resources/Subscriptions/Update.cs @@ -7,6 +7,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using SW.Bitween.Domain.Accounts; namespace SW.Bitween.Resources.Subscriptions @@ -92,7 +93,19 @@ private static bool ValidateMatch(IPropertyMatchSpecification model) private class Validate : AbstractValidator { - public Validate(IServiceProvider serviceProvider) + private ValueTask GetSub(BitweenDbContext dbContext,IHttpContextAccessor httpContextAccessor) + { + var path = httpContextAccessor.HttpContext?.Request.Path.Value; + + var lastSegment = path? + .Split('/', StringSplitOptions.RemoveEmptyEntries) + .LastOrDefault(); + if(lastSegment is null || !int.TryParse(lastSegment, out var subId)) + return new ValueTask((Subscription)null); + + return dbContext.FindAsync(subId); + } + public Validate(BitweenDbContext dbContext,IHttpContextAccessor httpContextAccessor,NativeAdapterDiscoveryService nativeAdapterDiscovery, IServerlessService serverless) { RuleFor(i => i.Name).NotEmpty(); RuleFor(i => i.MatchExpression).Must(ValidateMatch); @@ -100,7 +113,7 @@ public Validate(IServiceProvider serviceProvider) When(i => i.MapperId != null, () => { - RuleFor(i => i.MapperProperties).CustomAsync(async (i, context, ct) => + RuleFor(i => i.MapperProperties).CustomAsync(async (i, context, _) => { var mapperId = ((SubscriptionUpdate)context.InstanceToValidate).MapperId; var mustProps = Enumerable.Empty(); @@ -108,13 +121,11 @@ public Validate(IServiceProvider serviceProvider) // Check if it's a native adapter if (mapperId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) { - var nativeAdapterDiscovery = serviceProvider.GetService(); var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(mapperId); mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); } else { - var serverless = serviceProvider.GetService(); await serverless.StartAsync(mapperId, null); mustProps = (await serverless.GetExpectedStartupValues()) .Where(p => p.Value.Optional == false).Select(p => p.Key); @@ -137,13 +148,12 @@ public Validate(IServiceProvider serviceProvider) // Check if it's a native adapter if (handlerId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) { - var nativeAdapterDiscovery = serviceProvider.GetService(); var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(handlerId); mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); } else { - var serverless = serviceProvider.GetService(); + await serverless.StartAsync(handlerId, null); mustProps = (await serverless.GetExpectedStartupValues()) .Where(p => p.Value.Optional == false).Select(p => p.Key); @@ -158,14 +168,13 @@ public Validate(IServiceProvider serviceProvider) RuleFor(i => i).CustomAsync(async (model, context, ct) => { - var dbContext = serviceProvider.GetService(); - var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); - + var subscription = await GetSub(dbContext, httpContextAccessor); + if (subscription?.Type == SubscriptionType.Receiving) { if (string.IsNullOrEmpty(model.ReceiverId)) context.AddFailure(nameof(model.ReceiverId), "ReceiverId is required for Receiving subscriptions"); - + if (model.Schedules == null || !model.Schedules.Any()) context.AddFailure(nameof(model.Schedules), "Schedules are required for Receiving subscriptions"); @@ -176,13 +185,11 @@ public Validate(IServiceProvider serviceProvider) // Check if it's a native adapter if (model.ReceiverId.StartsWith("native.", StringComparison.OrdinalIgnoreCase)) { - var nativeAdapterDiscovery = serviceProvider.GetService(); var properties = nativeAdapterDiscovery.GetNativeAdapterProperties(model.ReceiverId); mustProps = properties.Where(p => p.Value.EndsWith(" *")).Select(p => p.Key); } else { - var serverless = serviceProvider.GetService(); await serverless.StartAsync(model.ReceiverId, null); mustProps = (await serverless.GetExpectedStartupValues()) .Where(p => p.Value.Optional == false).Select(p => p.Key); @@ -198,14 +205,13 @@ public Validate(IServiceProvider serviceProvider) RuleFor(i => i).CustomAsync(async (model, context, ct) => { - var dbContext = serviceProvider.GetService(); - var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); - + var subscription = await GetSub(dbContext, httpContextAccessor); + if (subscription?.Type == SubscriptionType.Aggregation) { if (model.Schedules == null || !model.Schedules.Any()) context.AddFailure(nameof(model.Schedules), "Schedules are required for Aggregation subscriptions"); - + if (!model.AggregationForId.HasValue) context.AddFailure(nameof(model.AggregationForId), "AggregationForId is required for Aggregation subscriptions"); } @@ -213,16 +219,16 @@ public Validate(IServiceProvider serviceProvider) RuleFor(i => i).CustomAsync(async (model, context, ct) => { - var dbContext = serviceProvider.GetService(); - var subscription = await dbContext.FindAsync(new object[] { context.RootContextData["Key"] }, ct); - + + var subscription = await GetSub(dbContext, httpContextAccessor); + if (subscription?.Type == SubscriptionType.GatewayApiCall) { if (model.PartnerId.HasValue) context.AddFailure(nameof(model.PartnerId), "PartnerId must be null for GatewayApiCall subscriptions"); } }); - + } } } diff --git a/SW.Bitween.Api/Services/XchangeService.cs b/SW.Bitween.Api/Services/XchangeService.cs index c27ccab2..2035002d 100644 --- a/SW.Bitween.Api/Services/XchangeService.cs +++ b/SW.Bitween.Api/Services/XchangeService.cs @@ -53,11 +53,13 @@ public XchangeService(BitweenOptions BitweenSettings, BitweenDbContext dbContext } public async Task SubmitSubscriptionXchange(int subscriptionId, XchangeFile file, - string[] references = null, Partner gatewayPartner = null) + string[] references = null, Partner gatewayPartner = null, + GlobalAdapterValuesSet[] globalAdapterValuesSets = null) { var subscription = await _BitweenCache.SubscriptionByIdAsync(subscriptionId); - var xchange = await CreateXchange(subscription, file, references, Guid.NewGuid().ToString("N"), gatewayPartner); + var xchange = await CreateXchange(subscription, file, references, Guid.NewGuid().ToString("N"), gatewayPartner, + globalAdapterValuesSets); await _dbContext.SaveChangesAsync(); return xchange.Id; } @@ -108,9 +110,11 @@ public async Task CreateXchange(Document document, WorkGroup workGroup, } public async Task CreateXchange(Subscription subscription, XchangeFile file, - string[] references = null, string correlationId = null, Partner gatewayPartner = null,GlobalAdapterValuesSet[] globalAdapterValuesSets = null) + string[] references = null, string correlationId = null, Partner gatewayPartner = null, + GlobalAdapterValuesSet[] globalAdapterValuesSets = null) { - var xchange = new Xchange(subscription, file, references, correlationId, gatewayPartner,globalAdapterValuesSets); + var xchange = new Xchange(subscription, file, references, correlationId, gatewayPartner, + globalAdapterValuesSets); await AddFile(xchange.Id, XchangeFileType.Input, file); _dbContext.Add(xchange); return xchange; @@ -171,7 +175,8 @@ public async Task RunValidator(string validatorId, IDictionary p // Use serverless for external adapters var serverless = _serviceProvider.GetRequiredService(); await serverless.StartAsync(validatorId, null, properties); - result = await serverless.InvokeAsync(nameof(IInfolinkValidator.Validate), xchangeFile); + result = await serverless.InvokeAsync(nameof(IInfolinkValidator.Validate), + xchangeFile); } if (!result.Success) @@ -215,7 +220,8 @@ private T InstantiateNativeAdapter(string adapterId, IDictionary c.GetParameters().Length > 0); if (constructor == null) - throw new BitweenException($"Native adapter {adapterId} must have a constructor that accepts an input model"); + throw new BitweenException( + $"Native adapter {adapterId} must have a constructor that accepts an input model"); // Get the input parameter type var inputParameter = constructor.GetParameters().First(); @@ -228,15 +234,15 @@ private T InstantiateNativeAdapter(string adapterId, IDictionary + var propEntry = properties.FirstOrDefault(p => string.Equals(p.Key, prop.Name, StringComparison.OrdinalIgnoreCase)); - + if (!string.IsNullOrEmpty(propEntry.Key)) { var value = propEntry.Value; try { - var convertedValue = Convert.ChangeType(value, + var convertedValue = Convert.ChangeType(value, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); prop.SetValue(inputInstance, convertedValue); } @@ -251,7 +257,7 @@ private T InstantiateNativeAdapter(string adapterId, IDictionary(); await serverless.StartAsync(notifier.HandlerId, correlationId, handlerProperties); @@ -508,7 +512,8 @@ public Task Process(string messageTypeName, string message) public async Task> GetMessageTypeNamesWithOptions() { - var workgroups = (await _BitweenCache.ListWorkGroupsAsync()).ToList(); + // var workgroups = (await _BitweenCache.ListWorkGroupsAsync()).ToList(); + var workgroups = await _dbContext.Set().ToListAsync(); workgroups.Add(WorkGroup.None); var messageTypeNamesWithOptions = new Dictionary(); foreach (var workGroup in workgroups) diff --git a/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs index 8f6ae161..4f66dd04 100644 --- a/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs +++ b/SW.Bitween.NativeAdapters/HttpHandler/HttpHandler.cs @@ -120,11 +120,12 @@ public async Task Handle(XchangeFile xchangeFile) else uri = new Uri(_options.Url); + var httpMethod = HttpMethodFromString(_options.Verb); HttpRequestMessage request = new HttpRequestMessage() { RequestUri = uri, - Method = HttpMethodFromString(_options.Verb), - Content = content + Method = httpMethod, + Content = httpMethod == HttpMethod.Get ? null : content }; string? headers1 = _options.Headers; IEnumerable>? headers = headers1 != null diff --git a/SW.Bitween.Web/Startup.cs b/SW.Bitween.Web/Startup.cs index 8944175a..946195b2 100644 --- a/SW.Bitween.Web/Startup.cs +++ b/SW.Bitween.Web/Startup.cs @@ -59,6 +59,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddScoped(); + services.AddHttpContextAccessor(); services.AddHostedService(); services.AddHostedService(); From 399cede720aba9c996ab4267bb7a12c5177fdeca Mon Sep 17 00:00:00 2001 From: AhmadAbuhussein Date: Wed, 25 Feb 2026 13:25:27 +0300 Subject: [PATCH 09/10] Remove sensitive connection strings and configuration settings from appsettings.json --- SW.Bitween.Web/appsettings.json | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/SW.Bitween.Web/appsettings.json b/SW.Bitween.Web/appsettings.json index 83aaa620..a2822b84 100644 --- a/SW.Bitween.Web/appsettings.json +++ b/SW.Bitween.Web/appsettings.json @@ -1,12 +1,6 @@ { "ASPNETCORE_ENVIRONMENT":"Development", "AllowedHosts": "*", - "ConnectionStrings": { - "BitweenDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", - "BitweenDb_Postgresql": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", - "InfolinkDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", - "RabbitMQ": "amqps://ivtycyae:jRsc0VsYbm1nH37DlO7YjOHg5FKH2UbQ@eager-ivory-wasp.rmq.cloudamqp.com/ivtycyae" - }, "Theme": { "LoginLogo": "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/Graphics/s9.png", "BitweenLogo": "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/Graphics/BitweenFull.svg", @@ -24,23 +18,5 @@ }, "SWLogger": { "LoggingLevel": 2 - }, - "CloudFiles": { - "AccessKeyId": "XAZGFKBXRE6C3GL723JA", - "SecretAccessKey": "TVSfjjr7jBLB4SvT/6+9/q1CPv4EQJKOGo1hHF4u+zI", - "BucketName": "traxis", - "ServiceUrl": "https://nyc3.digitaloceanspaces.com" - }, - "Bitween": { - "DatabaseType": "PgSql", - "AdminCredentials": "1:1" - }, - "InfolinkClient": { - "BaseUrl": "http://localhost:5000/api/" - }, - "Token": { - "Key": "6547647654764764767657658658758765876532542", - "Issuer": "local", - "Audience": "local" } -} +} \ No newline at end of file From 2eaaf204d78de1aed2269daaf00977e5a5bb5aa0 Mon Sep 17 00:00:00 2001 From: AhmadAbuhussein Date: Wed, 25 Feb 2026 13:30:13 +0300 Subject: [PATCH 10/10] Remove sensitive configuration settings from launchSettings.json --- SW.Bitween.Web/Properties/launchSettings.json | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/SW.Bitween.Web/Properties/launchSettings.json b/SW.Bitween.Web/Properties/launchSettings.json index 61599420..68c5a1f0 100644 --- a/SW.Bitween.Web/Properties/launchSettings.json +++ b/SW.Bitween.Web/Properties/launchSettings.json @@ -17,59 +17,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "TraxisDev": { - "commandName": "Project", - "launchBrowser": false, - "launchUrl": "https://localhost:5003", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "SwLogger__LoggingLevel": "2", - "ConnectionStrings__InfolinkDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", - "ConnectionStrings__BitweenDb": "Server=pgsql-traxis-do-user-7890710-0.b.db.ondigitalocean.com;Port=25060;SSL Mode=Require;Database=traxis_dev;User Id=doadmin;Password=qi5fh7vuashytm03;Trust Server Certificate=true;Max Auto Prepare=5000;Auto Prepare Min Usages=1", - "CloudFiles__AccessKeyId": "XAZGFKBXRE6C3GL723JA", - "CloudFiles__SecretAccessKey": "TVSfjjr7jBLB4SvT/6+9/q1CPv4EQJKOGo1hHF4u+zI", - "CloudFiles__BucketName": "traxis", - "CloudFiles__ServiceUrl": "https://nyc3.digitaloceanspaces.com", - "ConnectionStrings__RabbitMQ" : "amqps://ivtycyae:jRsc0VsYbm1nH37DlO7YjOHg5FKH2UbQ@eager-ivory-wasp.rmq.cloudamqp.com/ivtycyae", - "Infolink__DatabaseType": "PgSql", - "Infolink__AdminCredentials": "1:1", - "InfolinkClient__BaseUrl": "https://localhost:5003/api/", - "Bitween__DatabaseType": "PgSql", - "Bitween__AdminCredentials": "1:1", - "Bitween__BaseUrl": "https://localhost:5003/api/", - "Token__Key": "6547647654764764767657658658758765876532542", - "Token__Issuer": "local", - "Token__Audience": "local" - }, - "applicationUrl": "https://localhost:5002;http://localhost:5003" - }, - "PmmDev": { - "commandName": "Project", - "launchBrowser": false, - "launchUrl": "https://localhost:5003", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "SwLogger__LoggingLevel": "1", - "ConnectionStrings__InfolinkDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", - "ConnectionStrings__BitweenDb": "Server=localhost;Database=pmm2;User Id=postgres;Password=postgres;Trust Server Certificate=true", - "CloudFiles__AccessKeyId": "R3LNFRKWMAC4OCCRICS5", - "CloudFiles__SecretAccessKey": "YPyyTdxs+lZMQEtYIDRK9lkIzjJrCKXinE3OfKEfc7k", - "CloudFiles__BucketName": "sf9", - "CloudFiles__ServiceUrl": "https://fra1.digitaloceanspaces.com", - "ConnectionStrings__RabbitMQ" : "amqps://veebicsq:nw98MHANGkxAMgwOm57ALalyanCAwW2f@cow.rmq2.cloudamqp.com/veebicsq", - "Infolink__DatabaseType": "PgSql", - "Infolink__AdminCredentials": "1:1", - "InfolinkClient__BaseUrl": "https://localhost:5003/api/", - "Bitween__DatabaseType": "PgSql", - "Bitween__AdminCredentials": "1:1", - "Bitween__BaseUrl": "https://localhost:5003/api/", - "Token__Key": "6547647654764764767657658658758765876532542", - "Token__Issuer": "local", - "Token__Audience": "local" - }, - "applicationUrl": "https://localhost:5002;http://localhost:5003" } - } } \ No newline at end of file