Skip to content

Muhannad/xchange created on multi queue - #116

Merged
mmalkhatib merged 8 commits into
releases/r8.0from
muhannad/xchange-created-on-multi-queue
Jan 26, 2026
Merged

Muhannad/xchange created on multi queue#116
mmalkhatib merged 8 commits into
releases/r8.0from
muhannad/xchange-created-on-multi-queue

Conversation

@mmalkhatib

@mmalkhatib mmalkhatib commented Jan 25, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Added WorkGroup management system with create, update, delete, and search capabilities
    • Subscriptions now support WorkGroup association for improved organization
    • Enhanced caching layer to support WorkGroup data retrieval
  • Infrastructure & Maintenance

    • Consolidated database schema for consistency across providers
    • Updated dependency packages for improved performance and compatibility

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 25, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A comprehensive WorkGroup feature is introduced, allowing subscriptions and xchanges to associate with work groups for organizational scoping. The system adds a WorkGroup domain entity, updates domain events to implement IHasWorkGroup, propagates WorkGroup context through Xchange creation, establishes CRUD handlers for WorkGroup management, and synchronizes schema changes across three database providers.

Changes

Cohort / File(s) Summary
Domain Entity: WorkGroup
SW.Bitween.Api/Domain/WorkGroup/WorkGroup.cs, SW.Bitween.Api/Interfaces/IHasWorkGroup.cs
New IWorkGroup interface and WorkGroup entity with BusMessageName, Options, and GetBusMessageName() method; static None property for ungrouped context.
Domain Entity: Subscription
SW.Bitween.Api/Domain/Subscription/Subscription.cs
Added WorkGroupId and WorkGroup navigation property; constructors updated to include WorkGroup parameter (defaults to WorkGroup.None).
Domain Entity: Xchange
SW.Bitween.Api/Domain/Xchange/Xchange.cs
Constructors updated to accept and propagate IWorkGroup workGroup parameter through delegation chain; assignments ensure WorkGroup context flows through creation paths.
Domain Events
SW.Bitween.Api/Domain/Xchange/XchangeCreatedEvent.cs, SW.Bitween.Api/Domain/XchangeResult/XchangeResultCreatedEvent.cs
XchangeCreatedEvent and XchangeResultCreatedEvent implement IHasWorkGroup interface; added WorkGroup property and GetBusMessageName() method; new internal XchangeMessage class with Id property.
Database Context
SW.Bitween.Api/Data/BitweenDbContext.cs
Added WorkGroup entity mapping with BusMessageName (non-Unicode, max 100) and Options (JSON); modified SaveChangesAsync to publish domain events inline with per-event logic checking IHasWorkGroup.
Database Migrations: SQL Server
SW.Bitween.MsSql/Migrations/20260121164302_SubscriptionWorkGroup.*, SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs, SW.Bitween.MsSql/SW.Bitween.MsSql.csproj
New migration adds WorkGroupId column to Subscriptions, creates WorkGroup table with identity and constraints; snapshot updated with WorkGroup entity and Subscription relationship; EntityFrameworkCore.SqlServer bumped 8.0.12 → 8.0.23.
Database Migrations: MySQL
SW.Bitween.MySql/Migrations/20260121165119_SubscriptionWorkGroup.*, SW.Bitween.MySql/Migrations/BitweenDbContextModelSnapshot.cs, SW.Bitween.MySql/SW.Bitween.MySql.csproj
Parallel MySQL migration and snapshot updates; Pomelo.EntityFrameworkCore.MySql bumped 8.0.2 → 8.0.3.
Database Migrations: PostgreSQL
SW.Bitween.PgSql/BitweenDbContext.cs, SW.Bitween.PgSql/Migrations/20260123062659_SubscriptionWorkGroup.*, SW.Bitween.PgSql/Migrations/BitweenDbContextModelSnapshot.cs
Schema changed from "Bitween" to "infolink" across all existing migrations and new SubscriptionWorkGroup migration; WorkGroup table uses jsonb for Options; extensive migration designer updates.
API Handlers: WorkGroups CRUD
SW.Bitween.Api/Resources/WorkGroups/Create.cs, SW.Bitween.Api/Resources/WorkGroups/Delete.cs, SW.Bitween.Api/Resources/WorkGroups/Search.cs, SW.Bitween.Api/Resources/WorkGroups/Update.cs
New CRUD handlers for WorkGroup management with validation, cache invalidation, and consumer refresh.
API Handlers: Xchanges
SW.Bitween.Api/Resources/Xchanges/Create.cs, SW.Bitween.Api/Resources/Xchanges/BulkRetry.cs, SW.Bitween.Api/Resources/Xchanges/Retry.cs, SW.Bitween.Api/Resources/Xchanges/Update.cs
Updated Xchange creation and retry paths to retrieve Subscription and pass WorkGroup; minor formatting adjustments.
API Resources: Subscriptions
SW.Bitween.Api/Resources/Subscriptions/Get.cs, SW.Bitween.Api/Resources/Subscriptions/Search.cs
Added WorkGroupId to SubscriptionUpdate and search projection responses.
Caching & Services
SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs, SW.Bitween.Api/Interfaces/IInfolinkCache.cs, SW.Bitween.Api/Services/BitweenOptions.cs
IInfolinkCache signature updated: BroadcastRevoke() → Task; added ListWorkGroupsAsync(), WorkGroupByIdAsync(), WorkGroupBySubscriptionIdAsync(); InMemoryInfolinkCache implemented with nameof(Entity) cache keys and WorkGroup retrieval; BitweenOptions added ConsumeLegacyEventMessages and QueuePrefix properties.
Database Extensions
SW.Bitween.Api/Extensions/InfolinkDbContextExtensions.cs
New Subscriptions() extension method with eager Include for WorkGroup navigation.
XchangeService Rewrite
SW.Bitween.Api/Services/XchangeService.cs
Major service rewrite: dependency injection constructor, multiple public methods for creation/submission/processing, per-event publishing integration, helper methods for mapping/validation/handling, consumer options retrieval, and result notification orchestration.
SDK Models
SW.Bitween.Sdk/Model/Subscription.cs, SW.Bitween.Sdk/Model/Workgroups.cs
Added WorkGroupId to SubscriptionUpdate; new WorkGroup-related DTOs (ConsumerSettings, WorkGroupOptions, WorkGroupModel, CreateWorkGroupModel, SearchWorkGroupModel, UpdateWorkGroupModel, DeleteWorkGroupModel).
Project Dependencies
SW.Bitween.Api/SW.Bitween.Api.csproj, SW.Bitween.SampleHandler/SW.Bitween.SampleHandler.csproj, SW.Bitween.SampleMapper/SW.Bitween.SampleMapper.csproj, SW.Bitween.SampleValidator/SW.Bitween.SampleValidator.csproj, SW.Bitween.Sdk/SW.Bitween.Sdk.csproj, SW.Bitween.UnitTests/SW.Bitween.UnitTests.csproj, SW.Bitween.Web/SW.Bitween.Web.csproj
Package upgrades across dependencies (EntityFrameworkCore, Bus, ServerlessSdk, Logger, CqApi); removed NewtonSoft.Json from SampleValidator; added SimplyWorks.Logger.ElasticSearch.
Web Infrastructure
SW.Bitween.Web/Program.cs, SW.Bitween.Web/Startup.cs
Logger switched from UseSwLogger() to UseSwElasticSearchLogger(); Bus ApplicationName made configurable via BitweenOptions.QueuePrefix.

