Skip to content

CometServer

samatstarion edited this page May 4, 2026 · 2 revisions

CometServer

CometServer is the ASP.NET Core web host that exposes the CDP4-COMET REST API. It is the main project in the COMET-WebServices-Community-Edition solution and the entry point that all clients talk to.

This page provides an overview of the project, the details of how each layer works is split across the following sub-pages:

  • CometServer Modules: Carter modules and the HTTP routes that implement Annex C.
  • CometServer Services: service layer between the modules and the DAOs (business logic, resolvers, authorization, caching, revisions).
  • CometServer SideEffects: the lifecycle hooks that enforce data-model invariants around every persistence operation.

What it implements

  • Annex C of ECSS-E-TM-10-25A: the REST contract documented page-by-page in REST API - ICD.
  • Annex A, the data model, by way of the auto-generated DAOs in CDP4-ORM and the auto-generated services in CometServer/AutoGenServices.
  • Round-trip JSON exchange files: import, export, restore and seeding.
  • Background tasks: long-running operations are surfaced through /tasks/{taskId} and run on Hangfire.
  • Notifications: periodic change-log emails composed by the ChangeNoticationService.

Stack and key dependencies

Verified from CometServer/CometServer.csproj:

Concern Technology
Runtime .NET 10
Web framework ASP.NET Core + Carter 10 (minimal-APIs with module-based routing)
Dependency injection Autofac (registered via AutofacServiceProviderFactory)
Background jobs Hangfire with in-memory storage
Logging Serilog (console + Grafana Loki sinks)
Metrics prometheus-net (/metrics endpoint)
JSON CDP4JsonSerializer
MessagePack CDP4MessagePackSerializer
Service messaging CDP4ServicesMessaging (RabbitMQ producer for change-broadcast)
Authentication plugins CDP4Authentication, CDP4DatabaseAuthentication, CDP4WspDatabaseAuthentication
File handling SharpZipLib for the .zip exchange files
Mail MailKit for change-log notifications

Project layout

CometServer/
├── Program.cs                 // entry point; builds the Generic Host with Autofac and Serilog
├── Startup.cs                 // Autofac registrations, Carter wiring, auth scheme setup
├── Modules/                   // Carter modules — see [[CometServer Modules|CometServer-Modules]]
│   ├── 10-25/                 //   the Annex C endpoints (SiteDirectory, EngineeringModel, exchange)
│   ├── Authentication/        //   /login, /logout, /refresh, /username, /auth/schemes
│   ├── Health/                //   /health/startup, /healthz, /ready
│   ├── Tasks/                 //   /tasks, /tasks/{taskId}
│   ├── Root/                  //   GET / landing page
│   ├── CarterExtensions/      //   shared response/header helpers
│   └── Constraints/           //   custom route constraints (e.g. EnumerableOfGuid)
├── Services/                  // Service layer — see [[CometServer Services|CometServer-Services]]
│   ├── Operations/            //   IOperationProcessor + SideEffects pipeline
│   ├── BusinessLogic/         //   parameter value-set algorithms, RDL chains, file binaries
│   ├── Authorization/         //   ISecurityContext (per-request)
│   ├── Resolve/               //   container/path resolution
│   ├── Revision/              //   revision marking and time-travel resolution
│   ├── Cache/                 //   *_Cache table maintenance
│   ├── Supplemental/          //   hand-written services that complement AutoGenServices
│   ├── ChangeLog/             //   model log entry composition
│   ├── CherryPick/            //   filtered iteration reads
│   ├── DataStore/             //   restore/seed/import controllers
│   ├── Email/                 //   SMTP sender
│   ├── JsonExchangeFile/      //   .zip/.json import + export
│   ├── ModelInfo/             //   chain-of-RDL helpers
│   ├── Protocol/              //   query-string parsing and content negotiation
│   ├── Headers/               //   CDP4-specific HTTP headers
│   └── Copy/                  //   model and iteration copy
├── AutoGenServices/           // ~360 generated Service classes, one per Thing subtype
├── Authentication/            // Anonymous / Basic / Bearer schemes + JWT issuing
├── Authorization/             // Credentials, PermissionService, AccessRightKindService, Obfuscation
├── ChangeNotification/        // weekly e-mail composition, subscriber resolution
├── Configuration/             // strongly-typed appsettings.json bindings (AppConfig…)
├── Health/                    // CometStartUpService + CometHasStartedService
├── Tasks/                     // ICometTaskService — bookkeeping for long-running POSTs
├── MigrationEngine/           // schema migrations applied at boot
├── Helpers/                   // Cdp4TransactionManager, IDataSource, request helpers
├── Extensions/                // string and HTTP extension methods
├── Enumerations/              // shared enums
├── Exceptions/                // BadRequestException, Cdp4ModelValidationException, …
├── Resources/                 // ascii-art banner and the root HTML page
└── wwwroot/                   // static files served by ASP.NET Core

