-
Notifications
You must be signed in to change notification settings - Fork 5
CometServer Services
Sub-page of CometServer. Read that first for the overall picture.
The Services layer sits between the Carter modules (see CometServer Modules) and the auto-generated DAOs in CDP4-ORM. It is where the ECSS-E-TM-10-25 Annex A model is interpreted: cross-cutting business logic, request authorization, container resolution, revision tracking, JSON-exchange import/export, change-log composition, and the orchestration of every Create/Update/Delete operation.
CometServer/AutoGenServices/ is the data-access half of this layer. Each generated XxxService derives from ServiceBase and forwards calls to the matching IXxxDao while applying authorization. The generated services are scanned and registered by Autofac in Startup.cs:
builder.RegisterAssemblyTypes(typeof(ServiceBase).Assembly)
.Where(x => typeof(ServiceBase).IsAssignableFrom(x))
.AsImplementedInterfaces()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies)
.InstancePerLifetimeScope();The folders under CometServer/Services/ are everything that wraps, complements or coordinates that flow.
| Folder | Responsibility |
|---|---|
Operations/ |
The CUD pipeline: IOperationProcessor walks the posted operation, dispatches to services, and runs the SideEffects. |
Operations/SideEffects/ |
The per-Thing lifecycle hooks. Detail in [[CometServer SideEffects |
BusinessLogic/ |
Algorithmic services that are not bound to a single DAO: parameter value-set computation, RDL chain resolution, file binary handling, finite-state logic. |
Supplemental/ |
Hand-written services that complement the generated services where extra Annex A behaviour is needed (e.g. EngineeringModelService, IterationService, ParameterService, PersonService). |
Authorization/ |
The ISecurityContext carried through the request and used by the SideEffects. |
Resolve/ |
IResolveService — fills in container info and missing types when an operation references a Thing not in the post payload. |
Revision/ |
Revision marking, registry writes, and time-travel reads. |
Cache/ |
The ICacheService that maintains the *_Cache JSONB tables. |
ChangeLog/ |
Builds ModelLogEntry rows so audit trails appear under SiteDirectory. |
CherryPick/ |
Selective iteration reads filtered by category and class kind. |
Copy/ |
Iteration and engineering model copy logic. |
DataStore/ |
Restore / seed / import controllers used by ExchangeFileImportApi. |
Email/ |
Outbound SMTP via MailKit. |
JsonExchangeFile/ |
Reading and writing the Annex C .zip exchange files. |
ModelInfo/ |
Chain-of-RDL helpers (IChainOfRdlComputationService). |
Protocol/ |
Query-string parsing (QueryParameters) and content negotiation. |
Headers/ |
CDP4-COMET-specific HTTP headers (HeaderInfoProvider). |
Cache/, Revision/, Resolve/
|
Together with the matching DAOs in CDP4-ORM they implement the three storage shapes (per-class tables, *_Revision, *_Cache). |
The generic IServiceProvider defined in Services/Supplemental/IServiceProvider.cs (note: not the BCL System.IServiceProvider) is the registry that maps a Thing type name to its IServiceBase instance — used by the operation processor and by SideEffects when they need to look up a service by name rather than by compile-time type.
Every generated service and every Supplemental service derives from Services/ServiceBase:
public abstract class ServiceBase
{
public const string CreateOperation = "create";
public const string UpdateOperation = "update";
public const string DeleteOperation = "delete";
public IPermissionService PermissionService { get; set; }
public ICredentialsService CredentialsService { get; set; }
// …
}The string constants are used by both the operation processor and the SideEffects to label the in-flight operation. PermissionService and CredentialsService are property-injected, which is why every Autofac registration in Startup.cs for these services uses .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies).
Defined in Services/Operations/IOperationProcessor.cs:
public interface IOperationProcessor
{
Task ProcessAsync(PostOperation operation, NpgsqlTransaction transaction, string partition, Dictionary<string, Stream> fileStore = null);
IReadOnlyList<Thing> OperationOriginalThingCache { get; }
}OperationProcessor (Services/Operations/OperationProcessor.cs) is the orchestrator that turns one HTTP POST into many DAO calls. Its responsibilities, in order:
-
Validate the envelope. Reject operations that target unknown classes, that lack
Iid,RevisionNumberorClassKind, or that touch the read-onlybaseProperties(Iid,RevisionNumber,ClassKind). -
Resolve missing context. Populate the internal
operationThingCache(typedDictionary<DtoInfo, DtoResolveHelper>) with containers and originalThings pulled from the database usingIResolveService. This is what allows a client to post a delta without re-sending every container above the modifiedThing. -
Order operations. Containers before contained, and dependencies before dependants — derived from the metadata in
IMetaInfoProvider(which itself is fed byCDP4Common.MetaInfo). -
Run the SideEffects. For each create/update/delete, invoke the corresponding
IOperationSideEffect.BeforeXxxAsync, dispatch to the service, then invokeAfterXxxAsync. See CometServer SideEffects. -
Track originals.
OperationOriginalThingCacheexposes the set ofThings as they were before the transaction so downstream consumers (the change-log composer, change notifications) can diff old vs. new. -
Record the operation in the revision history by calling into
IRevisionServiceand the*_RevisionDAOs.
top container types are hard-coded as SiteDirectory and EngineeringModel (OperationProcessor.cs:93), matching the partition layout described in CDP4-ORM.
Two folders cover authorization, with a deliberate split:
-
CometServer/Authorization/— the identity layer.ICredentialsServiceresolves the calling principal into aCredentialsobject holding thePerson, theEngineeringModelSetupfor the request (when applicable), and theParticipantRole.IPermissionServiceevaluatesPersonPermissionandParticipantPermissionagainstAccessRightKind.IAccessRightKindServicereads the static defaults declared on each generated DTO.IObfuscationServicestrips out fields the caller is not allowed to see.IOrganizationalParticipationResolverServicefilters byOrganizationalParticipant. -
CometServer/Services/Authorization/— the per-request security context:public interface ISecurityContext { bool ContainerReadAllowed { get; set; } bool ContainerWriteAllowed { get; set; } }
An
ISecurityContextis created when the module begins handling a request and is passed into every DAO call and SideEffect. It carries the cachedContainerReadAllowed/ContainerWriteAlloweddecisions so that authorization is computed once per container per request, not on every property check.
When the operation processor processes a child Thing it expects the parent's permission decisions to already be on the security context — the read/write decisions cascade down the containment tree.
These three folders are the service-side complements to the storage shapes documented in CDP4-ORM and ORM Dao.
Services/Resolve/IResolveService.cs:
public interface IResolveService
{
Task ResolveItemsAsync(NpgsqlTransaction transaction, string partition, Dictionary<DtoInfo, DtoResolveHelper> resolvableInfo);
Task<string> ResolveTypeNameByGuidAsync(NpgsqlTransaction transaction, string partition, Guid iid);
}A POST may reference a Thing only by Iid plus ClassKind. The operation processor builds a Dictionary<DtoInfo, DtoResolveHelper> of the references it cannot evaluate from the request body alone and asks IResolveService to fill them in. Internally it uses the ResolveDao and ContainerDao from CDP4Orm.
Services/Revision/IRevisionService.cs exposes GetAsync(transaction, partition, revision, useDefaultContext) for time-travel reads and a write side that registers the new revision against the partition's top container. Revisions are partition-scoped: the SiteDirectory and each EngineeringModel/Iteration carry their own monotonically increasing revision counter.
IRevisionResolver translates the revisionNumber, revisionFrom, revisionTo query-string parameters (defined in Services/Protocol/QueryParameters.cs) into the actual revision values the DAOs need. It also handles the special head token used by the cache path.
Services/Cache/CacheService.cs keeps the per-class *_Cache tables in sync. Each row contains Iid, Actor, and a Jsonb column holding the latest serialised form of the Thing. Reads served from the cache path are an order of magnitude faster than reading per-class tables and joining containers, but cannot answer historical queries — see the description of MapJsonbToDto in ORM Dao.
The cache is rebuilt as part of every successful CUD transaction. It is invalidated lazily for non-fatal failures during a transaction; for fatal failures the transaction rolls back and the cache is untouched.
Services/BusinessLogic/ is for algorithmic concerns that span more than one DAO. The most important members:
| Service | Role |
|---|---|
IDefaultValueArrayFactory |
Builds default ValueArray<string> instances for new ParameterValueSets. |
IParameterValueSetFactory, IParameterOverrideValueSetFactory, IParameterSubscriptionValueSetFactory
|
Build the set of value sets that match the parameter's ActualFiniteStateList/Option dimensionality. |
IFiniteStateLogicService |
Reconciles ParameterValueSet instances when the underlying ActualFiniteStateList changes. |
IStateDependentParameterUpdateService |
Coordinates value-set updates across state-dependent parameters. |
IOptionBusinessLogicService |
Adds/removes value sets when Options are added or removed from an Iteration. |
IChainOfRdlComputationService |
Resolves the chain of ReferenceDataLibrarys required to dereference a model's parameter types. |
ICachedReferenceDataService |
Memoises chain-of-RDL lookups for the duration of a request. |
IFileArchiveService, IFileBinaryService
|
Manage the on-disk representation of FileRevision content uploaded as multipart. |
IOldParameterContextProvider |
Snapshot of the parameter graph as it was before the operation, used by SideEffects that need to detect changes. |
Services/Supplemental/ holds hand-written services that complement (and in some cases override) the generated AutoGenServices. They are needed when the per-Thing behaviour cannot be expressed by simple DAO delegation alone — e.g. when one create on the wire fans out to many DAO writes, or when a service has to enforce a containment invariant that the SideEffects can't see.
Examples:
-
EngineeringModelServiceandIterationService— top-container creation involves seeding defaultParameterValueSets, propagatingOptionandPublicationdefaults, and registering the new partition. -
ParameterService,ParameterOverrideService,ParameterSubscriptionService— coordinate value-set creation and migration alongside the generated DAOs. -
PersonService— extra logic around password handling and authentication-person resolution. -
FileService,FolderService,CommonFileStoreService,DomainFileStoreService— bridge file binaries on disk with the metadata in the database. -
ActualFiniteStateListService,PublicationService— manage cascading updates that a single SideEffect couldn't.
These services follow the same pattern as the generated ones (deriving from ServiceBase) and are picked up by the same Autofac assembly scan.
Services/JsonExchangeFile/JsonExchangeFileReader.cs and JsonExchangeFileWriter.cs implement the round-trip between a CDP4 zip exchange archive and the database. The reader is what ExchangeFileImportApi's /Data/Exchange and /Data/Import invoke; the writer feeds ExchangeFileExportApi's /export. ZipArchiveWriter packages the JSON streams (one per partition) plus referenced file binaries into a single archive.
JsonExchangeFileReader does not go through IOperationProcessor. It writes directly through the DAOs because the exchange file is canonical input — there are no client deltas to validate against the existing state — and because it sets revision numbers explicitly to preserve the source history.
Services/DataStore/DataStoreController.cs orchestrates the destructive operations exposed at /Data/Exchange, /Data/Import and /Data/Restore. The cdp4server and cdp4serverrestore databases described in Configuration are the two databases this controller toggles between. DataStoreConnectionChecker is used by HealthModule's /ready probe to confirm the backtier is reachable.
CometServer/ChangeNotification/ (sibling to Services/) composes the weekly change e-mail. The Hangfire registration is in Startup.cs:520-523:
appLifetime.ApplicationStarted.Register(() =>
{
RecurringJob.AddOrUpdate<ChangeNoticationService>(
"ChangeNotificationService.Execute",
notificationService => notificationService.ExecuteAsync(),
Cron.Weekly(DayOfWeek.Monday, 0, 15));
});Services/ChangeLog/ChangeLogService writes per-operation ModelLogEntry rows during a POST. ChangelogBodyComposer later turns those entries into the e-mail body.
Services/CherryPick/CherryPickService and ContainmentService together implement the cherryPick query parameter accepted by EngineeringModelApi. Cherry-picking is a read-side filter: the server walks the iteration's containment tree and returns only the Things whose Category and ClassKind match the requested predicate. It is used by integration tools that want a typed slice of an iteration without paying for the full graph.
- For the lifecycle hooks the operation processor calls into, continue with CometServer SideEffects.
- For the SQL produced by the services' DAO calls, see ORM Dao.
- For the API surface that the modules expose to clients, see REST API - ICD.
copyright @ Starion Group S.A.