Sequence Diagram(s)

sequenceDiagram
    participant API as API Handler
    participant XchangeService as XchangeService
    participant DB as BitweenDbContext
    participant EventBus as Event Bus
    participant Cache as InfolinkCache
    
    API->>XchangeService: SubmitSubscriptionXchange(subscriptionId, file)
    XchangeService->>DB: Fetch Subscription with WorkGroup
    DB-->>XchangeService: Subscription + WorkGroup
    XchangeService->>XchangeService: CreateXchange(subscription, file, workGroup)
    XchangeService->>DB: SaveChangesAsync()
    Note over DB: Capture IGeneratesDomainEvents<br/>Clear events, publish each
    DB->>EventBus: Publish XchangeCreatedEvent<br/>with WorkGroup context
    alt Event implements IHasWorkGroup
        EventBus->>EventBus: Use GetBusMessageName() + WorkGroup
    else Event does not implement IHasWorkGroup
        EventBus->>EventBus: Use event type name
    end
    DB-->>XchangeService: Success
    XchangeService->>Cache: BroadcastRevoke()
    Cache-->>XchangeService: Task Complete
    XchangeService-->>API: Xchange Created
Loading
sequenceDiagram
    participant Admin as Admin
    participant WorkGroupHandler as Create Handler
    participant DB as BitweenDbContext
    participant Cache as InfolinkCache
    participant Broadcast as IBroadcast
    
    Admin->>WorkGroupHandler: POST /workgroups<br/>(CreateWorkGroupModel)
    WorkGroupHandler->>WorkGroupHandler: Build WorkGroup entity<br/>with Name, BusMessageName, Options
    WorkGroupHandler->>DB: dbContext.Set<WorkGroup>().Add(workgroup)
    WorkGroupHandler->>DB: SaveChangesAsync()
    DB-->>WorkGroupHandler: Success
    WorkGroupHandler->>Cache: BroadcastRevoke()
    Cache-->>WorkGroupHandler: Task Complete
    WorkGroupHandler->>Broadcast: RefreshConsumers()
    Broadcast-->>WorkGroupHandler: Success
    WorkGroupHandler-->>Admin: { workgroup.Id }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 A warren of work groups now thrives with care,
Organizing xchanges through layers fair,
Domain events hop with WorkGroup in tow,
Migrations smooth as databases grow,
Subscriptions unite with their bunny new friends,
Where scoping and context now beautifully blend! 🐇

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Muhannad/xchange created on multi queue' is vague and does not clearly convey the primary changes in the changeset, which involve implementing a WorkGroup feature with multi-queue support across domain entities, database migrations, and API handlers. Revise the title to be more descriptive and specific about the main feature, e.g., 'Add WorkGroup entity for multi-queue xchange routing' or 'Implement WorkGroup feature for managing message queues'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
SW.Bitween.PgSql/Migrations/20210701125443_update4.cs (1)

17-36: Missing foreign key constraint on subscription_id.

The on_hold_xchange table references subscription_id but lacks a foreign key constraint to the subscription table. This is inconsistent with other tables in the schema (e.g., subscription_schedule, subscription_trail, xchange) which enforce referential integrity. Since subscription_id is non-nullable, orphaned records could occur if subscriptions are deleted without cascade delete behavior.

Proposed fix to add foreign key constraint
             migrationBuilder.CreateTable(
                 name: "on_hold_xchange",
                 schema: "infolink",
                 columns: table => new
                 {
                     id = table.Column<int>(nullable: false)
                         .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                     subscription_id = table.Column<int>(nullable: false),
                     references = table.Column<string[]>(nullable: true)
                 },
                 constraints: table =>
                 {
                     table.PrimaryKey("pk_on_hold_xchange", x => x.id);
+                    table.ForeignKey(
+                        name: "fk_on_hold_xchange_subscription_subscription_id",
+                        column: x => x.subscription_id,
+                        principalSchema: "infolink",
+                        principalTable: "subscription",
+                        principalColumn: "id",
+                        onDelete: ReferentialAction.Cascade);
                 });
SW.Bitween.Web/Program.cs (1)

9-20: Verify namespace spelling for ElasticSearch logger—"ElasticSerach" may be a typo.

The SimplyWorks.Logger.ElasticSearch package (v8.1.1) is installed and UseSwElasticSearchLogger() is properly wired in Program.cs. However, the namespace is imported as using SW.Logger.ElasticSerach; (note: "SerAch" not "Search"), which is consistently used in both Program.cs and Startup.cs. Confirm this spelling matches the actual package namespace. Additionally, the SWLogger configuration in appsettings.json contains only LoggingLevel: 2—verify that ElasticSearch connection details (cluster, credentials, etc.) are configured in the appropriate environment-specific configuration or at runtime to avoid startup failures.

SW.Bitween.PgSql/Migrations/20220414101356_update9.cs (1)

33-107: Add explicit timezone conversion or document the data semantics for timestamp columns.

The migration converts multiple timestamp columns from timestamp without time zone to timestamp with time zone without explicit USING clauses. PostgreSQL will implicitly cast these values in the server's timezone context, potentially reinterpreting UTC-stored data if the server timezone differs from UTC. Either:

  • Use raw SQL with explicit AT TIME ZONE 'UTC' conversion if timestamps are stored in UTC, or
  • Confirm all existing timestamp values are already in the server's local time and document this assumption, or
  • Consider migrating to UTC-aware application logic before changing column types.
SW.Bitween.Api/Domain/Xchange/Xchange.cs (1)

41-52: Use the Subscriptions() extension method in Create.cs to ensure WorkGroup is eagerly loaded.

In SW.Bitween.Api/Resources/Xchanges/Create.cs at line 34, the subscription is fetched using _dbc.Set<Subscription>().FirstOrDefaultAsync(...) instead of the Subscriptions() extension method. This bypasses the automatic Include(s => s.WorkGroup) that exists in InfolinkDbContextExtensions.cs. The Xchange constructor immediately accesses subscription.WorkGroup (line 42), which will be null if not eager-loaded, causing the primary constructor to fall back to WorkGroup.None.

Change:

var subscription = await _dbc.Set<Subscription>().FirstOrDefaultAsync(d => d.Id == request.SubscriberId);

to:

var subscription = await _dbc.Subscriptions().FirstOrDefaultAsync(d => d.Id == request.SubscriberId);
🤖 Fix all issues with AI agents
In `@SW.Bitween.Api/Data/BitweenDbContext.cs`:
- Around line 314-331: The code commits DB changes in SaveChangesAsync then
publishes domain events via ChangeTracker entries (IGeneratesDomainEvents) and
publish.Publish, which can lose events if publishing fails; remove the
commented-out ChangeTracker.PublishDomainEvents call, implement the outbox
pattern (persist events to an Outbox table/entity within the same transaction
inside SaveChangesAsync or the method that calls base.SaveChangesAsync), and
change the loop that currently uses publish.Publish (and XchangeMessage
creation) to enqueue events into the outbox instead of directly publishing; if
you need a quicker mitigation, wrap the publish.Publish calls in a try-catch
with retry/backoff and log failures (using your logger) and do not clear
entity.Events until publish succeeds or the event is moved to the outbox so
IGeneratesDomainEvents entities (and methods like GetBusMessageName/GetType) are
updated accordingly.