Composition root

Program.cs builds a Generic Host configured with the Autofac service provider factory, Serilog, and Startup. After host.RunAsync() returns, any pending IBackgroundThingsMessageProducer queue is drained.

Startup.cs does three things:

  1. ConfigureServices: adds Carter, CORS (open by default), the in-memory cache, Hangfire, Autofac-aware service-messaging hooks, and routing-constraint maps. The bulk of the method is SetUpAuthentication, which validates the appsettings keys for Basic / Local JWT / External JWT and wires the matching AuthenticationBuilder calls and authorization policies.
  2. ConfigureContainer: registers everything with Autofac. The pattern is dominated by three wide assembly scans:
    • All BaseDao subclasses from CDP4Orm.
    • All ServiceBase subclasses from this assembly (the auto-generated services).
    • All IBusinessLogicService and all IOperationSideEffect implementations.
    • All Carter modules. Singletons are reserved for cross-request collaborators (AppConfigService, MigrationEngine, ResourceLoader, CometTaskService, CherryPickService); everything that touches a transaction is InstancePerLifetimeScope so it lives only for the duration of one HTTP request.
  3. Configure — assembles the request pipeline: exception handler, Serilog request logging, static files, the Hangfire dashboard at /hangfire, routing, CORS, authentication, authorization, Prometheus metrics, then MapCarter() to bind every Carter module's AddRoutes. After the host has started, the change-notification job is registered as a weekly Hangfire RecurringJob.

Request flow

For a typical POST /EngineeringModel/{iid}/iteration/{iid} operation:

  1. Carter module (EngineeringModelApi) matches the route and calls into a private async handler.
  2. ICredentialsService resolves the calling Person and their ParticipantRole for the engineering model in the URL.
  3. Cdp4TransactionManager opens an NpgsqlTransaction against the cdp4server database (the backtier, configured in BacktierConfig).
  4. IOperationProcessor.ProcessAsync deserializes the PostOperation body and walks the create/update/delete sets in dependency order. For each entry it:
    • Fires the matching IOperationSideEffect.BeforeCreate/Update/DeleteAsync (see CometServer SideEffects).
    • Calls the relevant auto-generated Service (AutoGenServices/) which delegates to the CDP4Orm DAO.
    • Fires the matching AfterCreate/Update/DeleteAsync hook.
  5. IRevisionService writes a RevisionRegistry entry and updates *_Revision rows.
  6. ICacheService rebuilds the affected *_Cache JSONB rows.
  7. The transaction commits; the modified Things are read back at the requested revision (or head) and serialized as JSON or MessagePack depending on Accept.
  8. IBackgroundThingsMessageProducer queues a change message to RabbitMQ for any subscribers (UI clients, external systems).

The same skeleton applies to GET, minus the SideEffects, the DAO calls return existing rows, and the operation processor is bypassed.

Where to look next

Clone this wiki locally