In `@SW.Bitween.Api/Domain/Xchange/XchangeCreatedEvent.cs`:
- Around line 10-15: Update XchangeCreatedEvent.GetBusMessageName to guard
against a null WorkGroup: check WorkGroup before calling
WorkGroup.GetBusMessageName() and return a safe fallback when WorkGroup is null
(e.g., a default message name or empty string) so publishing won’t throw; modify
the method on the XchangeCreatedEvent class (and keep the IWorkGroup usage) to
use a null check (WorkGroup) and return WorkGroup.GetBusMessageName() only when
non-null, otherwise return the chosen fallback value.

In `@SW.Bitween.Api/Interfaces/IInfolinkCache.cs`:
- Around line 16-21: Several call sites invoke the newly async BroadcastRevoke()
without awaiting it, causing fire-and-forget behavior; update each call site to
await cache.BroadcastRevoke() instead of calling it without await, and if the
containing method (the WorkGroups Create handler, WorkGroups Delete handler,
Documents Update handler, and Subscriptions Update handler) is not already
async/returning Task, change its signature to async Task and propagate awaits
accordingly so exceptions and ordering are preserved. Ensure you reference the
IInfolinkCache.BroadcastRevoke() call in the methods named Create (WorkGroups),
Delete (WorkGroups), Update (Documents), and Update (Subscriptions) and replace
the bare call with await cache.BroadcastRevoke().

In `@SW.Bitween.Api/Resources/WorkGroups/Delete.cs`:
- Around line 24-28: The call to _infolinkCache.BroadcastRevoke() is currently
invoked fire-and-forget which can cause unobserved exceptions and race
conditions with _broadcast.RefreshConsumers(); change the code to await
_infolinkCache.BroadcastRevoke() so the revocation completes (and any exceptions
propagate) before calling await _broadcast.RefreshConsumers(); ensure you keep
the surrounding async method signature that contains
dbContext.SaveChangesAsync(), BroadcastRevoke(), and RefreshConsumers()
(reference: BroadcastRevoke(), RefreshConsumers(), _infolinkCache, _broadcast).

In `@SW.Bitween.Api/Resources/WorkGroups/Update.cs`:
- Around line 15-23: The Update handler in Update.cs is not applying the
request's BusMessageName to the entity; update the code to set
workGroup.BusMessageName = request.BusMessageName (or remove BusMessageName from
CreateWorkGroupModel/UpdateWorkGroupModel if it must be immutable). Locate the
update logic that assigns workGroup.Name and workGroup.Options (in the Update
handler for WorkGroup) and add the BusMessageName assignment there so the
WorkGroup entity reflects the request's BusMessageName.

In `@SW.Bitween.Api/Resources/Xchanges/BulkRetry.cs`:
- Around line 33-46: The non-reset branch assumes subscription is non-null and
uses subscription.WorkGroup which throws if the subscription was deleted; update
the branch that calls _xchangeService.CreateXchange(xchange, xchangeFile,
subscription.WorkGroup) so it uses a null-safe WorkGroup (e.g.
subscription?.WorkGroup or a fallback/default workgroup) or otherwise
resolve/fetch the WorkGroup before calling CreateXchange; ensure the same
null-check style as the reset path and reference the variables subscription,
xchange, xchangeFile and the method CreateXchange when making the change.

In `@SW.Bitween.Api/Services/Caching/InMemoryInfolinkCache.cs`:
- Around line 108-113: BroadcastRevoke disposes the DI scope immediately because
it returns the broadcast Task without waiting, so scoped services can be
disposed too early; change BroadcastRevoke to be async (e.g., public async Task
BroadcastRevoke()) and await the call to broadcast.Broadcast(new
RevokeCacheMessage()) inside the using var scope = _ssf.CreateScope() block so
the scope (and IBroadcast instance) is kept alive until the broadcast completes.

In `@SW.Bitween.Api/Services/XchangeService.cs`:
- Around line 52-60: In SubmitSubscriptionXchange, guard against a missing
subscription by checking the result of
_BitweenCache.SubscriptionByIdAsync(subscriptionId) for null before calling
CreateXchange; if null, throw a clear exception (e.g., ArgumentException or
custom NotFoundException) identifying the subscriptionId and return early so you
don't call CreateXchange or _dbContext.SaveChangesAsync on a null subscription;
update the method to validate subscription and include the subscriptionId in the
error message.
- Around line 62-80: In SubmitFilterXchange, add an explicit null check after
awaiting _BitweenCache.DocumentByIdAsync(documentId) to handle unknown documents
(document == null) before any dereference; either throw a descriptive exception
(e.g., ArgumentException/NotFound) or return gracefully, and ensure downstream
calls like CreateXchange(document, ...) and the logic that reads
document.DisregardsUnfilteredMessages are only executed when document is
non-null so you avoid the null dereference.
- Around line 214-263: In Process, workGroup stays null when
xchange.SubscriptionId is null causing routing context loss; set workGroup to
the xchange's workgroup (or WorkGroup.None) before branching so it is propagated
into the XchangeResult; update the Process method to assign workGroup =
xchange.WorkGroup ?? WorkGroup.None (or set it inside the else branch before
calling CreateXchangesForHits and before adding new XchangeResult) so the
XchangeResult(xchange.Id, workGroup, outputFile, responseFile,
responseXchange?.Id) always receives a non-null WorkGroup.

In
`@SW.Bitween.MsSql/Migrations/20260121164302_SubscriptionWorkGroup.Designer.cs`:
- Around line 840-847: The migration seed contains a hard-coded API credential
in the anonymous object passed to b1.HasData (fields PartnerId/Id/Key/Name) —
remove the real-looking Key value from the migration snapshot (either omit the
Key property or replace it with a non-secret placeholder), and change seeding so
the real API key is provisioned at runtime via a secure provisioning step (e.g.,
environment secret, configuration store, or a post-deploy script) rather than
baked into SubscriptionWorkGroup seed data; also ensure any deployed key is
rotated if it was previously used.

In `@SW.Bitween.MsSql/SW.Bitween.MsSql.csproj`:
- Line 9: Update all EF Core package references to the same patch version (use
8.0.23) to avoid runtime/binding mismatches: change
Microsoft.EntityFrameworkCore.Design in SW.Bitween.Web,
Microsoft.EntityFrameworkCore.Sqlite in SW.Bitween.UnitTests, and any
Microsoft.EntityFrameworkCore.Relational references to 8.0.23 so they match
Microsoft.EntityFrameworkCore.SqlServer (8.0.23); ensure each project's
PackageReference for these packages (package IDs:
Microsoft.EntityFrameworkCore.Design, Microsoft.EntityFrameworkCore.Sqlite,
Microsoft.EntityFrameworkCore.Relational,
Microsoft.EntityFrameworkCore.SqlServer) uses the identical Version attribute.

In
`@SW.Bitween.MySql/Migrations/20260121165119_SubscriptionWorkGroup.Designer.cs`:
- Around line 838-843: The seed data in the migration (the anonymous object
containing PartnerId, Id, Key, Name in SubscriptionWorkGroup.Designer.cs)
includes a hard‑coded API credential in the Key field; remove that secret from
the migration and replace it with a non‑secret placeholder (null, empty string,
or a clearly marked placeholder value) or wire it to load from
configuration/secrets at runtime instead of seeding; also ensure the real
credential is rotated and not reintroduced into VCS, and update any
documentation/tests that relied on the seeded Key.

In `@SW.Bitween.PgSql/Migrations/20210612111308_update2.Designer.cs`:
- Around line 20-24: This historical migration designer was changed to set
modelBuilder.HasDefaultSchema("infolink") (and related annotations) which alters
recorded past state; revert the modifications in the
20210612111308_update2.Designer.cs (remove the HasDefaultSchema("infolink")
addition and any annotation edits) so the migration file reflects the original
state, and instead create a new migration that performs the schema
rename/migration (or document that deployments are always fresh) if you need the
DB moved to "infolink"; ensure references to modelBuilder.HasDefaultSchema,
Npgsql:ValueGenerationStrategy, and Relational:MaxIdentifierLength are only
updated in the new migration or documented deployment notes.

In
`@SW.Bitween.PgSql/Migrations/20260123062659_SubscriptionWorkGroup.Designer.cs`:
- Around line 1007-1013: The migration seed data contains a hard-coded API
credential ("Key" = "7facc758283844b49cc4ffd26a75b1de") which must be removed;
update the seed inserted in the SubscriptionWorkGroup migration to stop
embedding a static secret by either omitting the Key column in the seed, setting
it to NULL/empty, or using a placeholder that signals runtime injection, and
ensure the application/service populates or generates the API key at runtime (or
reads it from a secure config/secret store) in the code that creates
SubscriptionWorkGroup records; also rotate any leaked credential referenced by
this seed.

In `@SW.Bitween.Web/Startup.cs`:
- Line 30: Update the incorrect using directive "SW.Logger.ElasticSerach" to the
correct namespace "SimplyWorks.Logger.ElasticSearch" so the import compiles;
locate the using statement at the top of Startup.cs (the line showing
SW.Logger.ElasticSerach) and replace it with the proper namespace name to match
the referenced package.

In `@SW.Bitween.Web/SW.Bitween.Web.csproj`:
- Around line 18-25: SimplyWorks.Bus 8.1.7 introduces breaking changes: the
RabbitMQ ManagementUrl default and ManagementClient construction. Update any
RabbitMQ configuration to explicitly set ManagementUrl to the full URL including
:15672 if you rely on that port (set the ManagementUrl property where you
configure the Bus), and locate usages that create or resolve ManagementClient
(or any code expecting an injected HttpClient for rabbit management) and change
them to construct or accept a Uri-based ManagementClient or rework your DI so a
wrapper provides the needed HttpClient behavior; search for ManagementUrl,
ManagementClient and registrations of SimplyWorks.Bus to apply these fixes and
verify any custom HttpClient handlers, proxies or certificates are preserved via
a wrapper or updated client factory.
♻️ Duplicate comments (2)
SW.Bitween.PgSql/Migrations/20220816114929_XMLSupport.cs (1)

9-25: Same schema-existence guard as update11.

Please ensure infolink exists before this migration runs (or add EnsureSchema) for fresh DBs.

SW.Bitween.MsSql/Migrations/BitweenDbContextModelSnapshot.cs (1)

837-845: Duplicate: hard-coded API key in snapshot seed data.

Same credential as noted in the migration designer; remove it here as well.

🟡 Minor comments (4)
SW.Bitween.Api/Resources/WorkGroups/Update.cs-14-14 (1)

14-14: Fix the inconsistent error message.

The error message references "Category" but should say "WorkGroup" to match the entity being updated.

Proposed fix
-            throw new SWValidationException("WORK_GROUP_NOT_FOUND", $"Category with id {key} was not found");
+            throw new SWValidationException("WORK_GROUP_NOT_FOUND", $"WorkGroup with id {key} was not found");
SW.Bitween.Api/Domain/WorkGroup/WorkGroup.cs-20-22 (1)

20-22: WorkGroup.None will produce "0Ungrouped" from GetBusMessageName().

The static None property only sets BusMessageName = "Ungrouped", leaving Id at default value 0. When GetBusMessageName() is called, it returns $"{Id}{BusMessageName}" which evaluates to "0Ungrouped".

If the intent is to have a clean "Ungrouped" message name for the default case, consider handling Id == 0 specially or setting BusMessageName to just "Ungrouped" and adjusting the concatenation logic.

Also, line 21 contains commented-out incomplete code that should be removed.

🛠️ Possible fix
-    public string GetBusMessageName() => $"{Id}{BusMessageName}";
-    //public string 
-    public static WorkGroup None => new() { BusMessageName = "Ungrouped"};
+    public string GetBusMessageName() => Id == 0 ? BusMessageName : $"{Id}{BusMessageName}";
+    public static WorkGroup None => new() { BusMessageName = "Ungrouped" };
SW.Bitween.Api/Services/XchangeService.cs-191-205 (1)

191-205: Avoid logging full storage keys at Info level.
These keys can expose storage structure and identifiers; consider Debug or remove.

🛠️ Proposed fix
- _logger.LogInformation($"the file key is:'{key}'");
+ _logger.LogDebug("Xchange file key: {Key}", key);
SW.Bitween.Api/Services/XchangeService.cs-298-327 (1)

298-327: Clarify empty RunOnSubscriptions array behavior.

RunOnSubscriptions is an int[] that can be null or empty. The code checks for null but doesn't explicitly handle empty arrays. Since All() returns true on empty collections, an empty array causes the notifier to skip. If empty should mean "run for all subscriptions," add a length check:

Proposed fix (if empty = run for all)
- if (notifier.RunOnSubscriptions.All(i => i != xchange!.SubscriptionId))
+ if (notifier.RunOnSubscriptions.Length > 0 && 
+     notifier.RunOnSubscriptions.All(i => i != xchange!.SubscriptionId))
 {
     continue;
 }
🧹 Nitpick comments (10)
SW.Bitween.PgSql/BitweenDbContext.cs (1)

76-83: Missing Name property configuration for WorkGroup entity.

The WorkGroup entity has a Name property (per the domain class), but it's not configured here. Other entities like Document (line 37) and Partner (line 88) configure Name with IsRequired() and HasMaxLength() constraints. Consider adding similar constraints for consistency and data integrity.

♻️ Suggested configuration
 modelBuilder.Entity<WorkGroup>(wg =>
 {
     wg.HasKey(i => i.Id);
     wg.Property(i => i.Id).ValueGeneratedOnAdd();
+    wg.Property(p => p.Name).IsRequired().HasMaxLength(100);
     wg.Property(p => p.BusMessageName).IsRequired().IsUnicode(false).HasMaxLength(100);
     wg.Property(p => p.Options).HasColumnType("jsonb");

 });
SW.Bitween.Api/Resources/Subscriptions/Get.cs (1)

69-177: Remove commented-out dead code.

This large block of commented-out code (109 lines) should be removed. Version control preserves history if needed later. Keeping dead code reduces readability and maintainability.

SW.Bitween.PgSql/Migrations/20221221093002_update11.cs (1)

9-24: Guard against missing schema on fresh databases.

If infolink isn’t guaranteed to exist before this migration, AddColumn will fail. Consider explicitly ensuring the schema first.

🔧 Suggested guard
 protected override void Up(MigrationBuilder migrationBuilder)
 {
+    migrationBuilder.EnsureSchema("infolink");
     migrationBuilder.AddColumn<bool>(
         name: "disregards_unfiltered_messages",
         schema: "infolink",
         table: "document",
         type: "boolean",
         nullable: true);
 }
SW.Bitween.Web/Startup.cs (1)

61-64: Protect Bus ApplicationName from empty/whitespace QueuePrefix.

If QueuePrefix is empty, the bus application name can become invalid. Consider a fallback to the default value.

🔧 Suggested guard
-    config.ApplicationName = bitweenOptions.QueuePrefix;
+    config.ApplicationName = string.IsNullOrWhiteSpace(bitweenOptions.QueuePrefix)
+        ? "bitween"
+        : bitweenOptions.QueuePrefix;
SW.Bitween.Api/Interfaces/IHasWorkGroup.cs (1)

3-6: Clarify Id semantics in IHasWorkGroup.

Id is ambiguous in a “work group” interface. If this is intended to expose the work group identifier, consider renaming to WorkGroupId (or add a WorkGroup/WorkGroupId property) for clarity and to avoid collisions with entity IDs.

SW.Bitween.Api/Resources/WorkGroups/Update.cs (1)

8-8: Consider using UpdateWorkGroupModel instead of CreateWorkGroupModel.

The handler uses CreateWorkGroupModel for an update operation. Per the AI summary, an UpdateWorkGroupModel exists. Using the appropriate model type improves clarity and allows the update model to have different validation/requirements than create.

SW.Bitween.Api/Domain/WorkGroup/WorkGroup.cs (1)

1-3: Remove redundant and unused imports.

Line 1 imports SW.Bitween.Domain which is the same namespace this file declares on line 6. Line 3 imports SW.Bus.RabbitMqExtensions but nothing from that namespace appears to be used in this file.

♻️ Suggested fix
-using SW.Bitween.Domain;
 using SW.Bitween.Model;
-using SW.Bus.RabbitMqExtensions;
 using SW.PrimitiveTypes;
SW.Bitween.Api/Data/BitweenDbContext.cs (1)

86-93: Consider adding constraints for WorkGroup.Name property.

BusMessageName is configured with IsRequired(), IsUnicode(false), and HasMaxLength(100), but the Name property has no configuration. If Name is intended to be required or have length limits, add similar constraints.

♻️ Example configuration if Name should be required
 modelBuilder.Entity<WorkGroup>(wg =>
 {
     wg.HasKey(i => i.Id);
     wg.Property(i => i.Id).ValueGeneratedOnAdd();
+    wg.Property(p => p.Name).IsRequired().HasMaxLength(100);
     wg.Property(p => p.BusMessageName).IsRequired().IsUnicode(false).HasMaxLength(100);
     wg.Property(p => p.Options).StoreAsJson();

 });
SW.Bitween.Api/Services/XchangeService.cs (2)

264-269: Log processing failures before persisting the result.
Right now failures are swallowed silently, which makes production triage harder.

🛠️ Proposed fix
 catch (Exception ex)
 {
+    _logger.LogError(ex, "Failed processing xchange {XchangeId}", xchange.Id);
     _dbContext.Add(new XchangeResult(xchange.Id, workGroup, outputFile, responseFile, responseXchange?.Id,
         ex.ToString()));
     await _dbContext.SaveChangesAsync();
 }

404-409: Validate payload before processing.
If deserialization fails, eventMessage can be null and will throw later. Add a guard with a warning.

🛠️ Proposed fix
 var eventMessage = JsonConvert.DeserializeObject<XchangeMessage>(message);
+if (eventMessage?.Id == null)
+{
+    _logger.LogWarning("Skipping invalid message for type {MessageTypeName}", messageTypeName);
+    return Task.CompletedTask;
+}

 return messageTypeName.EndsWith(ResultQueueSuffix) ? ProcessResult(eventMessage) : Process(eventMessage);

Comment on lines 314 to +331
var affectedRecords = await base.SaveChangesAsync(cancellationToken);
await ChangeTracker.PublishDomainEvents(publish);
//await transaction.CommitAsync();
//await ChangeTracker.PublishDomainEvents(publish);
var entitiesWithEvents = ChangeTracker.Entries<IGeneratesDomainEvents>()
.Select(e => e.Entity)
.Where(e => e.Events.Any())
.ToArray();

foreach (var entity in entitiesWithEvents)
{
var events = entity.Events.ToArray();
entity.Events.Clear();
foreach (var domainEvent in events)
if (domainEvent is IHasWorkGroup hasWorkGroup)
await publish.Publish(hasWorkGroup.GetBusMessageName(),
JsonConvert.SerializeObject(new XchangeMessage { Id = hasWorkGroup.Id }));
else
await publish.Publish(domainEvent.GetType().Name, JsonConvert.SerializeObject(domainEvent));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Domain event publishing after SaveChangesAsync may lose events on failure.

The current flow commits database changes first (line 314), then publishes domain events (lines 321-331). If publishing fails (e.g., message broker unavailable), the database transaction has already committed but the events are lost. This can lead to data/event inconsistency.

Consider:

  1. Using the outbox pattern to persist events in the same transaction
  2. Wrapping both operations in a distributed transaction
  3. At minimum, adding error handling/retry logic for the publish calls

Also, line 315 contains commented-out code that should be removed.

🔒 Minimal improvement: add try-catch with logging
 var affectedRecords = await base.SaveChangesAsync(cancellationToken);
-//await ChangeTracker.PublishDomainEvents(publish);
 var entitiesWithEvents = ChangeTracker.Entries<IGeneratesDomainEvents>()
     .Select(e => e.Entity)
     .Where(e => e.Events.Any())
     .ToArray();

 foreach (var entity in entitiesWithEvents)
 {
     var events = entity.Events.ToArray();
     entity.Events.Clear();
     foreach (var domainEvent in events)
+    {
+        try
+        {
             if (domainEvent is IHasWorkGroup hasWorkGroup)
                 await publish.Publish(hasWorkGroup.GetBusMessageName(),
                     JsonConvert.SerializeObject(new XchangeMessage { Id = hasWorkGroup.Id }));
             else
                 await publish.Publish(domainEvent.GetType().Name, JsonConvert.SerializeObject(domainEvent));
+        }
+        catch (Exception ex)
+        {
+            // Log the failure - consider implementing outbox pattern for reliability
+            // logger.LogError(ex, "Failed to publish domain event {EventType}", domainEvent.GetType().Name);
+            throw; // or handle gracefully based on requirements
+        }
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var affectedRecords = await base.SaveChangesAsync(cancellationToken);
await ChangeTracker.PublishDomainEvents(publish);
//await transaction.CommitAsync();
//await ChangeTracker.PublishDomainEvents(publish);
var entitiesWithEvents = ChangeTracker.Entries<IGeneratesDomainEvents>()
.Select(e => e.Entity)
.Where(e => e.Events.Any())
.ToArray();
foreach (var entity in entitiesWithEvents)
{
var events = entity.Events.ToArray();
entity.Events.Clear();
foreach (var domainEvent in events)
if (domainEvent is IHasWorkGroup hasWorkGroup)
await publish.Publish(hasWorkGroup.GetBusMessageName(),
JsonConvert.SerializeObject(new XchangeMessage { Id = hasWorkGroup.Id }));
else
await publish.Publish(domainEvent.GetType().Name, JsonConvert.SerializeObject(domainEvent));
}
var affectedRecords = await base.SaveChangesAsync(cancellationToken);
var entitiesWithEvents = ChangeTracker.Entries<IGeneratesDomainEvents>()
.Select(e => e.Entity)
.Where(e => e.Events.Any())
.ToArray();
foreach (var entity in entitiesWithEvents)
{
var events = entity.Events.ToArray();
entity.Events.Clear();
foreach (var domainEvent in events)
{
try
{
if (domainEvent is IHasWorkGroup hasWorkGroup)
await publish.Publish(hasWorkGroup.GetBusMessageName(),
JsonConvert.SerializeObject(new XchangeMessage { Id = hasWorkGroup.Id }));
else
await publish.Publish(domainEvent.GetType().Name, JsonConvert.SerializeObject(domainEvent));
}
catch (Exception ex)
{
// Log the failure - consider implementing outbox pattern for reliability
// logger.LogError(ex, "Failed to publish domain event {EventType}", domainEvent.GetType().Name);
throw; // or handle gracefully based on requirements
}
}
}
🤖 Prompt for AI Agents
In `@SW.Bitween.Api/Data/BitweenDbContext.cs` around lines 314 - 331, The code
commits DB changes in SaveChangesAsync then publishes domain events via
ChangeTracker entries (IGeneratesDomainEvents) and publish.Publish, which can
lose events if publishing fails; remove the commented-out
ChangeTracker.PublishDomainEvents call, implement the outbox pattern (persist
events to an Outbox table/entity within the same transaction inside
SaveChangesAsync or the method that calls base.SaveChangesAsync), and change the
loop that currently uses publish.Publish (and XchangeMessage creation) to
enqueue events into the outbox instead of directly publishing; if you need a
quicker mitigation, wrap the publish.Publish calls in a try-catch with
retry/backoff and log failures (using your logger) and do not clear
entity.Events until publish succeeds or the event is moved to the outbox so
IGeneratesDomainEvents entities (and methods like GetBusMessageName/GetType) are
updated accordingly.

Comment on lines +10 to +15
internal abstract class XchangeCreatedEvent : BaseDomainEvent,IHasWorkGroup
{
public string Id { get; set; }
public string GetBusMessageName()=> WorkGroup.GetBusMessageName();

public IWorkGroup WorkGroup { get; set; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent null WorkGroup from breaking message routing.

GetBusMessageName() dereferences WorkGroup directly; if any event is created without a WorkGroup (legacy path or partial construction), this will throw at publish time. Consider a safe fallback.

🐛 Proposed fix
-        public string GetBusMessageName()=> WorkGroup.GetBusMessageName();
+        public string GetBusMessageName() =>
+            (WorkGroup ?? global::SW.Bitween.Domain.WorkGroup.None).GetBusMessageName();
🤖 Prompt for AI Agents
In `@SW.Bitween.Api/Domain/Xchange/XchangeCreatedEvent.cs` around lines 10 - 15,
Update XchangeCreatedEvent.GetBusMessageName to guard against a null WorkGroup:
check WorkGroup before calling WorkGroup.GetBusMessageName() and return a safe
fallback when WorkGroup is null (e.g., a default message name or empty string)
so publishing won’t throw; modify the method on the XchangeCreatedEvent class
(and keep the IWorkGroup usage) to use a null check (WorkGroup) and return
WorkGroup.GetBusMessageName() only when non-null, otherwise return the chosen
fallback value.

Comment on lines 16 to +21
void Revoke();
void BroadcastRevoke();
Task BroadcastRevoke();

Task<WorkGroup[]> ListWorkGroupsAsync();
Task<WorkGroup> WorkGroupByIdAsync(int workGroupId);
Task<WorkGroup> WorkGroupBySubscriptionIdAsync(int subscriptionId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find implementations and call sites for BroadcastRevoke / IInfolinkCache
echo "=== BroadcastRevoke call sites ==="
rg -n --glob '!**/bin/**' --glob '!**/obj/**' 'BroadcastRevoke\s*\('

echo ""
echo "=== IInfolinkCache implementations ==="
rg -n --glob '!**/bin/**' --glob '!**/obj/**' ':\s*IInfolinkCache\b'

Repository: simplify9/Bitween-api

Length of output: 887


Fix unawaited BroadcastRevoke() calls in four locations.

The signature change to async (Task return) is correctly implemented in InMemoryBitweenCache, but four call sites fail to await:

  • SW.Bitween.Api/Resources/WorkGroups/Create.cs:30
  • SW.Bitween.Api/Resources/WorkGroups/Delete.cs:27
  • SW.Bitween.Api/Resources/Documents/Update.cs:54
  • SW.Bitween.Api/Resources/Subscriptions/Update.cs:50

These fire-and-forget calls risk dropping exceptions and race conditions in cache invalidation. Add await to each call site.

🤖 Prompt for AI Agents
In `@SW.Bitween.Api/Interfaces/IInfolinkCache.cs` around lines 16 - 21, Several
call sites invoke the newly async BroadcastRevoke() without awaiting it, causing
fire-and-forget behavior; update each call site to await cache.BroadcastRevoke()
instead of calling it without await, and if the containing method (the
WorkGroups Create handler, WorkGroups Delete handler, Documents Update handler,
and Subscriptions Update handler) is not already async/returning Task, change
its signature to async Task and propagate awaits accordingly so exceptions and
ordering are preserved. Ensure you reference the
IInfolinkCache.BroadcastRevoke() call in the methods named Create (WorkGroups),
Delete (WorkGroups), Update (Documents), and Update (Subscriptions) and replace
the bare call with await cache.BroadcastRevoke().

Comment on lines +24 to +28
//Todo chek rabbitMq
dbContext.Remove(category);
await dbContext.SaveChangesAsync();
_infolinkCache.BroadcastRevoke();
await _broadcast.RefreshConsumers();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Await cache revocation to avoid fire‑and‑forget failures.

BroadcastRevoke() is async; not awaiting risks unobserved exceptions and races with RefreshConsumers().

🐛 Proposed fix
-        _infolinkCache.BroadcastRevoke();
+        await _infolinkCache.BroadcastRevoke();
🤖 Prompt for AI Agents
In `@SW.Bitween.Api/Resources/WorkGroups/Delete.cs` around lines 24 - 28, The call
to _infolinkCache.BroadcastRevoke() is currently invoked fire-and-forget which
can cause unobserved exceptions and race conditions with
_broadcast.RefreshConsumers(); change the code to await
_infolinkCache.BroadcastRevoke() so the revocation completes (and any exceptions
propagate) before calling await _broadcast.RefreshConsumers(); ensure you keep
the surrounding async method signature that contains
dbContext.SaveChangesAsync(), BroadcastRevoke(), and RefreshConsumers()
(reference: BroadcastRevoke(), RefreshConsumers(), _infolinkCache, _broadcast).

Comment on lines +15 to +23
workGroup.Name = request.Name;
workGroup.Options = new WorkGroupOptions
{
RabbitMqOptions = new ConsumerSettings
{
Prefetch = request.Options?.RabbitMqOptions?.Prefetch,
Priority = request.Options?.RabbitMqOptions?.Priority
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the UpdateWorkGroupModel definition
rg -n "class UpdateWorkGroupModel" -A 10

Repository: simplify9/Bitween-api

Length of output: 430


🏁 Script executed:

#!/bin/bash
# Check the CreateWorkGroupModel definition
rg -n "class CreateWorkGroupModel" -A 20

Repository: simplify9/Bitween-api

Length of output: 1209


🏁 Script executed:

#!/bin/bash
# Check the update handler file
cat -n "SW.Bitween.Api/Resources/WorkGroups/Update.cs" | head -40

Repository: simplify9/Bitween-api

Length of output: 1409


🏁 Script executed:

#!/bin/bash
# Check the WorkGroup entity definition
rg -n "class WorkGroup" -A 20 "Domain"

Repository: simplify9/Bitween-api

Length of output: 111


🏁 Script executed:

#!/bin/bash
# Find the WorkGroup entity definition
rg -n "class WorkGroup" -A 15

Repository: simplify9/Bitween-api

Length of output: 2405


BusMessageName is not being updated from the request.

The CreateWorkGroupModel (and by inheritance, UpdateWorkGroupModel) includes a BusMessageName property, but only Name and Options are being updated in the handler. The WorkGroup entity has a settable BusMessageName property, so this appears to be an incomplete update. Either add workGroup.BusMessageName = request.BusMessageName; or remove BusMessageName from the request model if it should be immutable.

🤖 Prompt for AI Agents
In `@SW.Bitween.Api/Resources/WorkGroups/Update.cs` around lines 15 - 23, The
Update handler in Update.cs is not applying the request's BusMessageName to the
entity; update the code to set workGroup.BusMessageName = request.BusMessageName
(or remove BusMessageName from CreateWorkGroupModel/UpdateWorkGroupModel if it
must be immutable). Locate the update logic that assigns workGroup.Name and
workGroup.Options (in the Update handler for WorkGroup) and add the
BusMessageName assignment there so the WorkGroup entity reflects the request's
BusMessageName.

Comment on lines +838 to +843
new
{
PartnerId = 1,
Id = 1,
Key = "7facc758283844b49cc4ffd26a75b1de",
Name = "default"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove the hard‑coded API credential from seed data.

This is a committed secret. Please remove/rotate it and avoid seeding real credentials in migrations.

🧰 Tools
🪛 Gitleaks (8.30.0)

[high] 842-842: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🤖 Prompt for AI Agents
In `@SW.Bitween.MySql/Migrations/20260121165119_SubscriptionWorkGroup.Designer.cs`
around lines 838 - 843, The seed data in the migration (the anonymous object
containing PartnerId, Id, Key, Name in SubscriptionWorkGroup.Designer.cs)
includes a hard‑coded API credential in the Key field; remove that secret from
the migration and replace it with a non‑secret placeholder (null, empty string,
or a clearly marked placeholder value) or wire it to load from
configuration/secrets at runtime instead of seeding; also ensure the real
credential is rotated and not reintroduced into VCS, and update any
documentation/tests that relied on the seeded Key.

Comment on lines 20 to 24
modelBuilder
.HasDefaultSchema("Bitween")
.HasDefaultSchema("infolink")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same concern: historical migration modification.

This is an even older migration (June 2021) with the same schema retrofit. The pattern of modifying multiple historical migration designers suggests an attempt to make the codebase look like it always used "infolink" schema.

For a clean migration path, consider:

  1. Keep historical migrations unchanged (they represent the state at that point in time)
  2. Add a new migration that renames the schema (if the database needs to change)
  3. Or, if this is acceptable for your deployment model (e.g., always fresh deployments), document this assumption
🤖 Prompt for AI Agents
In `@SW.Bitween.PgSql/Migrations/20210612111308_update2.Designer.cs` around lines
20 - 24, This historical migration designer was changed to set
modelBuilder.HasDefaultSchema("infolink") (and related annotations) which alters
recorded past state; revert the modifications in the
20210612111308_update2.Designer.cs (remove the HasDefaultSchema("infolink")
addition and any annotation edits) so the migration file reflects the original
state, and instead create a new migration that performs the schema
rename/migration (or document that deployments are always fresh) if you need the
DB moved to "infolink"; ensure references to modelBuilder.HasDefaultSchema,
Npgsql:ValueGenerationStrategy, and Relational:MaxIdentifierLength are only
updated in the new migration or documented deployment notes.

Comment on lines +1007 to +1013
new
{
PartnerId = 1,
Id = 1,
Key = "7facc758283844b49cc4ffd26a75b1de",
Name = "default"
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove the hard‑coded API credential from seed data.

A static API key in migration seed data is a secret exposure risk. Please remove it from source control and rotate any affected credentials; generate or inject secrets at runtime instead.

🤖 Prompt for AI Agents
In `@SW.Bitween.PgSql/Migrations/20260123062659_SubscriptionWorkGroup.Designer.cs`
around lines 1007 - 1013, The migration seed data contains a hard-coded API
credential ("Key" = "7facc758283844b49cc4ffd26a75b1de") which must be removed;
update the seed inserted in the SubscriptionWorkGroup migration to stop
embedding a static secret by either omitting the Key column in the seed, setting
it to NULL/empty, or using a placeholder that signals runtime injection, and
ensure the application/service populates or generates the API key at runtime (or
reads it from a secure config/secret store) in the code that creates
SubscriptionWorkGroup records; also rotate any leaked credential referenced by
this seed.

Comment thread SW.Bitween.Web/Startup.cs
using SW.Bitween.Resources.Accounts;
using SW.Bitween.Services;
using SW.CqApi.AuthOptions;
using SW.Logger.ElasticSerach;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure
find . -type f -name "*.cs" | head -20

Repository: simplify9/Bitween-api

Length of output: 1063


🏁 Script executed:

# Search for references to ElasticSerach and ElasticSearch
rg "ElasticSer(ch|ach)" -i --type cs

Repository: simplify9/Bitween-api

Length of output: 178


🏁 Script executed:

# Look specifically in SW.Logger or related packages
fd -type f ".*Logger.*" | grep -E "\.(cs|csproj)" | head -20

Repository: simplify9/Bitween-api

Length of output: 235


🏁 Script executed:

# Check the Startup.cs file in SW.Bitween.Web
cat -n SW.Bitween.Web/Startup.cs | head -50

Repository: simplify9/Bitween-api

Length of output: 1999


🏁 Script executed:

# Search for namespace declarations matching ElasticSer*
rg "namespace.*ElasticSer" -i --type-list

Repository: simplify9/Bitween-api

Length of output: 454


🏁 Script executed:

# Search for class/interface definitions in ElasticSerach namespace
rg "namespace.*ElasticSer|class.*ElasticSer|interface.*ElasticSer" -i

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Check if SW.Logger is a local project or external package
fd -type f -name "*.csproj" | xargs grep -l "SW.Logger" 2>/dev/null

Repository: simplify9/Bitween-api

Length of output: 237


🏁 Script executed:

# Look for any SW.Logger related files
find . -path "*/SW.Logger*" -o -path "*Logger*" 2>/dev/null | grep -v node_modules | head -20

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Check csproj files for SW.Logger package reference
find . -name "*.csproj" -exec cat {} \; | grep -i "sw.logger" -A 2 -B 2

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Search for what's actually being used from ElasticSerach namespace
rg "ElasticSerach\." -i

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Check if there's a packages.config or nuget references
find . -name "packages.config" -o -name "*.lock.json"

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Look for any build output or warnings about this namespace
find . -name "*.sln" -exec cat {} \; | head -50

Repository: simplify9/Bitween-api

Length of output: 3685


🏁 Script executed:

# Read the SW.Bitween.Web.csproj file
cat SW.Bitween.Web/SW.Bitween.Web.csproj

Repository: simplify9/Bitween-api

Length of output: 7652


🏁 Script executed:

# Search for all using statements with Logger to see what pattern is used
rg "using SW.Logger"

Repository: simplify9/Bitween-api

Length of output: 264


Correct the namespace typo in the using statement.

The package reference is SimplyWorks.Logger.ElasticSearch, not ElasticSerach. The current import will fail to compile. Change line 30 to use the correct namespace.

Suggested fix
-using SW.Logger.ElasticSerach;
+using SW.Logger.ElasticSearch;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
using SW.Logger.ElasticSerach;
using SW.Logger.ElasticSearch;
🤖 Prompt for AI Agents
In `@SW.Bitween.Web/Startup.cs` at line 30, Update the incorrect using directive
"SW.Logger.ElasticSerach" to the correct namespace
"SimplyWorks.Logger.ElasticSearch" so the import compiles; locate the using
statement at the top of Startup.cs (the line showing SW.Logger.ElasticSerach)
and replace it with the proper namespace name to match the referenced package.

Comment on lines +18 to +25
<PackageReference Include="SimplyWorks.Bus" Version="8.1.7" />
<PackageReference Include="SimplyWorks.CloudFiles.AS.Extensions" Version="8.1.1" />
<PackageReference Include="SimplyWorks.CloudFiles.OC.Extensions" Version="8.1.1" />
<PackageReference Include="SimplyWorks.CloudFiles.S3.Extensions" Version="8.1.1" />
<PackageReference Include="SimplyWorks.CqApi" Version="8.2.6" />
<PackageReference Include="SimplyWorks.Logger.ElasticSearch" Version="8.1.1" />
<PackageReference Include="SimplyWorks.PrimitiveTypes" Version="8.1.2" />
<PackageReference Include="SimplyWorks.Serverless" Version="8.1.1" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

SimplyWorks.Bus 8.1.7 release notes breaking changes

💡 Result:

For SimplyWorks.Bus 8.1.7 (published Jan 25, 2026), there don’t appear to be curated “release notes” on GitHub Releases (the repo shows no releases), but the NuGet page points to the repo/tags for change history. (nuget.org)

Likely breaking changes in 8.1.7

  • RabbitMQ Management URL default changed: default ManagementUrl moved from http://{host}:15672 to https://{host} (no port). If your RabbitMQ management endpoint is still on :15672 (common default), you’ll need to explicitly set ManagementUrl to keep things working. (github.com)
  • ManagementClient instantiation changed: code now constructs the RabbitMQ ManagementClient using a Uri built from busOptions.ManagementUrl instead of an injected HttpClient, which can break setups relying on DI-configured HttpClient behavior (custom handlers, proxies, cert settings, timeouts, etc.). (github.com)

Citations:


🌐 Web query:

SimplyWorks.CqApi 8.2.6 release notes

💡 Result:

SimplyWorks.CqApi 8.2.6 (tagged Jan 12, 2026) – release notes (what changed)

  • Removed an outdated System.IdentityModel.Tokens.Jwt NuGet package reference (v8.3.1) from the unit test project (SW.CqApi.UnitTests.csproj). [1][2]

Notes on availability

  • The repo has no GitHub “Releases” entries; 8.2.6 is published as a tag. [1][3]
  • On nuget.org, the latest listed package version is 8.0.5 (last updated Aug 11, 2025), so 8.2.6 may not be published there (or may be published elsewhere). [3]

Sources: [1] https://github.com/simplify9/SW-CqApi/tags • [2] simplify9/SW-CqApi#29 • [3] https://www.nuget.org/packages/SimplyWorks.CqApi


🏁 Script executed:

# Search git history for previous package versions to confirm the 8.0.x → 8.1.x jump claim
cd SW.Bitween.Web && git log --oneline -p SW.Bitween.Web.csproj 2>/dev/null | grep -A 50 "PackageReference" | head -100 || echo "Git history not available or file recently added"

Repository: simplify9/Bitween-api

Length of output: 6158


🏁 Script executed:

# Search entire codebase for any CHANGELOG, migration guide, or release notes files
find . -type f \( -iname "*changelog*" -o -iname "*migration*" -o -iname "*release*" \) | head -20

Repository: simplify9/Bitween-api

Length of output: 47


🏁 Script executed:

# Check if there are any comments or documentation in the csproj file itself about version migrations
cat -n SW.Bitween.Web/SW.Bitween.Web.csproj | head -50

Repository: simplify9/Bitween-api

Length of output: 3909


SimplyWorks.Bus 8.1.7 introduces breaking changes requiring RabbitMQ configuration updates.

  • RabbitMQ ManagementUrl default changed: moved from http://{host}:15672 to https://{host} (no port). If your RabbitMQ management endpoint uses the :15672 port (common default), you must explicitly set ManagementUrl to prevent runtime failures.
  • ManagementClient instantiation changed: now constructs from Uri directly instead of using an injected HttpClient. This breaks setups relying on DI-configured HttpClient behavior (custom handlers, proxies, certificate settings, timeouts).

Confirm RabbitMQ configuration and any custom HttpClient setup for the Bus package are compatible with these changes. For other packages (SimplyWorks.CloudFiles.*, SimplyWorks.Logger.ElasticSearch, SimplyWorks.PrimitiveTypes, SimplyWorks.Serverless), public release notes are unavailable; review their usage in code for potential compatibility issues.

🤖 Prompt for AI Agents
In `@SW.Bitween.Web/SW.Bitween.Web.csproj` around lines 18 - 25, SimplyWorks.Bus
8.1.7 introduces breaking changes: the RabbitMQ ManagementUrl default and
ManagementClient construction. Update any RabbitMQ configuration to explicitly
set ManagementUrl to the full URL including :15672 if you rely on that port (set
the ManagementUrl property where you configure the Bus), and locate usages that
create or resolve ManagementClient (or any code expecting an injected HttpClient
for rabbit management) and change them to construct or accept a Uri-based
ManagementClient or rework your DI so a wrapper provides the needed HttpClient
behavior; search for ManagementUrl, ManagementClient and registrations of
SimplyWorks.Bus to apply these fixes and verify any custom HttpClient handlers,
proxies or certificates are preserved via a wrapper or updated client factory.

@mmalkhatib
mmalkhatib merged commit 9a9193e into releases/r8.0 Jan 26, 2026
4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 11, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants