├── tests └── README.md ├── scripts └── README.md ├── src ├── didact-cli │ ├── README.md │ ├── appsettings.Staging.json │ ├── appsettings.Development.json │ ├── appsettings.Production.json │ ├── Constants │ │ └── CliConstants.cs │ ├── Settings │ │ ├── VersionCommandSettings.cs │ │ ├── TestCommandSettings.cs │ │ ├── UiInstallCommandSettings.cs │ │ └── EngineInstallCommandSettings.cs │ ├── appsettings.json │ ├── Commands │ │ ├── TestCommand.cs │ │ ├── VersionCommand.cs │ │ ├── UiInstallCommand.cs │ │ └── EngineInstallCommand.cs │ ├── Services │ │ ├── AppSettings.cs │ │ ├── TypeResolver.cs │ │ └── TypeRegistrar.cs │ ├── Properties │ │ └── launchsettings.json │ ├── DidactCli.csproj │ └── Program.cs ├── didact-core │ ├── README.md │ ├── Triggers │ │ ├── ICronScheduleTrigger.cs │ │ ├── ITrigger.cs │ │ └── CronScheduleTrigger.cs │ ├── Deployments │ │ └── IDeploymentContext.cs │ ├── Environments │ │ └── IEnvironmentContext.cs │ ├── Flows │ │ ├── IFlowContext.cs │ │ ├── IFlowLogger.cs │ │ ├── IFlowConfigurationContext.cs │ │ ├── IFlowRunContext.cs │ │ ├── IFlowExecutionContext.cs │ │ ├── IFlow.cs │ │ └── IFlowConfigurator.cs │ ├── Constants │ │ └── Defaults.cs │ ├── Plugins │ │ └── IPluginRegistrar.cs │ ├── DidactCore.csproj │ └── SomeFlow.cs ├── didact-ui │ ├── README.md │ ├── nuxt-app │ │ ├── public │ │ │ ├── robots.txt │ │ │ └── favicon.ico │ │ ├── assets │ │ │ └── css │ │ │ │ └── main.css │ │ ├── server │ │ │ └── tsconfig.json │ │ ├── .env.example │ │ ├── tsconfig.json │ │ ├── utils │ │ │ ├── environment │ │ │ │ └── index.ts │ │ │ └── environment-variables │ │ │ │ └── index.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── nuxt.config.ts │ │ └── app.vue │ └── dotnet-app │ │ ├── appsettings.Staging.json │ │ ├── appsettings.Production.json │ │ ├── DidactUi.http │ │ ├── appsettings.Development.json │ │ ├── wwwroot │ │ └── README.md │ │ ├── Services │ │ └── UiSettings.cs │ │ ├── appsettings.json │ │ ├── Constants │ │ └── UiConstants.cs │ │ ├── Exceptions │ │ └── MissingEnvironmentVariableException.cs │ │ ├── Controllers │ │ └── EnvironmentVariablesController.cs │ │ ├── DidactUi.csproj │ │ ├── Properties │ │ └── launchSettings.json │ │ └── Program.cs ├── didact-engine │ ├── README.md │ ├── appsettings.Staging.json │ ├── appsettings.Production.json │ ├── Licensing │ │ ├── LicenseChecker.cs │ │ ├── FeatureValidator.cs │ │ ├── LicensingService.cs │ │ ├── didact-prod-license-server-encryption-key.pem │ │ └── LicenseValidator.cs │ ├── Engine │ │ ├── EngineSettings.cs │ │ ├── EngineContext.cs │ │ ├── IEngineService.cs │ │ ├── EngineDto.cs │ │ ├── IEngineRepository.cs │ │ ├── EngineTuningDto.cs │ │ ├── EngineTuningContext.cs │ │ ├── IEngineSupervisor.cs │ │ ├── EngineRepository.cs │ │ ├── EngineService.cs │ │ └── EngineSupervisor.cs │ ├── Plugins │ │ ├── DeploymentPluginContext.cs │ │ ├── ShadowCopyService.cs │ │ ├── NoMatchedPluginException.cs │ │ ├── MultipleMatchedPluginsException.cs │ │ ├── PluginsModule.cs │ │ ├── PluginsService.cs │ │ ├── PluginAssemblyLoadContext.cs │ │ ├── PluginDependencyInjector.cs │ │ ├── IPluginContainer.cs │ │ ├── IPluginDependencyInjector.cs │ │ ├── IPluginContainers.cs │ │ ├── PluginExecutionVersion.cs │ │ ├── PluginContainers.cs │ │ └── PluginContainer.cs │ ├── Flows │ │ ├── FlowRunRepository.cs │ │ ├── FlowTypeNotFoundException.cs │ │ ├── SaveFlowConfigurationsException.cs │ │ ├── FlowExecutionContext.cs │ │ ├── FlowConfigurator.cs │ │ └── IFlowRepository.cs │ ├── appsettings.Development.json │ ├── Logging │ │ ├── EngineLogDto.cs │ │ ├── FlowRunLogDto.cs │ │ ├── EngineLogChannel.cs │ │ ├── FlowRunLogChannel.cs │ │ ├── EngineLoggerModule.cs │ │ └── FlowRunLoggerModule.cs │ ├── appsettings.json │ ├── Threading │ │ ├── ThreadpoolService.cs │ │ └── DidactThreadpoolTaskScheduler.cs │ ├── Scheduler │ │ ├── SchedulerService.cs │ │ └── SchedulerModule.cs │ ├── Modules │ │ ├── IModule.cs │ │ ├── ModuleContext.cs │ │ └── ModuleSupervisor.cs │ ├── Constants │ │ ├── Defaults.cs │ │ └── EngineConstants.cs │ ├── Deployments │ │ └── DeploymentsService.cs │ ├── Workers │ │ └── WorkersModule.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Controllers │ │ └── MaintenanceController.cs │ ├── DidactEngine.csproj │ └── Services │ │ └── BackgroundServices │ │ └── WorkerBackgroundService.cs ├── didact-primitives │ ├── Constants │ │ ├── Defaults.cs │ │ ├── ExecutionModes.cs │ │ ├── TriggerTypes.cs │ │ ├── TriggerScopes.cs │ │ ├── FlowRunEventTypes.cs │ │ ├── BlockStates.cs │ │ ├── FlowLibrarySourceTypes.cs │ │ ├── FlowConfiguratorStates.cs │ │ └── QueueTypes.cs │ └── DidactPrimitives.csproj ├── didact-services │ ├── Deployments │ │ └── DeploymentContext.cs │ ├── Environments │ │ └── EnvironmentContext.cs │ ├── Flows │ │ ├── FlowContext.cs │ │ └── IFlowRunRepository.cs │ ├── FlowRuns │ │ └── FlowRunContext.cs │ ├── Workers │ │ ├── IWorkerContext.cs │ │ └── WorkerContext.cs │ ├── DataModel │ │ ├── Entities │ │ │ ├── ScheduleType.cs │ │ │ ├── TriggerScope.cs │ │ │ ├── DeploymentType.cs │ │ │ ├── QueueDirection.cs │ │ │ ├── DeploymentStatus.cs │ │ │ ├── FlowRunEventType.cs │ │ │ ├── CronScheduleTrigger.cs │ │ │ ├── DeploymentSourceType.cs │ │ │ ├── Environment.cs │ │ │ ├── DeploymentSourceFilesystem.cs │ │ │ ├── TriggerType.cs │ │ │ ├── State.cs │ │ │ ├── ExecutionMode.cs │ │ │ ├── FlowRunStateChangeEvent.cs │ │ │ ├── HyperQueueItem.cs │ │ │ ├── StrictQueueItem.cs │ │ │ ├── Trigger.cs │ │ │ ├── FlowRunEvent.cs │ │ │ ├── FlowRunLogEvent.cs │ │ │ ├── FlowVersion.cs │ │ │ ├── Deployment.cs │ │ │ ├── Engine.cs │ │ │ ├── HyperQueue.cs │ │ │ ├── StrictQueue.cs │ │ │ ├── FlowSchedule.cs │ │ │ ├── BlockRun.cs │ │ │ ├── Flow.cs │ │ │ ├── Organization.cs │ │ │ └── FlowRun.cs │ │ ├── Configurations │ │ │ ├── StateConfiguration.cs │ │ │ ├── StrictQueueConfiguration.cs │ │ │ ├── HyperQueueConfiguration.cs │ │ │ ├── TriggerTypeConfiguration.cs │ │ │ ├── OrganizationConfiguration.cs │ │ │ ├── ScheduleTypeConfiguration.cs │ │ │ ├── FlowScheduleConfiguration.cs │ │ │ ├── EngineConfiguration.cs │ │ │ ├── FlowConfiguration.cs │ │ │ ├── HyperQueueItemConfiguration.cs │ │ │ ├── StrictQueueItemConfiguration.cs │ │ │ ├── BlockRunConfiguration.cs │ │ │ └── FlowRunConfiguration.cs │ │ ├── Migrations │ │ │ └── MigrationExtensions.cs │ │ └── Contexts │ │ │ └── DidactDbContext.cs │ ├── DidactServices.csproj │ ├── Constants │ │ └── Constants.cs │ └── HostAppEnvironments │ │ └── HostAppEnvironmentService.cs ├── versions.json └── Didact.sln ├── LICENSE.md ├── README.md └── LICENSE-COMM.md /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts -------------------------------------------------------------------------------- /src/didact-cli/README.md: -------------------------------------------------------------------------------- 1 | # Didact CLI -------------------------------------------------------------------------------- /src/didact-core/README.md: -------------------------------------------------------------------------------- 1 | # Didact Core -------------------------------------------------------------------------------- /src/didact-ui/README.md: -------------------------------------------------------------------------------- 1 | # Didact UI -------------------------------------------------------------------------------- /src/didact-engine/README.md: -------------------------------------------------------------------------------- 1 | # Didact Engine -------------------------------------------------------------------------------- /src/didact-engine/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/didact-engine/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/didact-cli/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/didact-cli/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/didact-cli/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "NuxtDevServerUrl": "http://localhost:3000" 3 | } -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "NuxtDevServerUrl": "http://localhost:3000" 3 | } -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/.env.example: -------------------------------------------------------------------------------- 1 | VITE_ENVIRONMENT_VARIABLES_DEV_BASE_URL = '' # The base URL for the containing dotnet web api. -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DidactHQ/didact/HEAD/src/didact-ui/nuxt-app/public/favicon.ico -------------------------------------------------------------------------------- /src/didact-cli/Constants/CliConstants.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCli.Constants 2 | { 3 | public static class CliConstants 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /src/didact-engine/Licensing/LicenseChecker.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Licensing 2 | { 3 | public class LicenseChecker 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/didact-engine/Licensing/FeatureValidator.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Licensing 2 | { 3 | public class FeatureValidator 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineSettings.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Engine 2 | { 3 | public class EngineSettings 4 | { 5 | // TODO 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/DeploymentPluginContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Plugins 2 | { 3 | public class DeploymentPluginContext 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/FlowRunRepository.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Flows 2 | { 3 | public class FlowRunRepository 4 | { 5 | // TODO Implement 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/DidactUi.http: -------------------------------------------------------------------------------- 1 | @DidactUi_HostAddress = http://localhost:5289 2 | 3 | GET {{DidactUi_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /src/didact-engine/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/didact-cli/Settings/VersionCommandSettings.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace DidactCli.Settings 4 | { 5 | public class VersionCommandSettings : CommandSettings 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/didact-core/Triggers/ICronScheduleTrigger.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Triggers 2 | { 3 | public interface ICronScheduleTrigger : ITrigger 4 | { 5 | string CronExpression { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/Defaults.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class Defaults 4 | { 5 | public const string DefaultEnvironmentName = "Default"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/didact-primitives/DidactPrimitives.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Engine 2 | { 3 | public class EngineContext 4 | { 5 | public long EngineId { get; set; } 6 | 7 | public Guid UniversalId { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/ExecutionModes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class ExecutionModes 4 | { 5 | public const string Auto = "Auto"; 6 | public const string Deferred = "Deferred"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "NuxtDevServerUrl": "http://localhost:3000" 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/utils/environment/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Determines if the single page app is currently running within the Nuxt dev server or not. 3 | * @returns 4 | */ 5 | const isDev = () => { 6 | return import.meta.dev; 7 | } 8 | 9 | export { isDev } -------------------------------------------------------------------------------- /src/didact-services/Deployments/DeploymentContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Deployments 2 | { 3 | public class DeploymentContext 4 | { 5 | public long DeploymentId { get; set; } 6 | 7 | public string? Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-core/Deployments/IDeploymentContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Deployments 2 | { 3 | public interface IDeploymentContext 4 | { 5 | public long DeploymentId { get; set; } 6 | 7 | public string? Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-core/Environments/IEnvironmentContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Environments 2 | { 3 | public interface IEnvironmentContext 4 | { 5 | public long EnvironmentId { get; set; } 6 | 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-services/Environments/EnvironmentContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Environments 2 | { 3 | public class EnvironmentContext 4 | { 5 | public long EnvironmentId { get; set; } 6 | 7 | public string Name { get; set; } = null!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/wwwroot/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt App 2 | 3 | The transpiled Nuxt app, where Didact UI is actually defined, goes into this folder. 4 | 5 | This dotnet host application is like a server-side wrapper around the Nuxt app. Together, they define the composite Didact UI application. -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Flows 2 | { 3 | public interface IFlowContext 4 | { 5 | public long FlowId { get; set; } 6 | 7 | public string? Name { get; set; } 8 | 9 | public string TypeName { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/didact-cli/Settings/TestCommandSettings.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace DidactCli.Settings 4 | { 5 | public class TestCommandSettings : CommandSettings 6 | { 7 | [CommandArgument(0, "[abcd]")] 8 | public string Version { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/didact-services/Flows/FlowContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Flows 2 | { 3 | public class FlowContext 4 | { 5 | public long FlowId { get; set; } 6 | 7 | public string? Name { get; set; } 8 | 9 | public string TypeName { get; set; } = null!; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Services/UiSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DidactUi.Services 4 | { 5 | public class UiSettings 6 | { 7 | [JsonPropertyName("didactEngineBaseUrl")] 8 | public string DidactEngineBaseUrl { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactCore.Flows 4 | { 5 | public interface IFlowLogger 6 | { 7 | void LogInformation(string message, params object[] args); 8 | 9 | void LogError(Exception ex, string message, params object[] args); 10 | } 11 | } -------------------------------------------------------------------------------- /src/didact-engine/Engine/IEngineService.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Engine 2 | { 3 | public interface IEngineService 4 | { 5 | EngineContext? EngineContext { get; } 6 | 7 | CancellationToken CancellationToken { get; } 8 | 9 | Task PollEngineShutdownAsync(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/EngineLogDto.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Logging 2 | { 3 | public class EngineLogDto 4 | { 5 | public string LogLevel { get; set; } = null!; 6 | 7 | public string Message { get; set; } = null!; 8 | 9 | public DateTime Timestamp { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/FlowRunLogDto.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Logging 2 | { 3 | public class FlowRunLogDto 4 | { 5 | public string LogLevel { get; set; } = null!; 6 | 7 | public string Message { get; set; } = null!; 8 | 9 | public DateTime Timestamp { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://console.didact.dev/schema/versions.json", 3 | "platform": { 4 | "version": "v0", 5 | "componentVersions": { 6 | "ui": "v0", 7 | "engine": "v0", 8 | "cli": "v0", 9 | "core": "v0" 10 | }, 11 | "componentVersionsHash": "" 12 | } 13 | } -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineDto.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Engine 2 | { 3 | public class EngineDto 4 | { 5 | // TODO Implement 6 | public long EngineId { get; set; } 7 | 8 | public string UniqueName { get; set; } = null!; 9 | 10 | public string? Name { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/didact-cli/Settings/UiInstallCommandSettings.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace DidactCli.Settings 4 | { 5 | public class UiInstallCommandSettings : CommandSettings 6 | { 7 | [CommandOption("-p|--path ")] 8 | public string InstallationPath { get; set; } = null!; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "NuxtDevServerUrl": "http://localhost:3000", 10 | "DidactConsole": { 11 | "BaseUrl": "" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/didact-core/Triggers/ITrigger.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Triggers 2 | { 3 | public interface ITrigger 4 | { 5 | public string TriggerType { get; } 6 | 7 | public string TriggerScope { get; set; } 8 | 9 | public string? Name { get; set; } 10 | 11 | public string? Description { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/didact-engine/Licensing/LicensingService.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Licensing 2 | { 3 | public class LicensingService 4 | { 5 | private readonly ILogger _logger; 6 | 7 | public LicensingService(ILogger logger) 8 | { 9 | _logger = logger; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/IEngineRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace DidactCore.Engine 4 | { 5 | public interface IEngineRepository 6 | { 7 | Task CheckForEngineShutdownAsync(); 8 | 9 | Task GetEngineAsync(); 10 | 11 | Task GetEngineTuningAsync(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/didact-engine/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "Didact": "Data Source=localhost;Initial Catalog=Didact;Integrated Security=true" 11 | }, 12 | "Didact": { 13 | } 14 | } -------------------------------------------------------------------------------- /src/didact-cli/Settings/EngineInstallCommandSettings.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace DidactCli.Settings 4 | { 5 | public class EngineInstallCommandSettings : CommandSettings 6 | { 7 | [CommandOption("-p|--path ")] 8 | // Add default path? 9 | public string InstallationPath { get; set; } = null!; 10 | } 11 | } -------------------------------------------------------------------------------- /src/didact-primitives/Constants/TriggerTypes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class TriggerTypes 4 | { 5 | public const string API = "API"; 6 | public const string UI = "UI"; 7 | public const string CronSchedule = "CRON Schedule"; 8 | public const string FlowRun = "Flow Run"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/TriggerScopes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class TriggerScopes 4 | { 5 | public const string Universal = "Universal"; 6 | public const string Organization = "Organization"; 7 | public const string Environment = "Environment"; 8 | public const string Flow = "Flow"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/didact-cli/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "MyCommand": "Debug" 6 | } 7 | }, 8 | "DidactEngine": { 9 | "RuntimeEnvironmentVariablesFileName": "enginesettings.json" 10 | }, 11 | "DidactUi": { 12 | "RuntimeEnvironmentVariablesFileName": "uisettings.json" 13 | }, 14 | "TestSetting": "Hello worldddddd!" 15 | } -------------------------------------------------------------------------------- /src/didact-cli/Commands/TestCommand.cs: -------------------------------------------------------------------------------- 1 | using DidactCli.Settings; 2 | using Spectre.Console.Cli; 3 | 4 | namespace DidactCli.Commands 5 | { 6 | public class TestCommand : Command 7 | { 8 | public override int Execute(CommandContext context, TestCommandSettings settings) 9 | { 10 | // Omitted 11 | return 0; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowConfigurationContext.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | using DidactCore.Environments; 3 | 4 | namespace DidactCore.Flows 5 | { 6 | public interface IFlowConfigurationContext 7 | { 8 | IEnvironmentContext EnvironmentContext { get; } 9 | 10 | IDeploymentContext DeploymentContext { get; } 11 | 12 | IFlowConfigurator Configurator { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowRunContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Flows 2 | { 3 | public interface IFlowRunContext 4 | { 5 | public long FlowRunId { get; set; } 6 | 7 | public long FlowVersionId { get; set; } 8 | 9 | public string FlowVersion { get; set; } 10 | 11 | public string? JsonPayload { get; set; } 12 | 13 | public int TimeoutSeconds { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Constants/UiConstants.cs: -------------------------------------------------------------------------------- 1 | namespace DidactUi.Constants 2 | { 3 | public static class UiConstants 4 | { 5 | public static class CorsPolicyNames 6 | { 7 | public const string Development = "DevelopmentCors"; 8 | public const string Staging = "StagingCors"; 9 | public const string Production = "ProductionCors"; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineTuningDto.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Engine 2 | { 3 | public class EngineTuningDto 4 | { 5 | // TODO Implement 6 | public decimal ThreadFactor { get; set; } 7 | 8 | public decimal TaskFactor { get; set; } 9 | 10 | public int FlowRunCancellationCheckInterval { get; set; } 11 | 12 | public int EngineShutdownCheckInterval { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/didact-services/FlowRuns/FlowRunContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCore.Flows 2 | { 3 | public class FlowRunContext 4 | { 5 | public long FlowRunId { get; set; } 6 | 7 | public long FlowVersionId { get; set; } 8 | 9 | public string FlowVersion { get; set; } = null!; 10 | 11 | public string? JsonPayload { get; set; } 12 | 13 | public int TimeoutSeconds { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineTuningContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Engine 2 | { 3 | public class EngineTuningContext 4 | { 5 | // TODO Implement 6 | public decimal ThreadFactor { get; set; } 7 | 8 | public decimal TaskFactor { get; set; } 9 | 10 | public int FlowRunCancellationCheckInterval { get; set; } 11 | 12 | public int EngineShutdownCheckInterval { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/didact-core/Constants/Defaults.cs: -------------------------------------------------------------------------------- 1 | using DidactPrimitives.Constants; 2 | 3 | namespace DidactCore.Constants 4 | { 5 | public static class Defaults 6 | { 7 | public const string DefaultFlowVersion = "1.0.0"; 8 | public const string DefaultQueueType = QueueTypes.HyperQueue; 9 | public const string DefaultQueueName = "Default"; 10 | public const string DefaultExecutionMode = ExecutionModes.Auto; 11 | } 12 | } -------------------------------------------------------------------------------- /src/didact-core/Plugins/IPluginRegistrar.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace DidactCore.Plugins 4 | { 5 | public interface IPluginRegistrar 6 | { 7 | /// 8 | /// Registers plugin dependencies to be used in plugin-isolated dependency injection. 9 | /// 10 | /// 11 | IServiceCollection RegisterServices(IServiceCollection pluginServiceCollection); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/EngineLogChannel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Channels; 2 | 3 | namespace DidactEngine.Logging 4 | { 5 | public class EngineLogChannel 6 | { 7 | private readonly Channel _channel; 8 | 9 | public Channel Channel => _channel; 10 | 11 | public EngineLogChannel() 12 | { 13 | _channel = System.Threading.Channels.Channel.CreateUnbounded(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/FlowRunLogChannel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Channels; 2 | 3 | namespace DidactEngine.Logging 4 | { 5 | public class FlowRunLogChannel 6 | { 7 | private readonly Channel _channel; 8 | 9 | public Channel Channel => _channel; 10 | 11 | public FlowRunLogChannel() 12 | { 13 | _channel = System.Threading.Channels.Channel.CreateUnbounded(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/didact-engine/Licensing/didact-prod-license-server-encryption-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArXcXnZQVLQiBEG2PwvdJ33vgk1UaqrvQ1+ZAr17IdhYSe1uuCZ4WTOfKT0u8P4HlcDKYq5KwegkBItR7uEhCu17gfGyJ/+nZzEddXSjAN97ar1e3yerr5SsVWKX5LRUtUOGrB8HpHgW5dSVvD8H1WJrvCdCH6NwxhMAZhiZgOMe2c5p4yyhz6HvzYydXlG7C+RH3da6FOAoYPFvzLX57WMZdYVRMtQ3kd28bxaa14HmqcSNsBbh3YOESUxKZH5spqv04xRUaJhI9CosgouoUTGdexremhnY+WkV2BYvXyUjTPPbWUS79F+Qrpi3He6MBl8Ud8pINW7XXf9jJ9HD1UQIDAQAB 3 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /src/didact-engine/Plugins/ShadowCopyService.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Plugins 2 | { 3 | public class ShadowCopyService 4 | { 5 | private readonly ILogger _logger; 6 | 7 | public ShadowCopyService(ILogger logger) 8 | { 9 | _logger = logger; 10 | } 11 | 12 | public async Task ShadowCopyDeployment(CancellationToken cancellationToken) 13 | { 14 | await Task.CompletedTask; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/didact-engine/Threading/ThreadpoolService.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | 3 | namespace DidactEngine.Threading 4 | { 5 | public class ThreadpoolService 6 | { 7 | public string ShutdownMode { get; private set; } = EngineConstants.ThreadpoolShutdownModes.Immediate; 8 | 9 | public decimal ThreadFactor { get; set; } = Defaults.DefaultThreadFactor; 10 | 11 | public CancellationToken CancellationToken { get; set; } 12 | 13 | public ThreadpoolService() { } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/didact-engine/Scheduler/SchedulerService.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Scheduler 2 | { 3 | public class SchedulerService 4 | { 5 | private readonly ILogger _logger; 6 | 7 | public SchedulerService(ILogger logger) 8 | { 9 | _logger = logger; 10 | } 11 | 12 | public async Task ScheduleAsync(CancellationToken cancellationToken) 13 | { 14 | // TODO Implement 15 | await Task.CompletedTask; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/FlowRunEventTypes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class FlowRunEventTypes 4 | { 5 | public const string LogEvent = "Log Event"; 6 | public const string StateChangeEvent = "State Change Event"; 7 | public const string EngineEvent = "Engine Event"; 8 | public const string SchedulerEvent = "Scheduler Event"; 9 | public const string TriggerEvent = "Trigger Event"; 10 | public const string BlockRunEvent = "Block Run Event"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Exceptions/MissingEnvironmentVariableException.cs: -------------------------------------------------------------------------------- 1 | namespace DidactUi.Exceptions 2 | { 3 | [Serializable] 4 | public class MissingEnvironmentVariableException : Exception 5 | { 6 | public MissingEnvironmentVariableException() 7 | { } 8 | 9 | public MissingEnvironmentVariableException(string message) : base(message) 10 | { } 11 | 12 | public MissingEnvironmentVariableException(string message, Exception innerException) : base(message, innerException) 13 | { } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/didact-cli/Services/AppSettings.cs: -------------------------------------------------------------------------------- 1 | namespace DidactCli.Services 2 | { 3 | public class AppSettings 4 | { 5 | public string TestSetting { get; set; } 6 | 7 | public DidactEngine DidactEngine { get; set; } 8 | 9 | public DidactUi DidactUi { get; set; } 10 | } 11 | 12 | public class DidactEngine 13 | { 14 | public string RuntimeEnvironmentVariablesFileName { get; set; } 15 | } 16 | 17 | public class DidactUi 18 | { 19 | public string RuntimeEnvironmentVariablesFileName { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/didact-cli/Commands/VersionCommand.cs: -------------------------------------------------------------------------------- 1 | using DidactCli.Settings; 2 | using Spectre.Console; 3 | using Spectre.Console.Cli; 4 | using System.Reflection; 5 | 6 | namespace DidactCli.Commands 7 | { 8 | public class VersionCommand : Command 9 | { 10 | public override int Execute(CommandContext context, VersionCommandSettings settings) 11 | { 12 | var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 13 | AnsiConsole.Write(version); 14 | return 0; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/didact-primitives/Constants/BlockStates.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class BlockStates 4 | { 5 | public const string Idle = "Idle"; 6 | public const string Running = "Running"; 7 | public const string Retrying = "Retrying"; 8 | public const string Failing = "Failing"; 9 | public const string Failed = "Failed"; 10 | public const string Succeeded = "Succeeded"; 11 | public const string Cancelled = "Cancelled"; 12 | public const string Cancelling = "Cancelling"; 13 | } 14 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | ## Dual Licensing 4 | 5 | This is an open core product that is dual-licensed in the following way: 6 | 7 | - Any source code found under a directory with the word "commercial" or "comm" in the directory name is licensed under the COMM license. 8 | - All other source code is licensed under the AGPLv3 license. 9 | 10 | ## Forking, Modification, and Redistribution 11 | 12 | When forking, modifying, or redistributing this repository, please carefully read through the COMM license details. Source code that is licensed under the COMM license has certain restrictions. -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowExecutionContext.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | using DidactCore.Environments; 3 | using System.Threading; 4 | 5 | namespace DidactCore.Flows 6 | { 7 | public interface IFlowExecutionContext 8 | { 9 | CancellationToken CancellationToken { get; } 10 | 11 | IFlowLogger Logger { get; } 12 | 13 | IEnvironmentContext EnvironmentContext { get; } 14 | 15 | IDeploymentContext DeploymentContext { get; } 16 | 17 | IFlowContext FlowContext { get; } 18 | 19 | IFlowRunContext FlowRunContext { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/didact-services/Workers/IWorkerContext.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | using DidactCore.Environments; 3 | using DidactCore.Flows; 4 | 5 | namespace DidactServices.Workers 6 | { 7 | public interface IWorkerContext 8 | { 9 | public IFlowContext FlowContext { get; init; } 10 | 11 | public IFlowRunContext FlowRunContext { get; init; } 12 | 13 | public IDeploymentContext DeploymentContext { get; init; } 14 | 15 | public IEnvironmentContext EnvironmentContext { get; init; } 16 | 17 | public IFlow? FlowInstance { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/didact-cli/Properties/launchsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "DidactCli Developement": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "DOTNET_ENVIRONMENT": "Development" 7 | } 8 | }, 9 | "DidactCli Staging": { 10 | "commandName": "Project", 11 | "environmentVariables": { 12 | "DOTNET_ENVIRONMENT": "Staging" 13 | } 14 | }, 15 | "DidactCli Production": { 16 | "commandName": "Project", 17 | "environmentVariables": { 18 | "DOTNET_ENVIRONMENT": "Production" 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/didact-primitives/Constants/FlowLibrarySourceTypes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class FlowLibrarySourceTypes 4 | { 5 | public const string LocalFilesystem = "Local Filesystem"; 6 | public const string NetworkLocation = "Network Location"; 7 | public const string AzureBlobStorage = "Azure BLOB Storage"; 8 | public const string AwsS3Storage = "AWS S3 Storage"; 9 | public const string GitHubRepository = "GitHub Repository"; 10 | public const string AzureDevopsRepository = "Azure Devops Repository"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/didact-core/DidactCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | enable 6 | CS8600;CS8602;CS8603 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/FlowConfiguratorStates.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class FlowConfiguratorStates 4 | { 5 | public const string FlowConfigurationUninitialized = "Flow Configuration Uninitialized"; 6 | public const string FlowInstantiationSuccessful = "Flow Instantiation Successful"; 7 | public const string FlowInstantiationFailed = "Flow Instantiation Failed"; 8 | public const string FlowConfigurationSuccessful = "Flow Configuration Successful"; 9 | public const string FlowConfigurationFailed = "Flow Configuration Failed"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/didact-engine/Modules/IModule.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Modules 2 | { 3 | public interface IModule 4 | { 5 | string Name { get; } 6 | 7 | bool Enabled { get; } 8 | 9 | /// 10 | /// The number of concurrent module s that can be created in the module supervisor. 11 | /// 12 | int Concurrency { get; } 13 | 14 | /// 15 | /// The delay from one module loop iteration to the next. The unit is milliseconds. 16 | /// 17 | int IntervalDelay { get; } 18 | 19 | Task ExecuteAsync(CancellationToken ct); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "@primeuix/themes": "^1.0.0", 14 | "@tailwindcss/vite": "^4.0.14", 15 | "axios": "^1.8.4", 16 | "nuxt": "^3.16.1", 17 | "primevue": "^4.3.2", 18 | "tailwindcss": "^4.0.14", 19 | "vue": "^3.5.13", 20 | "vue-router": "^4.5.0" 21 | }, 22 | "devDependencies": { 23 | "@primevue/nuxt-module": "^4.3.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlow.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace DidactCore.Flows 4 | { 5 | public interface IFlow 6 | { 7 | /// 8 | /// Asynchronously configures the Flow metadata. 9 | /// 10 | /// 11 | /// 12 | Task ConfigureAsync(IFlowConfigurationContext context); 13 | 14 | /// 15 | /// Asynchronously executes the Flow. 16 | /// 17 | /// 18 | /// 19 | Task ExecuteAsync(IFlowExecutionContext context); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/ScheduleType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class ScheduleType 6 | { 7 | public int ScheduleTypeId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/TriggerScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class TriggerScope 6 | { 7 | public int TriggerScopeId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/DeploymentType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class DeploymentType 6 | { 7 | public int DeploymentTypeId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/QueueDirection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class QueueDirection 6 | { 7 | public int QueueDirectionId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-engine/Modules/ModuleContext.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Modules 2 | { 3 | public sealed class ModuleContext 4 | { 5 | public IModule Module { get; } 6 | 7 | public CancellationToken CancellationToken { get; } 8 | 9 | public Guid Id { get; } = Guid.NewGuid(); 10 | 11 | public int WorkerIndex { get; } 12 | 13 | public string Name => $"{Module.Name} module (index: {WorkerIndex} | id: {Id})"; 14 | 15 | public ModuleContext(IModule module, int workerIndex, CancellationToken cancellationToken) 16 | { 17 | Module = module; 18 | WorkerIndex = workerIndex; 19 | CancellationToken = cancellationToken; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/DeploymentStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class DeploymentStatus 6 | { 7 | public int DeploymentStatusId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowRunEventType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class FlowRunEventType 6 | { 7 | public int FlowRunEventTypeId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/CronScheduleTrigger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class CronScheduleTrigger 6 | { 7 | public long CronScheduleTriggerId { get; set; } 8 | 9 | public long TriggerId { get; set; } 10 | 11 | public string CronExpression { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/DeploymentSourceType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class DeploymentSourceType 6 | { 7 | public int DeploymentSourceTypeId { get; set; } 8 | 9 | public string Name { get; set; } = null!; 10 | 11 | public string Description { get; set; } = null!; 12 | 13 | public DateTime Created { get; set; } 14 | 15 | public string CreatedBy { get; set; } = null!; 16 | 17 | public DateTime Updated { get; set; } 18 | 19 | public string UpdatedBy { get; set; } = null!; 20 | 21 | public bool Active { get; set; } 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/FlowTypeNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace DidactCore.Flows 5 | { 6 | [Serializable] 7 | public class FlowTypeNotFoundException : Exception 8 | { 9 | public FlowTypeNotFoundException() 10 | { 11 | } 12 | 13 | public FlowTypeNotFoundException(string message) : base(message) 14 | { 15 | } 16 | 17 | public FlowTypeNotFoundException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | } 20 | 21 | protected FlowTypeNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/NoMatchedPluginException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | [Serializable] 7 | public class NoMatchedPluginException : Exception 8 | { 9 | public NoMatchedPluginException() 10 | { 11 | } 12 | 13 | public NoMatchedPluginException(string message) : base(message) 14 | { 15 | } 16 | 17 | public NoMatchedPluginException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | } 20 | 21 | protected NoMatchedPluginException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-engine/Constants/Defaults.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Constants 2 | { 3 | public static class Defaults 4 | { 5 | public const decimal DefaultThreadFactor = 1; 6 | 7 | public static class DefaultModuleIntervalDelays 8 | { 9 | public const int Plugins = 120000; 10 | public const int Scheduler = 5000; 11 | public const int Workers = 0; 12 | public const int Licensing = 1800000; 13 | public const int EngineLogger = 0; 14 | public const int FlowRunLogger = 0; 15 | } 16 | 17 | public const int DefaultWorkersServiceDequeueIntervalDelay = 5000; 18 | public const int FlowRunCancellationPollingInterval = 60000; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/didact-services/DidactServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | net8.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Environment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class Environment 6 | { 7 | public long EnvironmentId { get; set; } 8 | 9 | public int OrganizationId { get; set; } 10 | 11 | public string Name { get; set; } = null!; 12 | 13 | public string? Description { get; set; } 14 | 15 | public DateTime Created { get; set; } 16 | 17 | public string CreatedBy { get; set; } = null!; 18 | 19 | public DateTime Updated { get; set; } 20 | 21 | public string UpdatedBy { get; set; } = null!; 22 | 23 | public bool Active { get; set; } 24 | 25 | public byte[] RowVersion { get; set; } = null!; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/didact-cli/Services/TypeResolver.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | 3 | namespace DidactCli.Services; 4 | 5 | public sealed class TypeResolver : ITypeResolver, IDisposable 6 | { 7 | private readonly IServiceProvider _provider; 8 | 9 | public TypeResolver(IServiceProvider provider) 10 | { 11 | _provider = provider ?? throw new ArgumentNullException(nameof(provider)); 12 | } 13 | 14 | public object Resolve(Type type) 15 | { 16 | if (type == null) 17 | { 18 | return null; 19 | } 20 | 21 | return _provider.GetService(type); 22 | } 23 | 24 | public void Dispose() 25 | { 26 | if (_provider is IDisposable disposable) 27 | { 28 | disposable.Dispose(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/didact-engine/Deployments/DeploymentsService.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | 3 | namespace DidactEngine.Deployments 4 | { 5 | public class DeploymentsService 6 | { 7 | private readonly ILogger _logger; 8 | 9 | public DeploymentsService(ILogger logger) 10 | { 11 | _logger = logger; 12 | } 13 | 14 | public async Task FetchMissingDeploymentsAsync(CancellationToken cancellationToken) 15 | { 16 | await Task.CompletedTask; 17 | } 18 | 19 | public async Task FetchDeploymentFromSourceAsync(IDeploymentContext deploymentContext, CancellationToken cancellationToken) 20 | { 21 | await Task.CompletedTask; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/SaveFlowConfigurationsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace DidactCore.Exceptions 5 | { 6 | [Serializable] 7 | public class SaveFlowConfigurationsException : Exception 8 | { 9 | public SaveFlowConfigurationsException() 10 | { 11 | } 12 | 13 | public SaveFlowConfigurationsException(string message) : base(message) 14 | { 15 | } 16 | 17 | public SaveFlowConfigurationsException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | } 20 | 21 | protected SaveFlowConfigurationsException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/MultipleMatchedPluginsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | [Serializable] 7 | public class MultipleMatchedPluginsException : Exception 8 | { 9 | public MultipleMatchedPluginsException() 10 | { 11 | } 12 | 13 | public MultipleMatchedPluginsException(string message) : base(message) 14 | { 15 | } 16 | 17 | public MultipleMatchedPluginsException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | } 20 | 21 | protected MultipleMatchedPluginsException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Controllers/EnvironmentVariablesController.cs: -------------------------------------------------------------------------------- 1 | using DidactUi.Services; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace DidactUi.Controllers 5 | { 6 | public class EnvironmentVariablesController : ControllerBase 7 | { 8 | private readonly ILogger _logger; 9 | private readonly UiSettings _uiSettings; 10 | 11 | public EnvironmentVariablesController(ILogger logger, UiSettings uiSettings) 12 | { 13 | _logger = logger; 14 | _uiSettings = uiSettings; 15 | } 16 | 17 | [HttpGet("environment-variables")] 18 | public IActionResult GetEnvironmentVariables() 19 | { 20 | return Ok(_uiSettings); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/DeploymentSourceFilesystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class DeploymentSourceFilesystem 6 | { 7 | public long DeploymentSourceFilesystemId { get; set; } 8 | 9 | public long DeploymentId { get; set; } 10 | 11 | public long EnvironmentId { get; set; } 12 | 13 | public string FolderPath { get; set; } = null!; 14 | 15 | public DateTime Created { get; set; } 16 | 17 | public string CreatedBy { get; set; } = null!; 18 | 19 | public DateTime Updated { get; set; } 20 | 21 | public string UpdatedBy { get; set; } = null!; 22 | 23 | public bool Active { get; set; } 24 | 25 | public byte[] RowVersion { get; set; } = null!; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/TriggerType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class TriggerType 7 | { 8 | public int TriggerTypeId { get; set; } 9 | 10 | public string Name { get; set; } = null!; 11 | 12 | public string Description { get; set; } = null!; 13 | 14 | public DateTime Created { get; set; } 15 | 16 | public string CreatedBy { get; set; } = null!; 17 | 18 | public DateTime Updated { get; set; } 19 | 20 | public string UpdatedBy { get; set; } = null!; 21 | 22 | public bool Active { get; set; } 23 | 24 | public byte[] RowVersion { get; set; } = null!; 25 | 26 | public virtual ICollection FlowRuns { get; } = new List(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/IEngineSupervisor.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Plugins; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace DidactCore.Engine 7 | { 8 | public interface IEngineSupervisor 9 | { 10 | long EngineId { get; set; } 11 | 12 | Guid EngineUniversalId { get; set; } 13 | 14 | EngineTuningDto EngineTuning { get; set; } 15 | 16 | string EngineState { get; set; } 17 | 18 | CancellationToken CancellationToken { get; set; } 19 | 20 | DateTime EngineStateUpdated { get; set; } 21 | 22 | IPluginContainers PluginContainers { get; set; } 23 | 24 | void SetEngineState(string engineState); 25 | 26 | Task CheckForEngineShutdownEventAsync(); 27 | 28 | string GetEngineState(); 29 | 30 | DateTime GetEngineStateUpdated(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/State.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class State 7 | { 8 | public int StateId { get; set; } 9 | 10 | public string Name { get; set; } = null!; 11 | 12 | public string? Description { get; set; } 13 | 14 | public DateTime Created { get; set; } 15 | 16 | public string CreatedBy { get; set; } = null!; 17 | 18 | public DateTime Updated { get; set; } 19 | 20 | public string UpdatedBy { get; set; } = null!; 21 | 22 | public bool Active { get; set; } 23 | 24 | public byte[] RowVersion { get; set; } = null!; 25 | 26 | public virtual ICollection FlowRuns { get; } = new List(); 27 | 28 | public virtual ICollection BlockRuns { get; } = new List(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/didact-engine/Scheduler/SchedulerModule.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | using DidactEngine.Modules; 3 | 4 | namespace DidactEngine.Scheduler 5 | { 6 | public class SchedulerModule : IModule 7 | { 8 | private readonly SchedulerService _schedulerService; 9 | 10 | public string Name => EngineConstants.ModuleNames.Scheduler; 11 | 12 | public bool Enabled { get; set; } = true; 13 | 14 | public int Concurrency { get; set; } = 1; 15 | 16 | public int IntervalDelay { get; set; } = Defaults.DefaultModuleIntervalDelays.Scheduler; 17 | 18 | public SchedulerModule(SchedulerService schedulerService) 19 | { 20 | _schedulerService = schedulerService; 21 | } 22 | 23 | public async Task ExecuteAsync(CancellationToken cancellationToken) 24 | { 25 | await _schedulerService.ScheduleAsync(cancellationToken); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/ExecutionMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class ExecutionMode 7 | { 8 | public int ExecutionModeId { get; set; } 9 | 10 | public string Name { get; set; } = null!; 11 | 12 | public string? Description { get; set; } 13 | 14 | public DateTime Created { get; set; } 15 | 16 | public string CreatedBy { get; set; } = null!; 17 | 18 | public DateTime Updated { get; set; } 19 | 20 | public string UpdatedBy { get; set; } = null!; 21 | 22 | public bool Active { get; set; } 23 | 24 | public byte[] RowVersion { get; set; } = null!; 25 | 26 | public virtual ICollection Flows { get; } = new List(); 27 | 28 | public virtual ICollection FlowRuns { get; } = new List(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowRunStateChangeEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class FlowRunStateChangeEvent 6 | { 7 | public long FlowRunStateChangeEventId { get; set; } 8 | 9 | public long FlowRunEventId { get; set; } 10 | 11 | public long EnvironmentId { get; set; } 12 | 13 | public string PreviousState { get; set; } = null!; 14 | 15 | public string NewState { get; set; } = null!; 16 | 17 | public DateTime Timestamp { get; set; } 18 | 19 | public DateTime Created { get; set; } 20 | 21 | public string CreatedBy { get; set; } = null!; 22 | 23 | public DateTime Updated { get; set; } 24 | 25 | public string UpdatedBy { get; set; } = null!; 26 | 27 | public bool Active { get; set; } 28 | 29 | public byte[] RowVersion { get; set; } = null!; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/didact-engine/Workers/WorkersModule.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | using DidactEngine.Modules; 3 | 4 | namespace DidactEngine.Workers 5 | { 6 | public class WorkersModule : IModule 7 | { 8 | private readonly WorkersService _workersService; 9 | 10 | public string Name => EngineConstants.ModuleNames.Workers; 11 | 12 | public bool Enabled { get; set; } = true; 13 | 14 | public int Concurrency { get; set; } = Environment.ProcessorCount; 15 | 16 | public int IntervalDelay { get; set; } = Defaults.DefaultModuleIntervalDelays.Workers; 17 | 18 | public WorkersModule(WorkersService workersService) 19 | { 20 | _workersService = workersService; 21 | } 22 | 23 | public async Task ExecuteAsync(CancellationToken cancellationToken) 24 | { 25 | await _workersService.WorkAsyncOnThreadpool(cancellationToken); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/HyperQueueItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class HyperQueueItem 6 | { 7 | public long HyperQueueItemId { get; set; } 8 | 9 | public long EnvironmentId { get; set; } 10 | 11 | public int HyperQueueId { get; set; } 12 | 13 | public long FlowRunId { get; set; } 14 | 15 | public DateTime Created { get; set; } 16 | 17 | public string CreatedBy { get; set; } = null!; 18 | 19 | public DateTime Updated { get; set; } 20 | 21 | public string UpdatedBy { get; set; } = null!; 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | 25 | public virtual Environment Environment { get; set; } = null!; 26 | 27 | public virtual HyperQueue HyperQueue { get; set; } = null!; 28 | 29 | public virtual FlowRun FlowRun { get; set; } = null!; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/StrictQueueItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class StrictQueueItem 6 | { 7 | public long StrictQueueItemId { get; set; } 8 | 9 | public long EnvironmentId { get; set; } 10 | 11 | public int StrictQueueId { get; set; } 12 | 13 | public long FlowRunId { get; set; } 14 | 15 | public DateTime Created { get; set; } 16 | 17 | public string CreatedBy { get; set; } = null!; 18 | 19 | public DateTime Updated { get; set; } 20 | 21 | public string UpdatedBy { get; set; } = null!; 22 | 23 | public byte[] RowVersion { get; set; } = null!; 24 | 25 | public virtual StrictQueue StrictQueue { get; set; } = null!; 26 | 27 | public virtual Environment Environment { get; set; } = null!; 28 | 29 | public virtual FlowRun FlowRun { get; set; } = null!; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/didact-services/Flows/IFlowRunRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace DidactServices.Flows 4 | { 5 | public interface IFlowRunRepository 6 | { 7 | Task GetFlowRunAsync(long flowRunId); 8 | 9 | Task GetFlowRunByNameAsync(string name); 10 | 11 | Task GetFlowRunByDescriptionAsync(string description); 12 | 13 | Task CreateAndEnqueueFlowRunAsync(FlowRun flowRun); 14 | 15 | Task CreateAndExecuteFlowRunAsync(FlowRun flowRun); 16 | 17 | Task UpdateFlowRunAsync(long flowRunId, FlowRun flowRun); 18 | 19 | Task DeleteFlowRunAsync(long flowRunId); 20 | 21 | Task CheckIfFlowRunIsCancelledAsync(long flowRunId); 22 | 23 | Task CancelFlowRunAsync(long flowRunId); 24 | 25 | Task TimeoutFlowRunAsync(long flowRunId); 26 | 27 | Task FailFlowRunAsync(long flowRunId); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/didact-core/SomeFlow.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Flows; 2 | using DidactCore.Triggers; 3 | using System.Threading.Tasks; 4 | 5 | namespace DidactCore 6 | { 7 | public class SomeFlow : IFlow 8 | { 9 | public Task ConfigureAsync(IFlowConfigurationContext context) 10 | { 11 | context.Configurator 12 | .WithName("An example flow") 13 | .WithDescription("A sample flow") 14 | .AsVersion("1.0.0") 15 | .WithCronScheduleTrigger(new CronScheduleTrigger("test")); 16 | 17 | return Task.FromResult(context); 18 | } 19 | 20 | public async Task ExecuteAsync(IFlowExecutionContext context) 21 | { 22 | var logger = context.Logger; 23 | logger.LogInformation("Starting work..."); 24 | await Task.Delay(100); 25 | logger.LogInformation("Work completed."); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Trigger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class Trigger 6 | { 7 | public long TriggerId { get; set; } 8 | 9 | public int TriggerTypeId { get; set; } 10 | 11 | public int TriggerScopeId { get; set; } 12 | 13 | public int? OrganizationId { get; set; } 14 | 15 | public long? EnvironmentId { get; set; } 16 | 17 | public long? FlowId { get; set; } 18 | 19 | public string? Name { get; set; } 20 | 21 | public string? Description { get; set; } 22 | 23 | public DateTime Created { get; set; } 24 | 25 | public string CreatedBy { get; set; } = null!; 26 | 27 | public DateTime Updated { get; set; } 28 | 29 | public string UpdatedBy { get; set; } = null!; 30 | 31 | public bool Active { get; set; } 32 | 33 | public byte[] RowVersion { get; set; } = null!; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/didact-engine/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:24358", 8 | "sslPort": 44329 9 | } 10 | }, 11 | "profiles": { 12 | "DidactEngine": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7098;http://localhost:5095", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/didact-cli/Commands/UiInstallCommand.cs: -------------------------------------------------------------------------------- 1 | using DidactCli.Services; 2 | using DidactCli.Settings; 3 | using Microsoft.Extensions.Logging; 4 | using Spectre.Console; 5 | using Spectre.Console.Cli; 6 | 7 | namespace DidactCli.Commands 8 | { 9 | public class UiInstallCommand : Command 10 | { 11 | private readonly ILogger _logger; 12 | private readonly AppSettings _appSettings; 13 | 14 | public UiInstallCommand(ILogger logger, AppSettings appSettings) 15 | { 16 | _logger = logger; 17 | _appSettings = appSettings; 18 | } 19 | 20 | public override int Execute(CommandContext context, UiInstallCommandSettings settings) 21 | { 22 | _logger.LogInformation("Reading path..."); 23 | _logger.LogInformation(_appSettings.TestSetting); 24 | AnsiConsole.WriteLine(settings.InstallationPath); 25 | return 0; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowRunEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class FlowRunEvent 6 | { 7 | public long FlowRunEventId { get; set; } 8 | 9 | public long FlowRunId { get; set; } 10 | 11 | public int FlowRunEventTypeId { get; set; } 12 | 13 | public long EnvironmentId { get; set; } 14 | 15 | public DateTime Timestamp { get; set; } 16 | 17 | public DateTime Created { get; set; } 18 | 19 | public string CreatedBy { get; set; } = null!; 20 | 21 | public DateTime Updated { get; set; } 22 | 23 | public string UpdatedBy { get; set; } = null!; 24 | 25 | public byte[] RowVersion { get; set; } = null!; 26 | 27 | public virtual FlowRun FlowRun { get; set; } = null!; 28 | 29 | public virtual FlowRunEventType FlowRunEventType { get; set; } = null!; 30 | 31 | public virtual Environment Environment { get; set; } = null!; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowRunLogEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class FlowRunLogEvent 6 | { 7 | public long FlowRunLogEventId { get; set; } 8 | 9 | public long FlowRunEventId { get; set; } 10 | 11 | public long EnvironmentId { get; set; } 12 | 13 | public string LogLevel { get; set; } = null!; 14 | 15 | public string Message { get; set; } = null!; 16 | 17 | public DateTime Timestamp { get; set; } 18 | 19 | public DateTime Created { get; set; } 20 | 21 | public string CreatedBy { get; set; } = null!; 22 | 23 | public DateTime Updated { get; set; } 24 | 25 | public string UpdatedBy { get; set; } = null!; 26 | 27 | public byte[] RowVersion { get; set; } = null!; 28 | 29 | public virtual FlowRunEvent FlowRunEvent { get; set; } = null!; 30 | 31 | public virtual Environment Environment { get; set; } = null!; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/didact-cli/Commands/EngineInstallCommand.cs: -------------------------------------------------------------------------------- 1 | using DidactCli.Services; 2 | using DidactCli.Settings; 3 | using Microsoft.Extensions.Logging; 4 | using Spectre.Console; 5 | using Spectre.Console.Cli; 6 | 7 | namespace DidactCli.Commands 8 | { 9 | public class EngineInstallCommand : Command 10 | { 11 | private readonly ILogger _logger; 12 | private readonly AppSettings _appSettings; 13 | 14 | public EngineInstallCommand(ILogger logger, AppSettings appSettings) 15 | { 16 | _logger = logger; 17 | _appSettings = appSettings; 18 | } 19 | 20 | public override int Execute(CommandContext context, EngineInstallCommandSettings settings) 21 | { 22 | _logger.LogInformation("Reading path..."); 23 | _logger.LogInformation(_appSettings.TestSetting); 24 | AnsiConsole.WriteLine(settings.InstallationPath); 25 | return 0; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/StateConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class StateConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(State)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class FlowVersion 7 | { 8 | public long FlowVersionId { get; set; } 9 | 10 | public long FlowId { get; set; } 11 | 12 | public long EnvironmentId { get; set; } 13 | 14 | public string Version { get; set; } = null!; 15 | 16 | public DateTime Created { get; set; } 17 | 18 | public string CreatedBy { get; set; } = null!; 19 | 20 | public DateTime Updated { get; set; } 21 | 22 | public string UpdatedBy { get; set; } = null!; 23 | 24 | public bool Active { get; set; } 25 | 26 | public byte[] RowVersion { get; set; } = null!; 27 | 28 | public virtual Flow Flow { get; set; } = null!; 29 | 30 | public virtual Environment Environment { get; set; } = null!; 31 | 32 | public virtual ICollection FlowRuns { get; } = new List(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/StrictQueueConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class StrictQueueConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | entity.ToTable(nameof(StrictQueue)); 12 | entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | entity.Property(e => e.UpdatedBy).HasMaxLength(255); 15 | entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Deployment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class Deployment 6 | { 7 | public long DeploymentId { get; set; } 8 | 9 | public int DeploymentTypeId { get; set; } 10 | 11 | public int DeploymentSourceTypeId { get; set; } 12 | 13 | public int DeploymentStatusId { get; set; } 14 | 15 | public long EnvironmentId { get; set; } 16 | 17 | public string? Name { get; set; } 18 | 19 | public string? Description { get; set; } 20 | 21 | public string Entrypoint { get; set; } = null!; 22 | 23 | public Guid PushId { get; set; } 24 | 25 | public DateTime Created { get; set; } 26 | 27 | public string CreatedBy { get; set; } = null!; 28 | 29 | public DateTime Updated { get; set; } 30 | 31 | public string UpdatedBy { get; set; } = null!; 32 | 33 | public bool Active { get; set; } 34 | 35 | public byte[] RowVersion { get; set; } = null!; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Engine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class Engine 6 | { 7 | public long EngineId { get; set; } 8 | 9 | public long EnvironmentId { get; set; } 10 | 11 | public Guid UniversalId { get; set; } 12 | 13 | public string UniqueName { get; set; } = null!; 14 | 15 | public string? Name { get; set; } 16 | 17 | public int? LatestProcessId { get; set; } 18 | 19 | public DateTime LastHeartbeat { get; set; } 20 | 21 | public string? Description { get; set; } 22 | 23 | public DateTime Created { get; set; } 24 | 25 | public string CreatedBy { get; set; } = null!; 26 | 27 | public DateTime Updated { get; set; } 28 | 29 | public string UpdatedBy { get; set; } = null!; 30 | 31 | public bool Active { get; set; } 32 | 33 | public byte[] RowVersion { get; set; } = null!; 34 | 35 | public virtual Environment Environment { get; set; } = null!; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/HyperQueueConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class HyperQueueConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(HyperQueue)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/TriggerTypeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class TriggerTypeConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(TriggerType)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineRepository.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System.Threading.Tasks; 3 | 4 | namespace DidactCore.Engine 5 | { 6 | public class EngineRepository : IEngineRepository 7 | { 8 | private readonly ILogger _logger; 9 | 10 | public EngineRepository(ILogger logger) 11 | { 12 | _logger = logger; 13 | } 14 | 15 | public async Task CheckForEngineShutdownAsync() 16 | { 17 | // TODO Implement 18 | await Task.CompletedTask; 19 | return false; 20 | } 21 | 22 | public async Task GetEngineAsync() 23 | { 24 | // TODO Implement 25 | await Task.CompletedTask; 26 | return new EngineDto(); 27 | } 28 | 29 | public async Task GetEngineTuningAsync() 30 | { 31 | // TODO Implement 32 | await Task.CompletedTask; 33 | return new EngineTuningDto(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/OrganizationConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class OrganizationConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(Organization)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/ScheduleTypeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class ScheduleTypeConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(ScheduleType)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Migrations/MigrationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace DidactServices.DataModel.Migrations 4 | { 5 | public static class MigrationExtensions 6 | { 7 | public static WebApplication MigrateDatabase(this WebApplication webApp) 8 | where T : DbContext 9 | { 10 | using (var scope = webApp.Services.CreateScope()) 11 | using (var dbContext = scope.ServiceProvider.GetRequiredService()) 12 | try 13 | { 14 | dbContext.Database.Migrate(); 15 | } 16 | catch (Exception e) 17 | { 18 | scope.ServiceProvider 19 | .GetRequiredService>() 20 | .LogError(e, "Unhandled exception while applying migrations for {T}", typeof(T)); 21 | 22 | Console.WriteLine(e); 23 | 24 | throw; 25 | } 26 | 27 | return webApp; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/utils/environment-variables/index.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { isDev } from "../environment"; 3 | 4 | const devBaseUrl: string = import.meta.env.VITE_ENVIRONMENT_VARIABLES_DEV_BASE_URL; 5 | 6 | interface EnvironmentVariables { 7 | didactEngineBaseUrl: string 8 | } 9 | 10 | /** 11 | * Determines the appropriate base URL for the runtime environment variables depending on how the single page app is currently running. 12 | * @returns 13 | */ 14 | const getBaseUrlForEnvironmentVariables = () => { 15 | return isDev() ? devBaseUrl : ''; 16 | } 17 | 18 | /** 19 | * Gets the dynamic set of runtime environment variables from the containing dotnet web api that serves this single page app. 20 | * @returns {EnvironmentVariables} 21 | */ 22 | const getEnvironmentVariables = async () : Promise => { 23 | const baseUrl = getBaseUrlForEnvironmentVariables(); 24 | const response = await axios.get(`${baseUrl}/environment-variables`); 25 | return response.data; 26 | } 27 | 28 | export { getEnvironmentVariables } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/FlowScheduleConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class FlowScheduleConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(FlowSchedule)); 12 | //entity.Property(e => e.CronExpression).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 16 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 17 | 18 | OnConfigurePartial(entity); 19 | } 20 | 21 | partial void OnConfigurePartial(EntityTypeBuilder entity); 22 | } 23 | } -------------------------------------------------------------------------------- /src/didact-services/Workers/WorkerContext.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | using DidactCore.Environments; 3 | using DidactCore.Flows; 4 | 5 | namespace DidactServices.Workers 6 | { 7 | public class WorkerContext : IWorkerContext 8 | { 9 | public IFlowContext FlowContext { get; init; } 10 | 11 | public IFlowRunContext FlowRunContext { get; init; } 12 | 13 | public IDeploymentContext DeploymentContext { get; init; } 14 | 15 | public IEnvironmentContext EnvironmentContext { get; init; } 16 | 17 | public IFlow? FlowInstance { get; set; } 18 | 19 | public WorkerContext(IFlowContext flowContext, IFlowRunContext flowRunContext, 20 | IDeploymentContext deploymentContext, IEnvironmentContext environmentContext, IFlow? flowInstance = null) 21 | { 22 | FlowContext = flowContext; 23 | FlowRunContext = flowRunContext; 24 | DeploymentContext = deploymentContext; 25 | EnvironmentContext = environmentContext; 26 | FlowInstance = flowInstance; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Didact 2 | The .NET job orchestrator that we've been missing. 3 | 4 | ## About 5 | 6 | This is the monorepo for Didact and it's primary components, namely: 7 | - Didact CLI 8 | - Didact Core 9 | - Didact Engine 10 | - Didact UI 11 | 12 | Originally, I had anticipated a polyrepo approach, but I have since decided against it. The older component repositories are archived, so please refer to this repository for the main platform components. I do still have some separate repositories such as the dedicated repository for the [Didact docsite](https://docs.didact.dev), please refer to the other repositories as necessary. 13 | 14 | ## Releases and Artifacts 15 | 16 | This repository will contain the platform releases and release artifacts for Didact, namely, the application binaries/executables. The CI/CD automations will also produce the associated NuGet packages and Docker images from here. 17 | 18 | ## Documentation 19 | 20 | Rather than create complex README files, I prefer to keep these shorter for now and instead refer you to the [Didact docsite](https://docs.didact.dev) for learning the platform. -------------------------------------------------------------------------------- /src/didact-cli/Services/TypeRegistrar.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Spectre.Console.Cli; 3 | 4 | namespace DidactCli.Services; 5 | 6 | public sealed class TypeRegistrar : ITypeRegistrar 7 | { 8 | private readonly IServiceCollection _builder; 9 | 10 | public TypeRegistrar(IServiceCollection builder) 11 | { 12 | _builder = builder; 13 | } 14 | 15 | public ITypeResolver Build() 16 | { 17 | return new TypeResolver(_builder.BuildServiceProvider()); 18 | } 19 | 20 | public void Register(Type service, Type implementation) 21 | { 22 | _builder.AddSingleton(service, implementation); 23 | } 24 | 25 | public void RegisterInstance(Type service, object implementation) 26 | { 27 | _builder.AddSingleton(service, implementation); 28 | } 29 | 30 | public void RegisterLazy(Type service, Func func) 31 | { 32 | if (func is null) 33 | { 34 | throw new ArgumentNullException(nameof(func)); 35 | } 36 | 37 | _builder.AddSingleton(service, (provider) => func()); 38 | } 39 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/HyperQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class HyperQueue 7 | { 8 | public int HyperQueueId { get; set; } 9 | 10 | public int QueueDirectionId { get; set; } 11 | 12 | public long EnvironmentId { get; set; } 13 | 14 | public string Name { get; set; } = null!; 15 | 16 | public string? Description { get; set; } 17 | 18 | public DateTime Created { get; set; } 19 | 20 | public string CreatedBy { get; set; } = null!; 21 | 22 | public DateTime Updated { get; set; } 23 | 24 | public string UpdatedBy { get; set; } = null!; 25 | 26 | public bool Active { get; set; } 27 | 28 | public byte[] RowVersion { get; set; } = null!; 29 | 30 | public virtual QueueDirection QueueDirection { get; set; } = null!; 31 | 32 | public virtual Environment Environment { get; set; } = null!; 33 | 34 | public virtual ICollection HyperQueueItems { get; } = new List(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/StrictQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class StrictQueue 7 | { 8 | public int StrictQueueId { get; set; } 9 | 10 | public int QueueDirectionId { get; set; } 11 | 12 | public long EnvironmentId { get; set; } 13 | 14 | public string Name { get; set; } = null!; 15 | 16 | public string? Description { get; set; } 17 | 18 | public DateTime Created { get; set; } 19 | 20 | public string CreatedBy { get; set; } = null!; 21 | 22 | public DateTime Updated { get; set; } 23 | 24 | public string UpdatedBy { get; set; } = null!; 25 | 26 | public bool Active { get; set; } 27 | 28 | public byte[] RowVersion { get; set; } = null!; 29 | 30 | public virtual QueueDirection QueueDirection { get; set; } = null!; 31 | 32 | public virtual Environment Environment { get; set; } = null!; 33 | 34 | public virtual ICollection StrictQueueItems { get; } = new List(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/didact-primitives/Constants/QueueTypes.cs: -------------------------------------------------------------------------------- 1 | namespace DidactPrimitives.Constants 2 | { 3 | public static class QueueTypes 4 | { 5 | /// 6 | /// 7 | /// A queue type designated for maximum throughput by running jobs concurrently or in parallel. 8 | /// 9 | /// 10 | /// A hyper queue will attempt best-effort ordering of Flows, but ordering is not strictly guaranteed. This is to prioritize throughput. 11 | /// 12 | /// 13 | /// A hyper queue is an excellent candidate for a clustered/distributed environment. 14 | /// 15 | /// 16 | public const string HyperQueue = "Hyper Queue"; 17 | 18 | /// 19 | /// 20 | /// A queue type designated for strict, guaranteed ordering. 21 | /// 22 | /// 23 | /// Because ordering is strictly enforced, a strict queue will necessarily sacrifice throughput. 24 | /// 25 | /// 26 | public const string StrictQueue = "Strict Queue"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginsModule.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | using DidactEngine.Modules; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | public class PluginsModule : IModule 7 | { 8 | public string Name => EngineConstants.ModuleNames.Plugins; 9 | 10 | public bool Enabled { get; set; } = true; 11 | 12 | public int Concurrency { get; set; } = 1; 13 | 14 | public int IntervalDelay { get; set; } = Defaults.DefaultModuleIntervalDelays.Plugins; 15 | 16 | public PluginsModule() { } 17 | 18 | public async Task ExecuteAsync(CancellationToken ct) 19 | { 20 | // TODO 21 | 22 | /* Implementation 23 | * Step 1: Poll database for missing deployments. 24 | * Step 2: Fetch each deployment. 25 | * Step 3: Shadow copy each deployment. 26 | * Step 4: Load each deployment in a new ALC. 27 | * Step 5: Implement plugin-isolated dependency injection. 28 | * Step 6: Run ConfigureAsync against all flows. 29 | */ 30 | 31 | await Task.CompletedTask; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/EngineLoggerModule.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | using DidactEngine.Modules; 3 | 4 | namespace DidactEngine.Logging 5 | { 6 | public class EngineLoggerModule : IModule 7 | { 8 | private readonly EngineLogChannel _engineChannel; 9 | private readonly IEngineLogRepository _repository; 10 | 11 | public string Name => EngineConstants.ModuleNames.EngineLogger; 12 | 13 | public bool Enabled { get; set; } = true; 14 | 15 | public int Concurrency { get; set; } = 1; 16 | 17 | public int IntervalDelay { get; set; } = Defaults.DefaultModuleIntervalDelays.EngineLogger; 18 | 19 | public EngineLoggerModule(EngineLogChannel channel, IEngineLogRepository repository) 20 | { 21 | _engineChannel = channel; 22 | _repository = repository; 23 | } 24 | 25 | public async Task ExecuteAsync(CancellationToken token) 26 | { 27 | await foreach (var log in _engineChannel.Channel.Reader.ReadAllAsync(token)) 28 | { 29 | await _repository.InsertLogAsync(log, token); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowSchedule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class FlowSchedule 6 | { 7 | public long FlowScheduleId { get; set; } 8 | 9 | public long EnvironmentId { get; set; } 10 | 11 | public long FlowId { get; set; } 12 | 13 | public int ScheduleTypeId { get; set; } 14 | 15 | public string CronExpression { get; set; } = null!; 16 | 17 | public DateTime? LastRunTime { get; set; } 18 | 19 | public DateTime? NextRunTime { get; set; } 20 | 21 | public DateTime Created { get; set; } 22 | 23 | public string CreatedBy { get; set; } = null!; 24 | 25 | public DateTime Updated { get; set; } 26 | 27 | public string UpdatedBy { get; set; } = null!; 28 | 29 | public bool Active { get; set; } 30 | 31 | public byte[] RowVersion { get; set; } = null!; 32 | 33 | public virtual Environment Environment { get; set; } = null!; 34 | 35 | public virtual ScheduleType ScheduleType { get; set; } = null!; 36 | 37 | public virtual Flow Flow { get; set; } = null!; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Minimal Starter 2 | 3 | Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | 3 | import tailwindcss from "@tailwindcss/vite"; 4 | import Aura from '@primeuix/themes/aura'; 5 | 6 | export default defineNuxtConfig({ 7 | compatibilityDate: '2024-11-01', 8 | devtools: { enabled: true }, 9 | vite: { 10 | plugins: [ 11 | tailwindcss(), 12 | ], 13 | }, 14 | css: ['~/assets/css/main.css'], 15 | modules: [ 16 | '@primevue/nuxt-module' 17 | ], 18 | primevue: { 19 | options: { 20 | theme: { 21 | preset: Aura 22 | } 23 | }, 24 | components: { 25 | include: '*', 26 | // Need to temporarily exclude PrimeVue form components because of npm install bug. 27 | // See https://github.com/primefaces/primevue/issues/7434. 28 | // Also need to exclude Editor and Chart because of weird build issues. 29 | // See https://github.com/primefaces/primevue-nuxt-module/issues/16#issuecomment-1794482993. 30 | exclude: ['Form', 'FormField', 'Editor', 'Chart'] 31 | } 32 | }, 33 | ssr: false, 34 | nitro: { 35 | output: { 36 | publicDir: '../dotnet-app/DidactUi/wwwroot' 37 | } 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/FlowExecutionContext.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Deployments; 2 | using DidactCore.Environments; 3 | using DidactCore.Flows; 4 | 5 | namespace DidactEngine.Flows 6 | { 7 | public class FlowExecutionContext : IFlowExecutionContext 8 | { 9 | public CancellationToken CancellationToken { get; } 10 | 11 | public IFlowLogger Logger { get; } 12 | 13 | public IEnvironmentContext EnvironmentContext { get; } 14 | 15 | public IDeploymentContext DeploymentContext { get; } 16 | 17 | public IFlowContext FlowContext { get; } 18 | 19 | public IFlowRunContext FlowRunContext { get; } 20 | 21 | public FlowExecutionContext(IEnvironmentContext environmentContext, IDeploymentContext deploymentContext, 22 | IFlowContext flowContext, IFlowRunContext flowRunContext, IFlowLogger flowLogger, CancellationToken cancellationToken) 23 | { 24 | EnvironmentContext = environmentContext; 25 | DeploymentContext = deploymentContext; 26 | FlowContext = flowContext; 27 | FlowRunContext = flowRunContext; 28 | Logger = flowLogger; 29 | CancellationToken = cancellationToken; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/didact-engine/Logging/FlowRunLoggerModule.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Constants; 2 | using DidactEngine.Modules; 3 | 4 | namespace DidactEngine.Logging 5 | { 6 | public class FlowRunLoggerModule : IModule 7 | { 8 | private readonly FlowRunLogChannel _flowRunLogChannel; 9 | private readonly IFlowLogRepository _repository; 10 | 11 | public string Name => EngineConstants.ModuleNames.FlowRunLogger; 12 | 13 | public bool Enabled { get; set; } = true; 14 | 15 | public int Concurrency { get; set; } = 1; 16 | 17 | public int IntervalDelay { get; set; } = Defaults.DefaultModuleIntervalDelays.FlowRunLogger; 18 | 19 | public FlowRunLoggerModule(FlowRunLogChannel flowRunLogChannel, IFlowLogRepository repository) 20 | { 21 | _flowRunLogChannel = flowRunLogChannel; 22 | _repository = repository; 23 | } 24 | 25 | public async Task ExecuteAsync(CancellationToken stoppingToken) 26 | { 27 | await foreach (var log in _flowRunLogChannel.Channel.Reader.ReadAllAsync(stoppingToken)) 28 | { 29 | await _repository.InsertLogAsync(log, stoppingToken); 30 | } 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/didact-ui/nuxt-app/app.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginsService.cs: -------------------------------------------------------------------------------- 1 | using DidactServices.Workers; 2 | using System.Collections.Concurrent; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | public class PluginsService 7 | { 8 | private readonly ILogger _logger; 9 | private readonly ConcurrentDictionary _pluginContainersDictionary; 10 | 11 | public PluginsService(ILogger logger) 12 | { 13 | _logger = logger; 14 | _pluginContainersDictionary = new ConcurrentDictionary(); 15 | } 16 | 17 | public async Task PollMissingDeploymentsAsync() 18 | { 19 | await Task.CompletedTask; 20 | } 21 | 22 | public async Task ShadowCopyDeploymentAsync() 23 | { 24 | await Task.CompletedTask; 25 | } 26 | 27 | public async Task LoadDeploymentAsPluginAsync() 28 | { 29 | await Task.CompletedTask; 30 | } 31 | 32 | public void ConfigurePluginDependencyInjection() 33 | { 34 | 35 | } 36 | 37 | public async Task ConfigureAllFlowsInPluginAsync() 38 | { 39 | await Task.CompletedTask; 40 | } 41 | 42 | public WorkerContext InstantiateFlow(IWorkerContext workerContext) 43 | { 44 | 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/BlockRun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactServices.DataModel.Entities 4 | { 5 | public class BlockRun 6 | { 7 | public long BlockRunId { get; set; } 8 | 9 | public long FlowRunId { get; set; } 10 | 11 | public long EnvironmentId { get; set; } 12 | 13 | public string? BlockName { get; set; } 14 | 15 | public string? Name { get; set; } 16 | 17 | public string? Description { get; set; } 18 | 19 | public DateTime? ExecutionStarted { get; set; } 20 | 21 | public DateTime? ExecutionEnded { get; set; } 22 | 23 | public int StateId { get; set; } 24 | 25 | public DateTime StateUpdated { get; set; } 26 | 27 | public string StateUpdatedBy { get; set; } = null!; 28 | 29 | public DateTime Created { get; set; } 30 | 31 | public string CreatedBy { get; set; } = null!; 32 | 33 | public DateTime Updated { get; set; } 34 | 35 | public string UpdatedBy { get; set; } = null!; 36 | 37 | public bool Active { get; set; } 38 | 39 | public byte[] RowVersion { get; set; } = null!; 40 | 41 | public virtual FlowRun FlowRun { get; set; } = null!; 42 | 43 | public virtual Environment Environment { get; set; } = null!; 44 | 45 | public virtual State State { get; set; } = null!; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Flow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class Flow 7 | { 8 | public long FlowId { get; set; } 9 | 10 | public long EnvironmentId { get; set; } 11 | 12 | public long DeploymentId { get; set; } 13 | 14 | public string? Name { get; set; } 15 | 16 | public string? Description { get; set; } 17 | 18 | public string TypeName { get; set; } = null!; 19 | 20 | public int ExecutionModeId { get; set; } 21 | 22 | public int ConcurrencyLimit { get; set; } 23 | 24 | public string DefaultQueueType { get; set; } = null!; 25 | 26 | public string DefaultQueueName { get; set; } = null!; 27 | 28 | public DateTime Created { get; set; } 29 | 30 | public string CreatedBy { get; set; } = null!; 31 | 32 | public DateTime Updated { get; set; } 33 | 34 | public string UpdatedBy { get; set; } = null!; 35 | 36 | public bool Active { get; set; } 37 | 38 | public byte[] RowVersion { get; set; } = null!; 39 | 40 | public virtual Environment Environment { get; set; } = null!; 41 | 42 | public virtual ExecutionMode ExecutionMode { get; set; } = null!; 43 | 44 | public virtual ICollection FlowRuns { get; } = new List(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineService.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Plugins; 2 | using DidactEngine.System; 3 | 4 | namespace DidactEngine.Engine 5 | { 6 | public class EngineService : IEngineService, IDisposable 7 | { 8 | private ILogger _logger; 9 | private readonly IPluginContainers _pluginContainers; 10 | private readonly CancellationTokenSource _cancellationTokenSource; 11 | private readonly SystemContext _systemContext; 12 | 13 | public EngineContext? EngineContext { get; private set; } 14 | 15 | public CancellationToken CancellationToken => _cancellationTokenSource.Token; 16 | 17 | public EngineService(ILogger logger, IPluginContainers pluginContainers, SystemContext systemContext) 18 | { 19 | _logger = logger; 20 | _pluginContainers = pluginContainers; 21 | _cancellationTokenSource = new CancellationTokenSource(); 22 | _systemContext = systemContext; 23 | } 24 | 25 | public async Task PollEngineShutdownAsync() 26 | { 27 | // TODO Implement 28 | } 29 | 30 | private void ShutdownEngine() 31 | { 32 | _logger.LogInformation("Shutting down the engine..."); 33 | _cancellationTokenSource.Cancel(); 34 | } 35 | 36 | public void Dispose() => _cancellationTokenSource.Dispose(); 37 | } 38 | } -------------------------------------------------------------------------------- /src/didact-engine/Constants/EngineConstants.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Constants 2 | { 3 | public static class EngineConstants 4 | { 5 | public const string ApiBasePath = "/api"; 6 | 7 | public static class CorsPolicyNames 8 | { 9 | public const string Development = "DevelopmentCors"; 10 | public const string Staging = "StagingCors"; 11 | public const string Production = "ProductionCors"; 12 | } 13 | 14 | public static class ModuleNames 15 | { 16 | public const string Plugins = "Plugins"; 17 | public const string Scheduler = "Scheduler"; 18 | public const string Workers = "Workers"; 19 | public const string Licensing = "Licensing"; 20 | public const string EngineLogger = "Engine Logger"; 21 | public const string FlowRunLogger = "FlowRun Logger"; 22 | } 23 | 24 | public static class PluginStates 25 | { 26 | public const string Loading = "Loading"; 27 | public const string Active = "Active"; 28 | public const string Draining = "Draining"; 29 | public const string Unloaded = "Unloaded"; 30 | } 31 | 32 | public static class ThreadpoolShutdownModes 33 | { 34 | public const string Immediate = "Immediate"; 35 | public const string Drain = "Drain"; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/didact-services/Constants/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace DidactServices.Constants 2 | { 3 | public static class Constants 4 | { 5 | public static class ApplicationNames 6 | { 7 | public const string DidactUi = "Didact UI"; 8 | public const string DidactEngine = "Didact Engine"; 9 | public const string DidactCli = "Didact CLI"; 10 | } 11 | 12 | public static class ApplicationConfigurationFileNames 13 | { 14 | public const string DidactCli = "cliconfig.json"; 15 | public const string DidactUi = "uiconfig.json"; 16 | public const string DidactEngine = "engineconfig.json"; 17 | } 18 | 19 | public static class Defaults 20 | { 21 | public const string DefaultBuildEnvironment = BuildEnvironments.Production; 22 | } 23 | 24 | public static class Keys 25 | { 26 | public const string BuildEnvironment = "DIDACT_BUILD_ENVIRONMENT"; 27 | } 28 | 29 | public static class BuildEnvironments 30 | { 31 | public const string Development = "Development"; 32 | public const string Staging = "Staging"; 33 | public const string Production = "Production"; 34 | } 35 | 36 | public static class DatabaseProviders 37 | { 38 | public const string SqlServer = "SqlServer"; 39 | public const string Postgres = "Postgres"; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/Organization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class Organization 7 | { 8 | public int OrganizationId { get; set; } 9 | 10 | public string Name { get; set; } = null!; 11 | 12 | public string? Description { get; set; } 13 | 14 | public DateTime Created { get; set; } 15 | 16 | public string CreatedBy { get; set; } = null!; 17 | 18 | public DateTime Updated { get; set; } 19 | 20 | public string UpdatedBy { get; set; } = null!; 21 | 22 | public bool Active { get; set; } 23 | 24 | public byte[] RowVersion { get; set; } = null!; 25 | 26 | public virtual ICollection Flows { get; } = new List(); 27 | 28 | public virtual ICollection FlowRuns { get; } = new List(); 29 | 30 | public virtual ICollection BlockRuns { get; } = new List(); 31 | 32 | public virtual ICollection HyperQueues { get; } = new List(); 33 | 34 | public virtual ICollection StrictQueues { get; } = new List(); 35 | 36 | public virtual ICollection HyperQueueItems { get; } = new List(); 37 | 38 | public virtual ICollection StrictQueueItems { get; } = new List(); 39 | 40 | public virtual ICollection Engines { get; } = new List(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/EngineConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class EngineConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(Engine)); 12 | //entity.HasIndex(e => e.UniqueName).IsUnique(); 13 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 14 | //entity.Property(e => e.UniqueName).IsRequired().HasMaxLength(255); 15 | //entity.Property(e => e.Name).HasMaxLength(255); 16 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 17 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 18 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 19 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 20 | 21 | //entity.HasOne(d => d.Organization) 22 | // .WithMany(p => p.Engines) 23 | // .HasForeignKey(d => d.OrganizationId) 24 | // .OnDelete(DeleteBehavior.ClientSetNull) 25 | // .HasConstraintName($"FK_{nameof(Engine)}_{nameof(Organization)}"); 26 | 27 | OnConfigurePartial(entity); 28 | } 29 | 30 | partial void OnConfigurePartial(EntityTypeBuilder entity); 31 | } 32 | } -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/DidactUi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | true 9 | true 10 | false 11 | true 12 | true 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:48736", 8 | "sslPort": 44369 9 | } 10 | }, 11 | "profiles": { 12 | "Development": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7248;http://localhost:5289", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "Staging": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7248;http://localhost:5289", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Staging" 30 | } 31 | }, 32 | "Production": { 33 | "commandName": "Project", 34 | "dotnetRunMessages": true, 35 | "launchBrowser": true, 36 | "applicationUrl": "https://localhost:7248;http://localhost:5289", 37 | "environmentVariables": { 38 | "ASPNETCORE_ENVIRONMENT": "Production" 39 | } 40 | }, 41 | "IIS Express": { 42 | "commandName": "IISExpress", 43 | "launchBrowser": true, 44 | "launchUrl": "swagger", 45 | "environmentVariables": { 46 | "ASPNETCORE_ENVIRONMENT": "Development" 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/didact-core/Triggers/CronScheduleTrigger.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Constants; 2 | 3 | namespace DidactCore.Triggers 4 | { 5 | public class CronScheduleTrigger : ICronScheduleTrigger 6 | { 7 | public string TriggerType { get; } = TriggerTypes.CronSchedule; 8 | 9 | public string TriggerScope { get; set; } = TriggerScopes.Flow; 10 | 11 | public string? Name { get; set; } 12 | 13 | public string? Description { get; set; } 14 | 15 | public string CronExpression { get; set; } = null!; 16 | 17 | // TODO Implement later? 18 | //public long? ExistingCronScheduleTriggerId { get; set; } 19 | 20 | public CronScheduleTrigger(string cronExpression, string triggerScope = TriggerScopes.Flow) 21 | { 22 | CronExpression = cronExpression; 23 | TriggerScope = triggerScope; 24 | } 25 | 26 | public CronScheduleTrigger(string cronExpression, string name, string triggerScope = TriggerScopes.Flow) 27 | { 28 | CronExpression = cronExpression; 29 | Name = name; 30 | TriggerScope = triggerScope; 31 | } 32 | 33 | public CronScheduleTrigger(string cronExpression, string name, string description, string triggerScope = TriggerScopes.Flow) 34 | { 35 | CronExpression= cronExpression; 36 | Name = name; 37 | Description = description; 38 | TriggerScope = triggerScope; 39 | } 40 | 41 | // TODO Implement later? 42 | //public CronScheduleTrigger(long existingCronScheduleTriggerId) 43 | //{ 44 | // ExistingCronScheduleTriggerId = existingCronScheduleTriggerId; 45 | //} 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/didact-services/HostAppEnvironments/HostAppEnvironmentService.cs: -------------------------------------------------------------------------------- 1 | namespace DidactServices.HostAppEnvironments 2 | { 3 | using DidactServices.Constants; 4 | using System; 5 | 6 | public class HostAppEnvironmentService 7 | { 8 | public HostAppEnvironmentService() { } 9 | 10 | /// 11 | /// Gets the Didact Build Environment and uses the default if one is not found. 12 | /// 13 | /// 14 | public static string GetBuildEnvironment() 15 | { 16 | return Environment.GetEnvironmentVariable(Constants.Keys.BuildEnvironment) 17 | ?? Constants.Defaults.DefaultBuildEnvironment; 18 | } 19 | 20 | public static bool IsBuildEnvironmentProduction(string buildEnvironment) => 21 | string.Equals(buildEnvironment, Constants.BuildEnvironments.Production, StringComparison.OrdinalIgnoreCase); 22 | 23 | /// 24 | /// Gets the dynamic environment name of the host app by using the build environment. 25 | /// This helps distinguish a Didact maintainer from a Didact user. 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static string? GetDynamicHostAppEnvironment(string buildEnvironment, string? hostAppEnvironment) 31 | { 32 | if (string.IsNullOrEmpty(hostAppEnvironment)) 33 | { 34 | return hostAppEnvironment; 35 | } 36 | 37 | return IsBuildEnvironmentProduction(buildEnvironment) 38 | ? buildEnvironment 39 | : hostAppEnvironment; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/FlowConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class FlowConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(Flow)); 12 | //entity.HasIndex(e => e.Name).IsUnique(); 13 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 14 | //entity.Property(e => e.Version).HasMaxLength(255); 15 | //entity.Property(e => e.TypeName).IsRequired(); 16 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 17 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 18 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 19 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 20 | 21 | //entity.HasOne(d => d.Organization) 22 | // .WithMany(p => p.Flows) 23 | // .HasForeignKey(d => d.OrganizationId) 24 | // .OnDelete(DeleteBehavior.ClientSetNull) 25 | // .HasConstraintName($"FK_{nameof(Flow)}_{nameof(Organization)}"); 26 | 27 | //entity.HasOne(d => d.ExecutionMode) 28 | // .WithMany(p => p.Flows) 29 | // .HasForeignKey(d => d.ExecutionModeId) 30 | // .OnDelete(DeleteBehavior.ClientSetNull) 31 | // .HasConstraintName($"FK_{nameof(Flow)}_{nameof(ExecutionMode)}"); 32 | 33 | OnConfigurePartial(entity); 34 | } 35 | 36 | partial void OnConfigurePartial(EntityTypeBuilder entity); 37 | } 38 | } -------------------------------------------------------------------------------- /src/didact-cli/DidactCli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | true 10 | true 11 | false 12 | true 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/didact-engine/Controllers/MaintenanceController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Swashbuckle.AspNetCore.Annotations; 3 | 4 | namespace DidactEngine.Controllers 5 | { 6 | [ApiController] 7 | [Route("api/{controller}")] 8 | public class MaintenanceController : ControllerBase 9 | { 10 | private readonly ILogger _logger; 11 | private readonly IHostApplicationLifetime _hostApplicationLifetime; 12 | 13 | public MaintenanceController(ILogger logger, IHostApplicationLifetime hostApplicationLifetime) 14 | { 15 | _logger = logger; 16 | _hostApplicationLifetime = hostApplicationLifetime; 17 | } 18 | 19 | /// 20 | /// Returns a 200 heartbeat to show the caller that it has access to the API. 21 | /// 22 | /// 23 | [HttpGet("/heartbeat")] 24 | [SwaggerResponse(StatusCodes.Status200OK)] 25 | public IActionResult GetHeartbeat() 26 | { 27 | return Ok(); 28 | } 29 | 30 | /// 31 | /// Shuts down the API. Most likely, the API will be running behind a traditional web server, so the next request to the API will revive it. 32 | /// This will potentially be used to restart the API once a new set of DLL files are detected. 33 | /// 34 | /// 35 | [HttpGet("/shutdown")] 36 | [SwaggerResponse(StatusCodes.Status200OK, type: typeof(string))] 37 | public IActionResult ShutdownApplication() 38 | { 39 | _logger.LogCritical("Shutting down the application via the /shutdown endpoint..."); 40 | _hostApplicationLifetime.StopApplication(); 41 | return Ok("Didact Engine is now shutting down."); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/HyperQueueItemConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class HyperQueueItemConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | entity.ToTable(nameof(HyperQueueItem)); 12 | entity.Property(e => e.CreatedBy).HasMaxLength(255); 13 | entity.Property(e => e.UpdatedBy).HasMaxLength(255); 14 | entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 15 | 16 | //entity.HasOne(d => d.Organization) 17 | // .WithMany(p => p.HyperQueueInbounds) 18 | // .HasForeignKey(d => d.OrganizationId) 19 | // .OnDelete(DeleteBehavior.ClientSetNull) 20 | // .HasConstraintName($"FK_{nameof(HyperQueueItem)}_{nameof(Organization)}"); 21 | 22 | //entity.HasOne(d => d.FlowRun) 23 | // .WithMany(p => p.HyperQueueInbounds) 24 | // .HasForeignKey(d => d.FlowRunId) 25 | // .OnDelete(DeleteBehavior.ClientSetNull) 26 | // .HasConstraintName($"FK_{nameof(HyperQueueItem)}_{nameof(FlowRun)}"); 27 | 28 | //entity.HasOne(d => d.HyperQueue) 29 | // .WithMany(p => p.HyperQueueInbounds) 30 | // .HasForeignKey(d => d.HyperQueueId) 31 | // .OnDelete(DeleteBehavior.ClientSetNull) 32 | // .HasConstraintName($"FK_{nameof(HyperQueueItem)}_${nameof(HyperQueue)}"); 33 | 34 | OnConfigurePartial(entity); 35 | } 36 | 37 | partial void OnConfigurePartial(EntityTypeBuilder entity); 38 | } 39 | } -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginAssemblyLoadContext.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.Loader; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | public class PluginAssemblyLoadContext : AssemblyLoadContext 7 | { 8 | private readonly AssemblyDependencyResolver _resolver; 9 | 10 | public PluginAssemblyLoadContext(string pluginPath) : base(isCollectible: true) 11 | { 12 | _resolver = new AssemblyDependencyResolver(pluginPath); 13 | } 14 | 15 | protected override Assembly? Load(AssemblyName assemblyName) 16 | { 17 | // If the assembly is already loaded in the default AssemblyLoadContext, 18 | // then bypass the plugin's assembly and use the one from the default AssemblyLoadContext. 19 | var assembly = Default.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name); 20 | if (assembly is not null) 21 | { 22 | return assembly; 23 | } 24 | 25 | // Use the resolver to load other assemblies specific to this AssemblyLoadContext 26 | var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); 27 | if (assemblyPath is not null) 28 | { 29 | return LoadFromAssemblyPath(assemblyPath); 30 | } 31 | 32 | return null; 33 | } 34 | 35 | protected override nint LoadUnmanagedDll(string unmanagedDllName) 36 | { 37 | string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); 38 | if (libraryPath is not null) 39 | { 40 | return LoadUnmanagedDllFromPath(libraryPath); 41 | } 42 | 43 | return nint.Zero; 44 | } 45 | 46 | public IEnumerable GetAssemblies() 47 | { 48 | return Assemblies; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/StrictQueueItemConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class StrictQueueItemConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(StrictQueueItem)); 12 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 13 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 15 | 16 | //entity.HasOne(d => d.Organization) 17 | // .WithMany(p => p.FifoQueueInbounds) 18 | // .HasForeignKey(d => d.OrganizationId) 19 | // .OnDelete(DeleteBehavior.ClientSetNull) 20 | // .HasConstraintName($"FK_{nameof(StrictQueueItem)}_{nameof(Organization)}"); 21 | 22 | //entity.HasOne(d => d.FlowRun) 23 | // .WithMany(p => p.FifoQueueInbounds) 24 | // .HasForeignKey(d => d.FlowRunId) 25 | // .OnDelete(DeleteBehavior.ClientSetNull) 26 | // .HasConstraintName($"FK_{nameof(StrictQueueItem)}_{nameof(FlowRun)}"); 27 | 28 | //entity.HasOne(d => d.FifoQueue) 29 | // .WithMany(p => p.FifoQueueInbounds) 30 | // .HasForeignKey(d => d.FifoQueueId) 31 | // .OnDelete(DeleteBehavior.ClientSetNull) 32 | // .HasConstraintName($"FK_{nameof(StrictQueueItem)}_{nameof(StrictQueue)}"); 33 | 34 | OnConfigurePartial(entity); 35 | } 36 | 37 | partial void OnConfigurePartial(EntityTypeBuilder entity); 38 | } 39 | } -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginDependencyInjector.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection.Extensions; 2 | 3 | namespace DidactEngine.Plugins 4 | { 5 | public class PluginDependencyInjector : IPluginDependencyInjector 6 | { 7 | private readonly IServiceCollection _applicationServiceCollection; 8 | 9 | public IServiceCollection PluginServiceCollection { get; set; } 10 | 11 | public IServiceProvider PluginServiceProvider { get; set; } 12 | 13 | public PluginDependencyInjector(IServiceCollection applicationServiceCollection) 14 | { 15 | _applicationServiceCollection = applicationServiceCollection; 16 | PluginServiceCollection = new ServiceCollection(); 17 | PluginServiceProvider = PluginServiceCollection.BuildServiceProvider(); 18 | } 19 | 20 | public void ClearServiceCollection() 21 | { 22 | PluginServiceCollection.Clear(); 23 | } 24 | 25 | public void BuildServiceCollection(IServiceCollection pluginServiceCollection) 26 | { 27 | foreach (var service in _applicationServiceCollection) 28 | { 29 | PluginServiceCollection.TryAdd(service); 30 | } 31 | 32 | foreach (var service in pluginServiceCollection) 33 | { 34 | PluginServiceCollection.TryAdd(service); 35 | } 36 | 37 | PluginServiceProvider = PluginServiceCollection.BuildServiceProvider(); 38 | } 39 | 40 | public T CreateInstance(params object[] parameters) 41 | { 42 | return ActivatorUtilities.CreateInstance(PluginServiceProvider, parameters); 43 | } 44 | 45 | public object CreateInstance(Type type, params object[] parameters) 46 | { 47 | return ActivatorUtilities.CreateInstance(PluginServiceProvider, type, parameters); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/FlowConfigurator.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Flows; 2 | using DidactCore.Triggers; 3 | using DidactPrimitives.Constants; 4 | 5 | namespace DidactEngine.Flows 6 | { 7 | public class FlowConfigurator : IFlowConfigurator 8 | { 9 | public string? Name { get; private set; } 10 | 11 | public string? Description { get; private set; } 12 | 13 | public string Version { get; private set; } = DidactCore.Constants.Defaults.DefaultFlowVersion; 14 | 15 | public string? TypeName { get; private set; } 16 | 17 | public string DefaultQueueType { get; private set; } = QueueTypes.HyperQueue; 18 | 19 | public string DefaultQueueName { get; private set; } = DidactCore.Constants.Defaults.DefaultQueueName; 20 | 21 | public ICollection CronScheduleTriggers { get; private set; } = []; 22 | 23 | public IFlowConfigurator WithName(string name) 24 | { 25 | Name = name; 26 | return this; 27 | } 28 | 29 | public IFlowConfigurator WithDescription(string description) 30 | { 31 | Description = description; 32 | return this; 33 | } 34 | 35 | public IFlowConfigurator AsVersion(string version) 36 | { 37 | Version = version; 38 | return this; 39 | } 40 | 41 | public IFlowConfigurator WithTypeName(string typeName) 42 | { 43 | TypeName = typeName; 44 | return this; 45 | } 46 | 47 | public IFlowConfigurator WithDefaultQueue(string queueType, string queueName = DidactCore.Constants.Defaults.DefaultQueueName) 48 | { 49 | DefaultQueueType = queueType; 50 | DefaultQueueName = queueName; 51 | return this; 52 | } 53 | 54 | public IFlowConfigurator WithCronScheduleTrigger(ICronScheduleTrigger cronScheduleTrigger) 55 | { 56 | CronScheduleTriggers.Add(cronScheduleTrigger); 57 | return this; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/IPluginContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace DidactEngine.Plugins 4 | { 5 | public interface IPluginContainer 6 | { 7 | PluginAssemblyLoadContext PluginAssemblyLoadContext { get; set; } 8 | 9 | ICollection PluginExecutionVersions { get; } 10 | 11 | DateTime? PluginLoadedAt { get; set; } 12 | 13 | DateTime? LastExecution { get; set; } 14 | 15 | IPluginDependencyInjector PluginDependencyInjector { get; set; } 16 | 17 | /// 18 | /// Gets an enumeration of the assemblies from the plugin container's . 19 | /// 20 | /// 21 | IEnumerable GetAssemblies(); 22 | 23 | /// 24 | /// Sets a timestamp for when the plugin was loaded into the plugin container. 25 | /// 26 | /// 27 | void SetPluginLoadedAt(DateTime? pluginLoadedAt); 28 | 29 | /// 30 | /// Configures the plugin's dependency injection system. 31 | /// 32 | void ConfigureDependencyInjection(); 33 | 34 | /// 35 | /// Retrieves and instantiates all Flow types to generate their configurators. 36 | /// Then uses the configurators and reflection to determine each Flow's . 37 | /// 38 | Task CollectPluginExecutionVersionsAsync(); 39 | 40 | /// 41 | /// Retrieves all Flow types from the plugin's assemblies using reflection, 42 | /// instantiates each Flow type using the plugin dependency injection system, 43 | /// and runs their configuration functions. 44 | /// If configuration fails for a specific set of Flows, 45 | /// gracefully handles the failures and passes through the successes. 46 | /// 47 | /// 48 | Task ConfigureFlowsAsync(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/BlockRunConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class BlockRunConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(BlockRun)); 12 | //entity.Property(e => e.BlockName).HasMaxLength(255); 13 | //entity.Property(e => e.Name).HasMaxLength(255); 14 | //entity.Property(e => e.StateLastUpdatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 16 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 17 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 18 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 19 | 20 | //entity.HasOne(d => d.FlowRun) 21 | // .WithMany(p => p.BlockRuns) 22 | // .HasForeignKey(d => d.FlowRunId) 23 | // .OnDelete(DeleteBehavior.ClientSetNull) 24 | // .HasConstraintName($"FK_{nameof(BlockRun)}_{nameof(FlowRun)}"); 25 | 26 | //entity.HasOne(d => d.Organization) 27 | // .WithMany(p => p.BlockRuns) 28 | // .HasForeignKey(d => d.OrganizationId) 29 | // .OnDelete(DeleteBehavior.ClientSetNull) 30 | // .HasConstraintName($"FK_{nameof(BlockRun)}_{nameof(Organization)}"); 31 | 32 | //entity.HasOne(d => d.State) 33 | // .WithMany(p => p.BlockRuns) 34 | // .HasForeignKey(d => d.StateId) 35 | // .OnDelete(DeleteBehavior.ClientSetNull) 36 | // .HasConstraintName($"FK_{nameof(BlockRun)}_{nameof(State)}"); 37 | 38 | OnConfigurePartial(entity); 39 | } 40 | 41 | partial void OnConfigurePartial(EntityTypeBuilder entity); 42 | } 43 | } -------------------------------------------------------------------------------- /src/didact-engine/Licensing/LicenseValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace DidactEngine.Licensing 5 | { 6 | public class LicenseValidator 7 | { 8 | // From didact-prod-license-server-encryption-key PEM file. 9 | private const string PublicKeyPemString = @" 10 | -----BEGIN PUBLIC KEY----- 11 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArXcXnZQVLQiBEG2PwvdJ33vgk1UaqrvQ1+ZAr17IdhYSe1uuCZ4WTOfKT0u8P4HlcDKYq5KwegkBItR7uEhCu17gfGyJ/+nZzEddXSjAN97ar1e3yerr5SsVWKX5LRUtUOGrB8HpHgW5dSVvD8H1WJrvCdCH6NwxhMAZhiZgOMe2c5p4yyhz6HvzYydXlG7C+RH3da6FOAoYPFvzLX57WMZdYVRMtQ3kd28bxaa14HmqcSNsBbh3YOESUxKZH5spqv04xRUaJhI9CosgouoUTGdexremhnY+WkV2BYvXyUjTPPbWUS79F+Qrpi3He6MBl8Ud8pINW7XXf9jJ9HD1UQIDAQAB 12 | -----END PUBLIC KEY----- 13 | "; 14 | 15 | private readonly ILogger _logger; 16 | private readonly RSA _publicKey; 17 | 18 | public LicenseValidator(ILogger logger) 19 | { 20 | _logger = logger; 21 | _publicKey = RSA.Create(); 22 | _publicKey.ImportFromPem(PublicKeyPemString.ToCharArray()); 23 | } 24 | 25 | public bool ValidateLicense(string fullLicense) 26 | { 27 | // Split data and signature 28 | var parts = fullLicense.Split('.'); 29 | var dataBytes = Convert.FromBase64String(parts[0]); 30 | var signatureBytes = Convert.FromBase64String(parts[1]); 31 | 32 | // Verify the signature 33 | bool isValid = _publicKey.VerifyData(dataBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); 34 | 35 | if (!isValid) return false; 36 | 37 | // Parse the license data 38 | var licenseData = Encoding.UTF8.GetString(dataBytes); 39 | var parts2 = licenseData.Split('|'); 40 | var failSafeExpiry = DateTime.Parse(parts[1]); 41 | 42 | // Check if the fail-safe expiry is still valid 43 | return DateTime.UtcNow <= failSafeExpiry; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/didact-services/DataModel/Entities/FlowRun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactServices.DataModel.Entities 5 | { 6 | public class FlowRun 7 | { 8 | public long FlowRunId { get; set; } 9 | 10 | public long FlowId { get; set; } 11 | 12 | public long TriggerId { get; set; } 13 | 14 | public long FlowVersionId { get; set; } 15 | 16 | public long EnvironmentId { get; set; } 17 | 18 | public int ExecutionModeId { get; set; } 19 | 20 | public string? Name { get; set; } 21 | 22 | public string? Description { get; set; } 23 | 24 | public string? JsonPayload { get; set; } 25 | 26 | public int TimeoutSeconds { get; set; } 27 | 28 | public int StateId { get; set; } 29 | 30 | public DateTime? ExecuteAt { get; set; } 31 | 32 | public DateTime? ExecutionStarted { get; set; } 33 | 34 | public DateTime? ExecutionEnded { get; set; } 35 | 36 | public DateTime Created { get; set; } 37 | 38 | public string CreatedBy { get; set; } = null!; 39 | 40 | public DateTime Updated { get; set; } 41 | 42 | public string UpdatedBy { get; set; } = null!; 43 | 44 | public bool Active { get; set; } 45 | 46 | public byte[] RowVersion { get; set; } = null!; 47 | 48 | public virtual Flow Flow { get; set; } = null!; 49 | 50 | public virtual FlowVersion FlowVersion { get; set; } = null!; 51 | 52 | public virtual Environment Environment { get; set; } = null!; 53 | 54 | public virtual State State { get; set; } = null!; 55 | 56 | public virtual Trigger Trigger { get; set; } = null!; 57 | 58 | public virtual ExecutionMode ExecutionMode { get; set; } = null!; 59 | 60 | public virtual ICollection BlockRuns { get; set; } = new List(); 61 | 62 | public virtual ICollection HyperQueueItems { get; } = new List(); 63 | 64 | public virtual ICollection StrictQueueItems { get; } = new List(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/didact-engine/Engine/EngineSupervisor.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Constants; 2 | using DidactEngine.Plugins; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace DidactCore.Engine 10 | { 11 | public class EngineSupervisor : IEngineSupervisor 12 | { 13 | public long EngineId { get; set; } 14 | 15 | public string MachineName { get; set; } = Environment.MachineName; 16 | 17 | public int ProcessId { get; set; } = Process.GetCurrentProcess().Id; 18 | 19 | public Guid EngineGuid { get; set; } = Guid.NewGuid(); 20 | 21 | public Guid EngineUniversalId { get; set; } 22 | 23 | public EngineTuningDto? EngineTuning { get; set; } 24 | 25 | public string EngineState { get; set; } = EngineStates.StartingUp; 26 | 27 | public CancellationToken CancellationToken { get; set; } 28 | 29 | public DateTime EngineStateUpdated { get; set; } = DateTime.Now; 30 | 31 | public IPluginContainers? PluginContainers { get; set; } 32 | 33 | private readonly ILogger _logger; 34 | private readonly IEngineRepository _engineRepository; 35 | private readonly CancellationTokenSource _cancellationTokenSource; 36 | 37 | public EngineSupervisor(ILogger logger, IEngineRepository engineRepository) 38 | { 39 | _logger = logger; 40 | _engineRepository = engineRepository; 41 | _cancellationTokenSource = new CancellationTokenSource(); 42 | } 43 | 44 | public void SetEngineState(string engineState) 45 | { 46 | EngineState = engineState; 47 | EngineStateUpdated = DateTime.Now; 48 | } 49 | 50 | public async Task CheckForEngineShutdownEventAsync() 51 | { 52 | // TODO Finish implementing 53 | await _engineRepository.CheckForEngineShutdownAsync(); 54 | } 55 | 56 | public string GetEngineState() 57 | { 58 | return EngineState; 59 | } 60 | 61 | public DateTime GetEngineStateUpdated() 62 | { 63 | return EngineStateUpdated; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/IPluginDependencyInjector.cs: -------------------------------------------------------------------------------- 1 | namespace DidactEngine.Plugins 2 | { 3 | public interface IPluginDependencyInjector 4 | { 5 | /// 6 | /// The that contains all of the services from both Didact Engine and the Flow Library. 7 | /// 8 | IServiceCollection PluginServiceCollection { get; set; } 9 | 10 | /// 11 | /// The corresponding to the . 12 | /// 13 | IServiceProvider PluginServiceProvider { get; set; } 14 | 15 | /// 16 | /// Clears the . 17 | /// 18 | void ClearServiceCollection(); 19 | 20 | /// 21 | /// Adds each service from the plugin to the 22 | /// and rebuilds the . 23 | /// 24 | /// 25 | void BuildServiceCollection(IServiceCollection pluginServiceCollection); 26 | 27 | /// 28 | /// A wrapper function for the method 29 | /// from the .NET dependency injection library that uses the 30 | /// as the default . 31 | /// 32 | /// 33 | /// 34 | /// 35 | T CreateInstance(params object[] parameters); 36 | 37 | /// 38 | /// A wrapper function for the method 39 | /// from the .NET dependency injection library that uses the 40 | /// as the default . 41 | /// 42 | /// 43 | /// 44 | /// 45 | object CreateInstance(Type type, params object[] parameters); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/didact-engine/DidactEngine.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | True 8 | $(NoWarn);1591 9 | $(NoWarn);IDE0290 10 | 8bccdc4b-2bdb-4817-a72e-bdd5935343db 11 | true 12 | true 13 | true 14 | false 15 | true 16 | true 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | all 37 | runtime; build; native; contentfiles; analyzers; buildtransitive 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/IPluginContainers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DidactEngine.Plugins 5 | { 6 | public interface IPluginContainers 7 | { 8 | ICollection PluginContainersCollection { get; set; } 9 | 10 | DateTime PluginContainersUpdatedAt { get; set; } 11 | 12 | void SetPluginContainersUpdatedAt(DateTime? pluginContainersUpdatedAt); 13 | 14 | /// 15 | /// Finds a matching for the given . 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | IPluginContainer FindMatchingPluginContainer(Type type); 22 | 23 | /// 24 | /// Finds a matching for the given . 25 | /// 26 | /// 27 | /// 28 | /// 29 | IPluginContainer FindMatchingPluginContainer(PluginExecutionVersion pluginExecutionVersion); 30 | 31 | /// 32 | /// Finds a matching for the given assembly FullName. 33 | /// 34 | /// 35 | /// 36 | /// 37 | IPluginContainer FindMatchingPluginContainer(string assemblyName, string assemblyVersion); 38 | 39 | /// 40 | /// Finds a matching for the given assembly FullName and type Name. 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | IPluginContainer FindMatchingPluginContainer(string assemblyName, string assemblyVersion, string typeName); 47 | 48 | void AddPluginContainer(IPluginContainer pluginContainer); 49 | 50 | void RemovePluginContainer(IPluginContainer pluginContainer); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginExecutionVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DidactEngine.Plugins 4 | { 5 | public class PluginExecutionVersion 6 | { 7 | public string FlowTypeName { get; set; } = null!; 8 | 9 | public string FlowVersion { get; set; } = null!; 10 | 11 | public string LibraryAssemblyName { get; set; } = null!; 12 | 13 | public string LibraryAssemblyVersion { get; set; } = null!; 14 | 15 | public PluginExecutionVersion(string flowTypeName, string flowVersion, string libraryAssemblyName, string libraryAssemblyVersion) 16 | { 17 | FlowTypeName = flowTypeName; 18 | FlowVersion = flowVersion; 19 | LibraryAssemblyName = libraryAssemblyName; 20 | LibraryAssemblyVersion = libraryAssemblyVersion; 21 | } 22 | 23 | // Override the Equals method 24 | public override bool Equals(object obj) 25 | { 26 | if (obj is null || GetType() != obj.GetType()) 27 | { 28 | return false; 29 | } 30 | 31 | return Equals((PluginExecutionVersion)obj); 32 | } 33 | 34 | // Implement IEquatable.Equals 35 | public bool Equals(PluginExecutionVersion other) 36 | { 37 | if (other is null) 38 | { 39 | return false; 40 | } 41 | 42 | return other.FlowTypeName == FlowTypeName 43 | && other.FlowVersion == FlowVersion 44 | && other.LibraryAssemblyName == LibraryAssemblyName 45 | && other.LibraryAssemblyVersion == LibraryAssemblyVersion; 46 | } 47 | 48 | // Override GetHashCode 49 | public override int GetHashCode() 50 | { 51 | // Combine hash codes from fields to ensure uniqueness 52 | return HashCode.Combine(FlowTypeName, FlowVersion, LibraryAssemblyName, LibraryAssemblyVersion); 53 | } 54 | 55 | // Optionally, override == and != operators for syntactic sugar 56 | public static bool operator ==(PluginExecutionVersion left, PluginExecutionVersion right) 57 | { 58 | if (left is null) 59 | { 60 | return right is null; 61 | } 62 | return left.Equals(right); 63 | } 64 | 65 | public static bool operator !=(PluginExecutionVersion left, PluginExecutionVersion right) 66 | { 67 | return !(left == right); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/didact-engine/Flows/IFlowRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using DidactCore.Entities; 4 | 5 | namespace DidactCore.Flows 6 | { 7 | public interface IFlowRepository 8 | { 9 | /// 10 | /// Asynchronously saves the values from IFlowConfigurator to persistent storage. 11 | /// 12 | /// 13 | /// 14 | /// 15 | Task SaveConfigurationsAsync(IFlowConfigurator flowConfigurator); 16 | 17 | /// 18 | /// Asynchronously retrieves a Flow from persistent storage by its primary key. 19 | /// 20 | /// 21 | /// 22 | Task GetFlowByIdAsync(long flowId); 23 | 24 | /// 25 | /// Asynchronously retrieves a Flow from persistent storage by its Type name. 26 | /// 27 | /// 28 | /// 29 | Task GetFlowByTypeNameAsync(string flowTypeName); 30 | 31 | /// 32 | /// Asynchronously retrieves a Flow from persistent storage by its name. 33 | /// 34 | /// 35 | /// 36 | Task GetFlowByNameAsync(string name); 37 | 38 | /// 39 | /// Asynchronously retrieves all Flows from persistent storage by the given organization primary key. 40 | /// 41 | /// 42 | /// 43 | Task> GetAllOrganizationFlowsFromStorageAsync(int organizationId); 44 | 45 | /// 46 | /// Asynchronously retrieves all Flows previously saved to persistent storage. 47 | /// 48 | /// 49 | Task> GetAllFlowsFromStorageAsync(); 50 | 51 | /// 52 | /// Asynchronously deactivates a Flow in persistent storage by its primary key. 53 | /// 54 | /// 55 | /// 56 | Task DeactivateFlowByIdAsync(long flowId); 57 | 58 | /// 59 | /// Asynchronously activates a Flow in persistent storage by its primary key. 60 | /// 61 | /// 62 | /// 63 | Task ActivateFlowByIdAsync(long flowId); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Configurations/FlowRunConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore; 3 | using DidactServices.DataModel.Entities; 4 | 5 | namespace DidactServices.DataModel.Configurations 6 | { 7 | public partial class FlowRunConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder entity) 10 | { 11 | //entity.ToTable(nameof(FlowRun)); 12 | //entity.Property(e => e.Name).IsRequired().HasMaxLength(255); 13 | //entity.Property(e => e.StateLastUpdatedBy).HasMaxLength(255); 14 | //entity.Property(e => e.CreatedBy).HasMaxLength(255); 15 | //entity.Property(e => e.LastUpdatedBy).HasMaxLength(255); 16 | //entity.Property(e => e.Active).IsRequired().HasDefaultValue(true); 17 | //entity.Property(e => e.RowVersion).IsRowVersion().IsConcurrencyToken(); 18 | 19 | //entity.HasOne(d => d.Flow) 20 | // .WithMany(p => p.FlowRuns) 21 | // .HasForeignKey(d => d.FlowId) 22 | // .OnDelete(DeleteBehavior.ClientSetNull) 23 | // .HasConstraintName($"FK_{nameof(FlowRun)}_{nameof(Flow)}"); 24 | 25 | //entity.HasOne(d => d.Organization) 26 | // .WithMany(p => p.FlowRuns) 27 | // .HasForeignKey(d => d.OrganizationId) 28 | // .OnDelete(DeleteBehavior.ClientSetNull) 29 | // .HasConstraintName($"FK_{nameof(FlowRun)}_{nameof(Organization)}"); 30 | 31 | //entity.HasOne(d => d.TriggerType) 32 | // .WithMany(p => p.FlowRuns) 33 | // .HasForeignKey(d => d.TriggerTypeId) 34 | // .OnDelete(DeleteBehavior.ClientSetNull) 35 | // .HasConstraintName($"FK_{nameof(FlowRun)}_{nameof(TriggerType)}"); 36 | 37 | //entity.HasOne(d => d.ExecutionMode) 38 | // .WithMany(p => p.FlowRuns) 39 | // .HasForeignKey(d => d.ExecutionModeId) 40 | // .OnDelete(DeleteBehavior.ClientSetNull) 41 | // .HasConstraintName($"FK_{nameof(FlowRun)}_{nameof(ExecutionMode)}"); 42 | 43 | //entity.HasOne(d => d.State) 44 | // .WithMany(p => p.FlowRuns) 45 | // .HasForeignKey(d => d.StateId) 46 | // .OnDelete(DeleteBehavior.ClientSetNull) 47 | // .HasConstraintName($"FK_{nameof(FlowRun)}_{nameof(State)}"); 48 | 49 | OnConfigurePartial(entity); 50 | } 51 | 52 | partial void OnConfigurePartial(EntityTypeBuilder entity); 53 | } 54 | } -------------------------------------------------------------------------------- /src/didact-engine/Modules/ModuleSupervisor.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Engine; 2 | 3 | namespace DidactEngine.Modules 4 | { 5 | public class ModuleSupervisor : BackgroundService 6 | { 7 | private readonly ILogger _logger; 8 | private readonly IEnumerable _modules; 9 | private readonly IEngineService _engineService; 10 | 11 | public ModuleSupervisor(IEnumerable modules, ILogger logger, IEngineService engineService) 12 | { 13 | _modules = modules; 14 | _logger = logger; 15 | _engineService = engineService; 16 | } 17 | 18 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 19 | { 20 | /* Create a composite cancellation token that covers an engine shutdown event from the runtime 21 | * as well as from manual engine polling. 22 | */ 23 | using var compositeCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, _engineService.CancellationToken); 24 | var compositeCancellationToken = compositeCancellationTokenSource.Token; 25 | 26 | var moduleTasks = _modules 27 | .Where(m => m.Enabled) 28 | .SelectMany(m => 29 | Enumerable.Range(0, m.Concurrency) 30 | .Select(i => RunModuleLoopAsync(new ModuleContext(m, i + 1, compositeCancellationToken)))) 31 | .ToList(); 32 | 33 | await Task.WhenAll(moduleTasks); 34 | } 35 | 36 | private async Task RunModuleLoopAsync(ModuleContext moduleContext) 37 | { 38 | _logger.LogInformation("{name} starting {supervisorName} execution loop...", moduleContext.Name, nameof(ModuleSupervisor)); 39 | 40 | while (!moduleContext.CancellationToken.IsCancellationRequested && moduleContext.Module.Enabled) 41 | { 42 | try 43 | { 44 | await moduleContext.Module.ExecuteAsync(moduleContext.CancellationToken); 45 | } 46 | catch (Exception ex) 47 | { 48 | _logger.LogError(ex, "{Name} crashed. Restarting...", moduleContext.Name); 49 | await Task.Delay(moduleContext.Module.IntervalDelay, moduleContext.CancellationToken); 50 | continue; // restart the loop (revives the module) 51 | } 52 | 53 | await Task.Delay(moduleContext.Module.IntervalDelay, moduleContext.CancellationToken); 54 | } 55 | 56 | _logger.LogWarning("{name} {supervisorName} execution loop stopped.", moduleContext.Name, nameof(ModuleSupervisor)); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /LICENSE-COMM.md: -------------------------------------------------------------------------------- 1 | Didact License 1.0 2 | 3 | Acceptance 4 | 5 | By using the software, you agree to all of the terms and conditions below. 6 | 7 | Copyright License 8 | 9 | The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use the software, subject to the limitations and conditions below. These terms and conditions grant you no license to modify or distribute the software, or to make it available to others. 10 | 11 | Limitations 12 | 13 | You must not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key. If you obtain a license key, you must not provide your license key to others, or allow them to use your license key. 14 | 15 | You must not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law. 16 | 17 | Patents 18 | 19 | The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company. 20 | 21 | No Other Rights 22 | 23 | These terms do not imply any licenses other than those expressly granted in these terms. 24 | 25 | Termination 26 | 27 | If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. The licensor may, at any time, discontinue access to the software. 28 | 29 | No Liability 30 | 31 | As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim. If this disclaimer is unenforceable under applicable law, this license is void. 32 | 33 | Definitions 34 | 35 | The licensor is Symbisoft, LLC, and the software is the software the licensor makes available under these terms, including any portion of it. 36 | 37 | You refers to the individual or entity agreeing to these terms. 38 | 39 | Your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. 40 | 41 | Control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect. 42 | 43 | Your licenses are all the licenses granted to you for the software under these terms. 44 | 45 | Use means anything you do with the software requiring one of your licenses. 46 | 47 | Trademark means trademarks, service marks, and similar rights. -------------------------------------------------------------------------------- /src/didact-core/Flows/IFlowConfigurator.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Constants; 2 | using DidactCore.Triggers; 3 | using System.Collections.Generic; 4 | 5 | namespace DidactCore.Flows 6 | { 7 | /// 8 | /// A helper interface that configures Flow metadata. 9 | /// This interface and its implementations need to be registered as a transient dependency in Didact Engine. 10 | /// 11 | public interface IFlowConfigurator 12 | { 13 | /// 14 | /// The Flow's name. 15 | /// 16 | string? Name { get; } 17 | 18 | /// 19 | /// The Flow's description. 20 | /// 21 | string? Description { get; } 22 | 23 | /// 24 | /// The Flow's version. 25 | /// 26 | string Version { get; } 27 | 28 | /// 29 | /// The Flow's Type name. 30 | /// 31 | string? TypeName { get; } 32 | 33 | /// 34 | /// The designated queue type that the Flow will execute against. 35 | /// 36 | string DefaultQueueType { get; } 37 | 38 | /// 39 | /// The designated queue that the Flow will execute against. 40 | /// 41 | string DefaultQueueName { get; } 42 | 43 | /// 44 | /// The collection of Cron Scheduler triggers for the Flow. 45 | /// 46 | ICollection CronScheduleTriggers { get; } 47 | 48 | /// 49 | /// Sets the Flow name. 50 | /// 51 | /// 52 | /// 53 | IFlowConfigurator WithName(string name); 54 | 55 | /// 56 | /// Sets the Flow description. 57 | /// 58 | /// 59 | /// 60 | IFlowConfigurator WithDescription(string description); 61 | 62 | /// 63 | /// Sets the Flow version. 64 | /// 65 | /// 66 | /// 67 | IFlowConfigurator AsVersion(string version); 68 | 69 | /// 70 | /// Sets the Flow Type name. 71 | /// 72 | /// 73 | /// 74 | IFlowConfigurator WithTypeName(string typeName); 75 | 76 | /// 77 | /// Sets the Flow to execute for a specific queue type and queue. 78 | /// 79 | /// 80 | /// 81 | /// 82 | IFlowConfigurator WithDefaultQueue(string queueType, string queueName = Defaults.DefaultQueueName); 83 | 84 | /// 85 | /// Adds the Cron Schedule trigger to the Flow's Cron Schedule trigger collection. 86 | /// 87 | /// 88 | /// 89 | IFlowConfigurator WithCronScheduleTrigger(ICronScheduleTrigger cronScheduleTrigger); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Didact.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.11.35327.3 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DidactCli", "didact-cli\DidactCli.csproj", "{421B0471-F9A7-4EEE-BCD5-07BFA4DD9E46}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DidactEngine", "didact-engine\DidactEngine.csproj", "{3001F9BD-13C0-4280-904D-451E818DFED9}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DidactUi", "didact-ui\dotnet-app\DidactUi.csproj", "{F5AFEEB0-F34C-4DA4-A8CD-F4B381B9AF1F}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DidactCore", "didact-core\DidactCore.csproj", "{41E0D8C9-8D73-4660-97D8-0FD4CEE4966D}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DidactServices", "didact-services\DidactServices.csproj", "{66E3BBF5-252F-45C5-8987-4C9F25522CEF}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DidactPrimitives", "didact-primitives\DidactPrimitives.csproj", "{070B01F0-78C3-4696-85D2-FA51B207E9B9}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {421B0471-F9A7-4EEE-BCD5-07BFA4DD9E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {421B0471-F9A7-4EEE-BCD5-07BFA4DD9E46}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {421B0471-F9A7-4EEE-BCD5-07BFA4DD9E46}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {421B0471-F9A7-4EEE-BCD5-07BFA4DD9E46}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {3001F9BD-13C0-4280-904D-451E818DFED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {3001F9BD-13C0-4280-904D-451E818DFED9}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {3001F9BD-13C0-4280-904D-451E818DFED9}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {3001F9BD-13C0-4280-904D-451E818DFED9}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {F5AFEEB0-F34C-4DA4-A8CD-F4B381B9AF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {F5AFEEB0-F34C-4DA4-A8CD-F4B381B9AF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {F5AFEEB0-F34C-4DA4-A8CD-F4B381B9AF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {F5AFEEB0-F34C-4DA4-A8CD-F4B381B9AF1F}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {41E0D8C9-8D73-4660-97D8-0FD4CEE4966D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {41E0D8C9-8D73-4660-97D8-0FD4CEE4966D}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {41E0D8C9-8D73-4660-97D8-0FD4CEE4966D}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {41E0D8C9-8D73-4660-97D8-0FD4CEE4966D}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {66E3BBF5-252F-45C5-8987-4C9F25522CEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {66E3BBF5-252F-45C5-8987-4C9F25522CEF}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {66E3BBF5-252F-45C5-8987-4C9F25522CEF}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {66E3BBF5-252F-45C5-8987-4C9F25522CEF}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {070B01F0-78C3-4696-85D2-FA51B207E9B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {070B01F0-78C3-4696-85D2-FA51B207E9B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {070B01F0-78C3-4696-85D2-FA51B207E9B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {070B01F0-78C3-4696-85D2-FA51B207E9B9}.Release|Any CPU.Build.0 = Release|Any CPU 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {88D98107-88FC-4281-9A70-42F7D8A0F152} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /src/didact-services/DataModel/Contexts/DidactDbContext.cs: -------------------------------------------------------------------------------- 1 | using DidactServices.DataModel.Entities; 2 | using Microsoft.Data.SqlClient; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace DidactServices.DataModel.Contexts 6 | { 7 | public partial class DidactDbContext : DbContext 8 | { 9 | public DidactDbContext() { } 10 | 11 | public DidactDbContext(DbContextOptions options) : base(options) { } 12 | 13 | public virtual DbSet StrictQueues { get; set; } = null!; 14 | 15 | public virtual DbSet StrictQueueItems { get; set; } = null!; 16 | 17 | public virtual DbSet Flows { get; set; } = null!; 18 | 19 | public virtual DbSet FlowRuns { get; set; } = null!; 20 | 21 | public virtual DbSet FlowSchedules { get; set; } = null!; 22 | 23 | public virtual DbSet Organizations { get; set; } = null!; 24 | 25 | public virtual DbSet ScheduleTypes { get; set; } = null!; 26 | 27 | public virtual DbSet HyperQueues { get; set; } = null!; 28 | 29 | public virtual DbSet HyperQueueItems { get; set; } = null!; 30 | 31 | public virtual DbSet States { get; set; } = null!; 32 | 33 | public virtual DbSet TriggerTypes { get; set; } = null!; 34 | 35 | public virtual DbSet ExecutionModes { get; set; } = null!; 36 | 37 | public virtual DbSet Engines { get; set; } = null!; 38 | 39 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 40 | { 41 | if (!optionsBuilder.IsConfigured) 42 | { 43 | var configuration = new ConfigurationBuilder() 44 | .AddUserSecrets(GetType().Assembly) 45 | .AddEnvironmentVariables() 46 | .Build(); 47 | 48 | var connectionString = configuration.GetConnectionString("Didact"); 49 | 50 | if (string.IsNullOrWhiteSpace(connectionString)) 51 | { 52 | throw new ArgumentNullException("A connection string was not found for the Didact database."); 53 | } 54 | 55 | var csBuilder = new SqlConnectionStringBuilder(connectionString) 56 | { 57 | ApplicationName = "Didact", 58 | PersistSecurityInfo = true, 59 | MultipleActiveResultSets = true, 60 | WorkstationID = System.Environment.MachineName, 61 | TrustServerCertificate = true 62 | }; 63 | 64 | var databaseProvider = configuration.GetSection("Didact").GetValue("DatabaseProvider"); 65 | switch (databaseProvider) 66 | { 67 | case "SqlServer": 68 | optionsBuilder.UseSqlServer(csBuilder.ConnectionString); 69 | break; 70 | case "PostgreSQL": 71 | //optionsBuilder.UsePostgreSQL 72 | default: 73 | optionsBuilder.UseSqlServer(csBuilder.ConnectionString); 74 | break; 75 | } 76 | } 77 | } 78 | 79 | protected override void OnModelCreating(ModelBuilder modelBuilder) 80 | { 81 | modelBuilder.ApplyConfiguration(new Configurations.StrictQueueConfiguration()); 82 | modelBuilder.ApplyConfiguration(new Configurations.StrictQueueItemConfiguration()); 83 | modelBuilder.ApplyConfiguration(new Configurations.FlowConfiguration()); 84 | modelBuilder.ApplyConfiguration(new Configurations.FlowRunConfiguration()); 85 | modelBuilder.ApplyConfiguration(new Configurations.FlowScheduleConfiguration()); 86 | modelBuilder.ApplyConfiguration(new Configurations.OrganizationConfiguration()); 87 | modelBuilder.ApplyConfiguration(new Configurations.ScheduleTypeConfiguration()); 88 | modelBuilder.ApplyConfiguration(new Configurations.HyperQueueConfiguration()); 89 | modelBuilder.ApplyConfiguration(new Configurations.HyperQueueItemConfiguration()); 90 | modelBuilder.ApplyConfiguration(new Configurations.TriggerTypeConfiguration()); 91 | modelBuilder.ApplyConfiguration(new Configurations.StateConfiguration()); 92 | modelBuilder.ApplyConfiguration(new Configurations.EngineConfiguration()); 93 | 94 | OnModelCreatingPartial(modelBuilder); 95 | } 96 | 97 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginContainers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DidactEngine.Plugins 6 | { 7 | public class PluginContainers : IPluginContainers 8 | { 9 | public ICollection PluginContainersCollection { get; set; } = []; 10 | 11 | public DateTime PluginContainersUpdatedAt { get; set; } 12 | 13 | public PluginContainers() { } 14 | 15 | public void SetPluginContainersUpdatedAt(DateTime? pluginContainersUpdatedAt) => 16 | PluginContainersUpdatedAt = pluginContainersUpdatedAt ?? DateTime.UtcNow; 17 | 18 | public IPluginContainer FindMatchingPluginContainer(Type type) 19 | { 20 | var assemblyFullName = type.Assembly.FullName; 21 | var matchingPluginContainers = PluginContainersCollection.Select(s => s) 22 | .Where(p => p.PluginAssemblyLoadContext.Assemblies.Select(a => a.FullName).Contains(assemblyFullName) 23 | && p.PluginAssemblyLoadContext.Assemblies.SelectMany(s => s.GetTypes()).Contains(type)) 24 | .ToList(); 25 | 26 | if (matchingPluginContainers.Count == 0) 27 | { 28 | throw new NoMatchedPluginException(); 29 | } 30 | if (matchingPluginContainers.Count > 1) 31 | { 32 | throw new MultipleMatchedPluginsException(); 33 | } 34 | 35 | return matchingPluginContainers.First(); 36 | } 37 | 38 | public IPluginContainer FindMatchingPluginContainer(PluginExecutionVersion pluginExecutionVersion) 39 | { 40 | var matchingPluginContainers = PluginContainersCollection.Select(s => s) 41 | .Where(p => p.PluginExecutionVersions.Contains(pluginExecutionVersion)) 42 | .ToList(); 43 | 44 | if (matchingPluginContainers.Count == 0) 45 | { 46 | throw new NoMatchedPluginException(); 47 | } 48 | 49 | if (matchingPluginContainers.Count > 1) 50 | { 51 | return matchingPluginContainers.OrderByDescending(p => p.PluginLoadedAt).First(); 52 | } 53 | 54 | return matchingPluginContainers.First(); 55 | } 56 | 57 | public IPluginContainer FindMatchingPluginContainer(string assemblyName, string assemblyVersion) 58 | { 59 | var matchingPluginContainers = PluginContainersCollection.Select(s => s) 60 | .Where(p => p.PluginAssemblyLoadContext.Assemblies.Select(a => a.GetName().Name).Contains(assemblyName) 61 | && p.PluginAssemblyLoadContext.Assemblies.Select(a => a.GetName().Version?.ToString() ?? string.Empty).Contains(assemblyVersion)) 62 | .ToList(); 63 | 64 | if (matchingPluginContainers.Count == 0) 65 | { 66 | throw new NoMatchedPluginException(); 67 | } 68 | if (matchingPluginContainers.Count > 1) 69 | { 70 | return matchingPluginContainers.OrderByDescending(p => p.PluginLoadedAt).First(); 71 | } 72 | 73 | return matchingPluginContainers.First(); 74 | } 75 | 76 | public IPluginContainer FindMatchingPluginContainer(string assemblyName, string assemblyVersion, string typeName) 77 | { 78 | var matchingPluginContainers = PluginContainersCollection.Select(s => s) 79 | .Where(p => p.PluginAssemblyLoadContext.Assemblies.Select(a => a.FullName).Contains(assemblyName) 80 | && p.PluginAssemblyLoadContext.Assemblies.Select(a => a.GetName().Version?.ToString() ?? string.Empty).Contains(assemblyVersion) 81 | && p.PluginAssemblyLoadContext.Assemblies.SelectMany(a => a.GetTypes()).Select(t => t.Name).Contains(typeName)) 82 | .ToList(); 83 | 84 | if (matchingPluginContainers.Count == 0) 85 | { 86 | throw new NoMatchedPluginException(); 87 | } 88 | 89 | if (matchingPluginContainers.Count > 1) 90 | { 91 | return matchingPluginContainers.OrderByDescending(p => p.PluginLoadedAt).First(); 92 | } 93 | 94 | return matchingPluginContainers.First(); 95 | } 96 | 97 | public void AddPluginContainer(IPluginContainer pluginContainer) 98 | { 99 | var matchedPluginContainers = PluginContainersCollection.Select(p => p).Where(p => p.Equals(pluginContainer)).ToList(); 100 | if (matchedPluginContainers.Count == 0) 101 | { 102 | PluginContainersCollection.Add(pluginContainer); 103 | } 104 | SetPluginContainersUpdatedAt(DateTime.UtcNow); 105 | } 106 | 107 | public void RemovePluginContainer(IPluginContainer pluginContainer) 108 | { 109 | var matchedPluginContainers = PluginContainersCollection.Select(p => p).Where(p => p.Equals(pluginContainer)).ToList(); 110 | if (matchedPluginContainers.Count > 0) 111 | { 112 | PluginContainersCollection.Remove(pluginContainer); 113 | } 114 | SetPluginContainersUpdatedAt(DateTime.UtcNow); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/didact-engine/Threading/DidactThreadpoolTaskScheduler.cs: -------------------------------------------------------------------------------- 1 | using DidactEngine.Threading; 2 | using System.Collections.Concurrent; 3 | 4 | namespace DidactCore.Threading 5 | { 6 | public class DidactThreadpoolTaskScheduler : TaskScheduler 7 | { 8 | private readonly ILogger _logger; 9 | 10 | private readonly ThreadpoolService _threadpoolService; 11 | 12 | private readonly CancellationToken _cancellationToken; 13 | 14 | private readonly ThreadLocal _currentThreadIsExecuting = new(false); 15 | 16 | private readonly ThreadLocal _currentThreadName = new(); 17 | 18 | private readonly long _threadCount; 19 | 20 | private readonly Thread[] _threads; 21 | 22 | private readonly BlockingCollection _tasks; 23 | 24 | /// 25 | /// 26 | /// Initializes a custom with a dedicated thread pool for fetching and executing FlowRuns. 27 | /// 28 | /// 29 | /// Remember that we have three essential performance parameters to balance: processor count, thread count, and task count. 30 | /// 31 | /// 32 | public DidactThreadpoolTaskScheduler(ILogger logger, ThreadpoolService threadpoolService) 33 | { 34 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 35 | _threadpoolService = threadpoolService ?? throw new ArgumentNullException(nameof(threadpoolService)); 36 | _cancellationToken = _threadpoolService.CancellationToken; 37 | _tasks = []; 38 | 39 | var threadCount = (long)Math.Ceiling(Environment.ProcessorCount * threadpoolService.ThreadFactor); 40 | _threadCount = threadCount <= 0 41 | ? throw new ArgumentOutOfRangeException(nameof(threadCount)) 42 | : threadCount; 43 | 44 | _threads = new Thread[_threadCount]; 45 | 46 | // Configure each thread 47 | for (int i = 0; i < _threadCount; i++) 48 | { 49 | _threads[i] = new Thread(() => ThreadExecutionLoop()) 50 | { 51 | IsBackground = true, 52 | // Might need to modify this later to include MachineName and/or ProcessId for distributed environments. 53 | // That should only matter if saving logs in persistent storage. Otherwise, we won't worry about it for now. 54 | Name = $"{nameof(DidactThreadpoolTaskScheduler)} Thread {i}" 55 | }; 56 | } 57 | 58 | // Start each thread 59 | _threads.ToList().ForEach(t => t.Start()); 60 | } 61 | 62 | private void ThreadExecutionLoop() 63 | { 64 | _currentThreadIsExecuting.Value = true; 65 | _currentThreadName.Value = Thread.CurrentThread.Name!; 66 | 67 | _logger.LogInformation("Initializing thread execution loop on {threadName}...", _currentThreadName.Value); 68 | 69 | while (true) 70 | { 71 | try 72 | { 73 | // Avoid busy waiting. 74 | foreach (var task in _tasks.GetConsumingEnumerable(_cancellationToken)) 75 | { 76 | TryExecuteTask(task); 77 | } 78 | } 79 | catch (OperationCanceledException) when (_cancellationToken.IsCancellationRequested) 80 | { 81 | // If the engine is shutting down, let the FlowRuns finish gracefully. 82 | // So we will swallow the exception here and not worry about it. 83 | _logger.LogWarning("{taskSchedulerName} cancellation detected on {threadName}. Stopping thread execution loop...", nameof(DidactThreadpoolTaskScheduler), _currentThreadName.Value); 84 | break; 85 | } 86 | catch (ThreadInterruptedException ex) 87 | { 88 | _logger.LogCritical("A {exName} occurred on thread {threadName}. See inner exception: {ex}", nameof(ThreadInterruptedException), _currentThreadName, ex); 89 | throw; 90 | } 91 | catch (Exception ex) 92 | { 93 | _logger.LogCritical("An unhandled exception occurred on thread {threadName}. See inner exception: {ex}", _currentThreadName, ex); 94 | throw; 95 | } 96 | } 97 | } 98 | 99 | protected sealed override void QueueTask(Task task) 100 | { 101 | _tasks.Add(task); 102 | } 103 | 104 | protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 105 | { 106 | // IMPORTANT: This was a difficult requirement that was not intuitive to me. 107 | // We have to PREVENT .NET ThreadPool threads from executing inline Tasks, so check if the CurrentThread is a Didact thread. 108 | // If it is not, stop it from executing by returning false. 109 | // Also, Microsoft generally recommends allowing for inline execution when using custom threads. 110 | // See https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler 111 | var isDidactThread = _currentThreadIsExecuting.Value; 112 | if (!isDidactThread) return false; 113 | 114 | // Prevent inlining for already-enqueued tasks. 115 | if (taskWasPreviouslyQueued) return false; 116 | 117 | return TryExecuteTask(task); 118 | } 119 | 120 | protected sealed override IEnumerable GetScheduledTasks() 121 | { 122 | return _tasks.ToArray(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/didact-engine/Services/BackgroundServices/WorkerBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Flows; 2 | using DidactEngine.TaskSchedulers; 3 | 4 | namespace DidactEngine.Services.BackgroundServices 5 | { 6 | public class WorkerBackgroundService : BackgroundService 7 | { 8 | private readonly ILogger _logger; 9 | private readonly IServiceProvider _serviceProvider; 10 | //private readonly IFlowRepository _flowRepository; 11 | //private readonly IFlowExecutor _flowExecutor; 12 | 13 | public WorkerBackgroundService(ILogger logger, IServiceProvider serviceProvider /*, IFlowRepository flowRepository, IFlowExecutor flowExecutor*/) 14 | { 15 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 16 | _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); 17 | //_flowRepository = flowRepository ?? throw new ArgumentNullException(nameof(flowRepository)); 18 | //_flowExecutor = flowExecutor ?? throw new ArgumentNullException(nameof(flowExecutor)); 19 | } 20 | 21 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 22 | { 23 | _logger.LogInformation("Starting {name}...", nameof(WorkerBackgroundService)); 24 | 25 | try 26 | { 27 | var taskList = new List(); 28 | var scheduler = ActivatorUtilities.CreateInstance(_serviceProvider, Environment.ProcessorCount); 29 | var taskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.AttachedToParent, scheduler); 30 | 31 | _logger.LogInformation("The logical processor count is: {count}", Environment.ProcessorCount); 32 | 33 | for (int i = 0; i < 1; i++) 34 | { 35 | var workerTaskName = string.Concat("Worker Task ", i + 1); 36 | var workerTask = Task.Factory.StartNew(async () => 37 | { 38 | while (!stoppingToken.IsCancellationRequested) 39 | { 40 | /* Use taskFactory to create a second factory task inside the first factory task. 41 | * Here is why: 42 | * If a Flow uses ConfigureAwait(false) at any point, then when we execute the Flow, we lose our custom task scheduler and all of our custom threads until that chain of tasks completes. 43 | * We need a way to GUARANTEE that the next Flow STARTS BACK ONTO our custom task scheduler and our custom threads. 44 | * So we await the inner task, let it complete, and then start a new one for the next Flow execution. 45 | * ============================================================================ 46 | * Think of this as an asynchronous context RESET between Flow executions. 47 | * ============================================================================ 48 | * This is done infinitely inside of the first factory task's while loop, so it's an infinite parent task that continues Flow executions. */ 49 | 50 | /* Actual Flow steps: 51 | * 1. Get the Flow from a Queue and FlowRun. 52 | * 2. Create a Flow instance from the Flow. 53 | * 3. Execute the Flow instance. 54 | * */ 55 | 56 | await LogThreadPoolMetrics(workerTaskName, scheduler); 57 | } 58 | }, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap(); 59 | 60 | _logger.LogInformation("Adding {name} to taskList", workerTaskName); 61 | taskList.Add(workerTask); 62 | } 63 | 64 | await Task.WhenAll(taskList); 65 | } 66 | catch (Exception ex) 67 | { 68 | _logger.LogCritical("{name} failed with the following exception:{nl}{exception}", 69 | nameof(WorkerBackgroundService), Environment.NewLine, ex); 70 | throw; 71 | } 72 | } 73 | 74 | public async Task LogThreadPoolMetrics(string workerTaskName, TaskScheduler scheduler) 75 | { 76 | _logger.LogInformation("{wtk} | Task heartbeat 1. | threadName: {threadName} | isThreadPoolThread: {tpt} | scheduler: {scheduler}", 77 | workerTaskName, Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread, TaskScheduler.Current); 78 | 79 | await Task.Delay(3000); 80 | 81 | _logger.LogInformation("{wtk} | Task heartbeat 2. | threadName: {threadName} | isThreadPoolThread: {tpt} | scheduler: {scheduler}", 82 | workerTaskName, Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread, TaskScheduler.Current); 83 | 84 | await Task.Factory.StartNew(async () => 85 | { 86 | await Task.Delay(3000).ConfigureAwait(false); 87 | }, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap(); 88 | 89 | _logger.LogInformation("{wtk} | Task heartbeat 3. | threadName: {threadName} | isThreadPoolThread: {tpt} | scheduler: {scheduler}", 90 | workerTaskName, Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread, TaskScheduler.Current); 91 | 92 | await Task.Factory.StartNew(async () => 93 | { 94 | await Task.Delay(3000).ConfigureAwait(false); 95 | }, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap(); 96 | 97 | _logger.LogInformation("{wtk} | Task heartbeat 4. | threadName: {threadName} | isThreadPoolThread: {tpt} | scheduler: {scheduler}", 98 | workerTaskName, Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread, TaskScheduler.Current); 99 | 100 | await Task.Delay(3000); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/didact-ui/dotnet-app/Program.cs: -------------------------------------------------------------------------------- 1 | using DidactServices.Constants; 2 | using DidactServices.HostAppEnvironments; 3 | using DidactUi.Constants; 4 | using DidactUi.Exceptions; 5 | using DidactUi.Services; 6 | using Microsoft.Extensions.FileProviders; 7 | using Spectre.Console; 8 | using System.Diagnostics; 9 | using System.Reflection; 10 | 11 | #region App metadata 12 | 13 | var applicationName = Constants.ApplicationNames.DidactUi; 14 | var themeColor = new Color(249, 115, 22); 15 | var assembly = Assembly.GetExecutingAssembly(); 16 | var assemblyName = assembly.GetName().Name; 17 | 18 | /* There is a distinction between the normal dotnet environment designation and the Didact build environment. 19 | * If the build environment is Production, then we assume this app is a tested and released version, 20 | * meaning end users should ONLY use production settings for Didact Platform. 21 | * However, if the build environment is NOT Production, then we assume this app is currently under development by Didact's maintainer, 22 | * meaning that we DO want to use the Development or Staging environment settings. 23 | */ 24 | var buildEnvironment = HostAppEnvironmentService.GetBuildEnvironment(); 25 | var hostAppEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); 26 | var appsettingsEnvironment = HostAppEnvironmentService.GetDynamicHostAppEnvironment(buildEnvironment, hostAppEnvironment); 27 | 28 | var settingsFilename = Constants.ApplicationConfigurationFileNames.DidactUiSettings; 29 | var urls = Environment.GetEnvironmentVariable("ASPNETCORE_URLS"); 30 | var urlsSplit = string.IsNullOrEmpty(urls) ? [] : urls.Split(';'); 31 | var consoleUrls = string.Empty; 32 | foreach (var url in urlsSplit) 33 | { 34 | var separator = string.IsNullOrEmpty(consoleUrls) ? string.Empty : Environment.NewLine; 35 | consoleUrls = string.Concat(consoleUrls, separator, url); 36 | } 37 | 38 | #endregion 39 | 40 | #region Configure Spectre Console decorator. 41 | 42 | var figletText = new FigletText(applicationName).LeftJustified().Color(themeColor); 43 | AnsiConsole.Write(figletText); 44 | 45 | // Create a table 46 | var table = new Table().HideHeaders(); 47 | table.AddColumn(""); 48 | table.AddColumn(new TableColumn("")); 49 | table.AddRow("Name", applicationName); 50 | table.AddRow("Version", assembly.GetName().Version!.ToString()); 51 | table.AddRow("Build Environment", buildEnvironment); 52 | table.AddRow("Start time", DateTime.UtcNow.ToString("O")); 53 | table.AddRow("Process Id", Environment.ProcessId.ToString()); 54 | table.AddRow("OS version", Environment.OSVersion.ToString()); 55 | table.AddRow("Machine name", Environment.MachineName); 56 | table.AddRow("Username", Environment.UserName); 57 | table.AddRow("Environment", hostAppEnvironment ?? string.Empty); 58 | if (!string.IsNullOrEmpty(consoleUrls)) 59 | table.AddRow("Dashboard", consoleUrls); 60 | table.BorderStyle(new Style(themeColor)); 61 | 62 | var padder = new Padder(table).PadBottom(1).PadTop(0); 63 | 64 | var grid = new Grid(); 65 | grid.AddColumn(); 66 | grid.AddRow(padder); 67 | 68 | AnsiConsole.Write(grid); 69 | 70 | #endregion 71 | 72 | var builder = WebApplication.CreateBuilder(args); 73 | 74 | #region Read appsettings.json as an embedded resource. 75 | 76 | // Support multi-environment appsettings files. 77 | var resourceFileName = string.IsNullOrEmpty(appsettingsEnvironment) 78 | ? $"{assemblyName}.appsettings.json" 79 | : $"{assemblyName}.appsettings.{appsettingsEnvironment}.json"; 80 | 81 | // Fetch the appsettings.json file as an embedded resource. 82 | var stream = assembly.GetManifestResourceStream(resourceFileName); 83 | var reader = new StreamReader(stream!); 84 | var json = reader.ReadToEnd(); 85 | 86 | // Create a new IConfiguration. 87 | var iConfiguration = new ConfigurationBuilder() 88 | .AddJsonStream(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json))) 89 | .Build(); 90 | 91 | #endregion 92 | 93 | #region Read uisettings.json as a runtime IConfiguration. 94 | 95 | // Get the real EXE directory 96 | var exePath = Process.GetCurrentProcess().MainModule?.FileName; 97 | var exeDirectory = Path.GetDirectoryName(exePath)!; 98 | 99 | // Define the path to UiSettings.json 100 | var uiSettingsPath = Path.Combine(exeDirectory, settingsFilename); 101 | 102 | // Build configuration 103 | var uiSettingsIConfiguration = new ConfigurationBuilder() 104 | .SetBasePath(exeDirectory) 105 | .AddJsonFile(uiSettingsPath, optional: true, reloadOnChange: true) 106 | .Build(); 107 | 108 | var uiSettings = new UiSettings(); 109 | uiSettingsIConfiguration.Bind(uiSettings); 110 | builder.Services.AddSingleton(uiSettings); 111 | 112 | #endregion 113 | 114 | #region Add CORS policy for local dev only for the Nuxt dev server. 115 | 116 | builder.Services.AddCors(options => 117 | { 118 | options.AddPolicy(name: UiConstants.CorsPolicyNames.Development, 119 | policy => 120 | { 121 | // Get the Nuxt dev server base url. 122 | var nuxtDevServerUrl = iConfiguration.GetValue("NuxtDevServerUrl"); 123 | 124 | if (string.IsNullOrEmpty(nuxtDevServerUrl)) 125 | throw new MissingEnvironmentVariableException("The 'NuxtDevServerUrl' environment variable is missing from the embedded appsettings.json file. Please include this environment variable."); 126 | 127 | policy.WithOrigins(nuxtDevServerUrl); 128 | policy.AllowAnyMethod(); 129 | policy.AllowAnyHeader(); 130 | }); 131 | }); 132 | 133 | #endregion 134 | 135 | // Add services to the container. 136 | 137 | builder.Services.AddControllers(); 138 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 139 | builder.Services.AddEndpointsApiExplorer(); 140 | builder.Services.AddSwaggerGen(); 141 | 142 | var app = builder.Build(); 143 | 144 | // Configure the HTTP request pipeline. 145 | if (app.Environment.IsDevelopment()) 146 | { 147 | app.UseSwagger(); 148 | app.UseSwaggerUI(); 149 | // Add CORS policy for local dev only for the Nuxt dev server. 150 | app.UseCors(UiConstants.CorsPolicyNames.Development); 151 | } 152 | 153 | app.UseHttpsRedirection(); 154 | app.MapControllers(); 155 | 156 | // Use an embedded file provider for the embedded wwwroot folder. 157 | var embeddedFileProvider = new ManifestEmbeddedFileProvider(assembly, "wwwroot"); 158 | app.UseDefaultFiles(new DefaultFilesOptions 159 | { 160 | FileProvider = embeddedFileProvider 161 | }); 162 | app.UseStaticFiles(new StaticFileOptions 163 | { 164 | FileProvider = embeddedFileProvider, 165 | RequestPath = string.Empty 166 | }); 167 | 168 | app.Run(); -------------------------------------------------------------------------------- /src/didact-cli/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | using DidactCli.Commands; 3 | using DidactCli.Services; 4 | using DidactServices.Constants; 5 | using DidactServices.HostAppEnvironments; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using Spectre.Console; 10 | using Spectre.Console.Cli; 11 | using System.Reflection; 12 | 13 | #region App metadata 14 | 15 | var applicationName = Constants.ApplicationNames.DidactCli; 16 | var themeColor = new Color(249, 115, 22); 17 | var assembly = Assembly.GetExecutingAssembly(); 18 | var assemblyName = assembly.GetName().Name; 19 | 20 | /* There is a distinction between the normal dotnet environment designation and the Didact build environment. 21 | * If the build environment is Production, then we assume this app is a tested and released version, 22 | * meaning end users should ONLY use production settings for Didact Platform. 23 | * However, if the build environment is NOT Production, then we assume this app is currently under development by Didact's maintainer, 24 | * meaning that we DO want to use the Development or Staging environment settings. 25 | */ 26 | var buildEnvironment = HostAppEnvironmentService.GetBuildEnvironment(); 27 | var hostAppEnvironment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"); 28 | var appsettingsEnvironment = HostAppEnvironmentService.GetDynamicHostAppEnvironment(buildEnvironment, hostAppEnvironment); 29 | 30 | #endregion 31 | 32 | #region Configure Spectre Console decorator. 33 | 34 | var figletText = new FigletText(applicationName).LeftJustified().Color(themeColor); 35 | AnsiConsole.Write(figletText); 36 | 37 | // Create a table 38 | var table = new Table().HideHeaders(); 39 | table.AddColumn(""); 40 | table.AddColumn(new TableColumn("")); 41 | table.AddRow("Name", applicationName); 42 | table.AddRow("Version", assembly.GetName().Version!.ToString()); 43 | table.AddRow("Build Environment", buildEnvironment); 44 | table.AddRow("Start time", DateTime.UtcNow.ToString("O")); 45 | table.AddRow("Process Id", Environment.ProcessId.ToString()); 46 | table.AddRow("OS version", Environment.OSVersion.ToString()); 47 | table.AddRow("Machine name", Environment.MachineName); 48 | table.AddRow("Username", Environment.UserName); 49 | table.AddRow("Environment", hostAppEnvironment ?? string.Empty); 50 | table.BorderStyle(new Style(themeColor)); 51 | 52 | var padder = new Padder(table).PadBottom(1).PadTop(0); 53 | 54 | var grid = new Grid(); 55 | grid.AddColumn(); 56 | grid.AddRow(padder); 57 | 58 | AnsiConsole.Write(grid); 59 | 60 | #endregion 61 | 62 | #region Read appsettings.json as an embedded resource. 63 | 64 | // Support multi-environment appsettings files. 65 | var resourceFileName = string.IsNullOrEmpty(appsettingsEnvironment) 66 | ? $"{assemblyName}.appsettings.json" 67 | : $"{assemblyName}.appsettings.{appsettingsEnvironment}.json"; 68 | 69 | // Fetch the appsettings.json file as an embedded resource. 70 | var stream = assembly.GetManifestResourceStream(resourceFileName); 71 | var reader = new StreamReader(stream!); 72 | var json = reader.ReadToEnd(); 73 | 74 | // Create a new IConfiguration. 75 | var iConfiguration = new ConfigurationBuilder() 76 | .AddJsonStream(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json))) 77 | .Build(); 78 | 79 | #endregion 80 | 81 | #region Setup dependency injection and other bootstrapping utilities. 82 | 83 | // Bind to the appsettings class. 84 | var appSettings = new AppSettings(); 85 | iConfiguration.Bind(appSettings); 86 | 87 | var services = new ServiceCollection(); 88 | services.AddSingleton(iConfiguration); 89 | services.AddSingleton(appSettings); 90 | services.AddLogging(configure => 91 | { 92 | configure.AddConsole(); 93 | }); 94 | var registrar = new TypeRegistrar(services); 95 | var app = new CommandApp(registrar); 96 | 97 | #endregion 98 | 99 | app.Configure(config => 100 | { 101 | config.SetApplicationName("Didact Cli"); 102 | config.AddCommand("version") 103 | .WithDescription("The version of Didact Cli."); 104 | 105 | config.AddBranch("engine", engine => 106 | { 107 | engine.AddCommand("install") // run "didact engine set" after installation? --set-after-install true ? 108 | .WithDescription("Installs Didact Engine to the target location."); 109 | engine.AddCommand("set") 110 | .WithDescription("Sets the target Didact Engine."); 111 | engine.AddCommand("get") 112 | .WithDescription("Gets the target Didact Engine."); 113 | engine.AddBranch("config", config => 114 | { 115 | config.AddCommand("generate") // --open true option for autoopening config file. // --with-file "" option for using existing config file. 116 | .WithDescription("Generates a new runtime environment variables file for Didact Engine."); 117 | config.AddCommand("get") 118 | .WithDescription("Gets the runtime environment variables file for Didact Engine."); 119 | }); 120 | }); 121 | 122 | config.AddBranch("ui", ui => 123 | { 124 | ui.AddCommand("install") 125 | .WithDescription("Installs Didact UI to the target location."); 126 | ui.AddCommand("set") 127 | .WithDescription("Sets the target Didact UI."); 128 | ui.AddCommand("get") 129 | .WithDescription("Gets the target Didact UI."); 130 | ui.AddBranch("config", config => 131 | { 132 | config.AddCommand("generate") // --open true option for autoopening config file. 133 | .WithDescription("Generates a new runtime environment variables file for Didact UI."); 134 | config.AddCommand("get") 135 | .WithDescription("Gets the runtime environment variables file for Didact UI."); 136 | }); 137 | }); 138 | 139 | config.AddBranch("license", license => 140 | { 141 | license.AddCommand("set") 142 | .WithDescription("Sets the Didact license key."); 143 | license.AddCommand("get") 144 | .WithDescription("Gets the Didact license key."); 145 | license.AddCommand("authenticate") 146 | .WithDescription("Authenticates the Didact license key."); 147 | license.AddCommand("validate") 148 | .WithDescription("Validates the Didact license key."); 149 | }); 150 | 151 | config.AddBranch("library", library => 152 | { 153 | library.AddCommand("list") 154 | .WithDescription("Lists the registered Didact flow libraries."); 155 | library.AddCommand("add") 156 | .WithDescription("Adds a Didact flow library to the registry."); 157 | library.AddCommand("remove") 158 | .WithDescription("Removes a Didact flow library to the registry."); 159 | library.AddCommand("deploy") 160 | .WithDescription("Deploys all flow libraries in the registry."); 161 | }); 162 | }); 163 | 164 | return app.Run(args); -------------------------------------------------------------------------------- /src/didact-engine/Plugins/PluginContainer.cs: -------------------------------------------------------------------------------- 1 | using DidactCore.Constants; 2 | using DidactCore.Flows; 3 | using DidactCore.Plugins; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Threading.Tasks; 9 | 10 | namespace DidactEngine.Plugins 11 | { 12 | public class PluginContainer : IPluginContainer 13 | { 14 | public PluginAssemblyLoadContext? PluginAssemblyLoadContext { get; set; } 15 | 16 | public ICollection PluginExecutionVersions { get; set; } = []; 17 | 18 | public DateTime? PluginLoadedAt { get; set; } 19 | 20 | public DateTime? LastExecution { get; set; } 21 | 22 | public IPluginDependencyInjector PluginDependencyInjector { get; set; } 23 | 24 | public PluginContainer(IPluginDependencyInjector pluginDependencyInjector) 25 | { 26 | PluginDependencyInjector = pluginDependencyInjector; 27 | } 28 | 29 | // TODO Implement custom exception 30 | public IEnumerable GetAssemblies() => PluginAssemblyLoadContext?.Assemblies ?? throw new Exception("Missing ALC!"); 31 | 32 | public void SetPluginLoadedAt(DateTime? pluginLoadedAt) => PluginLoadedAt = pluginLoadedAt ?? DateTime.UtcNow; 33 | 34 | public void ConfigureDependencyInjection() 35 | { 36 | var registrarType = GetAssemblies() 37 | .SelectMany(s => s.GetTypes()) 38 | .Where(t => t.GetInterfaces().Contains(typeof(IPluginRegistrar)) && t.IsClass && !t.IsAbstract) 39 | .SingleOrDefault(); 40 | 41 | if (registrarType is null) 42 | { 43 | // TODO throw a special exception here. 44 | throw new Exception("The plugin is missing a dependency injection registrar."); 45 | } 46 | 47 | var registrar = Activator.CreateInstance(registrarType) as IPluginRegistrar; 48 | 49 | if (registrar is null) 50 | { 51 | // TODO throw a special exception here. 52 | throw new Exception("The plugin registrar could not be instantiated."); 53 | } 54 | 55 | var pluginServiceCollection = registrar.CreateServiceCollection(); 56 | PluginDependencyInjector.AddAndRebuildServiceCollection(pluginServiceCollection); 57 | } 58 | 59 | public async Task CollectPluginExecutionVersionsAsync() 60 | { 61 | var flowConfigurators = new List(); 62 | var flowTypes = GetAssemblies() 63 | .SelectMany(s => s.GetTypes()) 64 | .Where(t => t.GetInterfaces().Contains(typeof(IFlow)) && t.IsClass && !t.IsAbstract); 65 | 66 | foreach (var flowType in flowTypes) 67 | { 68 | var iflow = PluginDependencyInjector.CreateInstance(flowType) as IFlow; 69 | if (iflow is null) 70 | { 71 | // TODO throw exception 72 | continue; 73 | } 74 | 75 | var newFlowConfigurator = PluginDependencyInjector.CreateInstance(); 76 | var flowConfigurator = await iflow.ConfigureAsync(newFlowConfigurator); 77 | 78 | var flowTypeName = flowConfigurator.TypeName; 79 | var flowVersion = flowConfigurator.Version; 80 | var assemblyName = flowType.Assembly.GetName().Name; 81 | var assemblyVersion = flowType.Assembly.GetName().Version?.ToString() ?? "Unknown"; 82 | 83 | var pluginExecutionVersion = new PluginExecutionVersion(flowTypeName, flowVersion, assemblyName, assemblyVersion); 84 | PluginExecutionVersions.Add(pluginExecutionVersion); 85 | } 86 | } 87 | 88 | public async Task ConfigureFlowsAsync() 89 | { 90 | var flowConfigurators = new List(); 91 | var flowTypes = GetAssemblies() 92 | .SelectMany(s => s.GetTypes()) 93 | .Where(t => t.GetInterfaces().Contains(typeof(IFlow)) && t.IsClass && !t.IsAbstract); 94 | 95 | // Configurator part 1: instantiate the Flows. 96 | foreach (var flowType in flowTypes) 97 | { 98 | var iflow = PluginDependencyInjector.CreateInstance(flowType) as IFlow; 99 | 100 | if (iflow is null) 101 | { 102 | var exception = new Exception( 103 | $"The Flow type {flowType.Name} could not be instantiated with dependency injection during Flow configuration."); 104 | var notInstantiatedFlowConfigurator = new FlowConfiguratorDto() 105 | { 106 | FlowType = flowType, 107 | State = FlowConfiguratorStates.FlowInstantiationFailed, 108 | Exception = exception 109 | }; 110 | flowConfigurators.Add(notInstantiatedFlowConfigurator); 111 | continue; 112 | } 113 | 114 | var instantiatedFlowConfigurator = new FlowConfiguratorDto() 115 | { 116 | FlowType = flowType, 117 | State = FlowConfiguratorStates.FlowInstantiationSuccessful, 118 | FlowInstance = iflow 119 | }; 120 | flowConfigurators.Add(instantiatedFlowConfigurator); 121 | } 122 | 123 | // Configurator part 2: execute the configuration functions. 124 | foreach (var flowConfiguratorDto in flowConfigurators.Where(c => c.State == FlowConfiguratorStates.FlowInstantiationSuccessful)) 125 | { 126 | try 127 | { 128 | var newFlowConfigurator = PluginDependencyInjector.CreateInstance(); 129 | var iFlowConfigurator = await flowConfiguratorDto.FlowInstance!.ConfigureAsync(newFlowConfigurator); 130 | //await _flowRepository.SaveConfigurationsAsync(iFlowConfigurator); 131 | flowConfiguratorDto.State = FlowConfiguratorStates.FlowConfigurationSuccessful; 132 | } 133 | catch (Exception ex) 134 | { 135 | var exception = new Exception( 136 | $"The Flow configurator for Flow type {flowConfiguratorDto.FlowType.Name} has failed. See inner exception.", ex); 137 | flowConfiguratorDto.Exception = exception; 138 | flowConfiguratorDto.State = FlowConfiguratorStates.FlowConfigurationFailed; 139 | } 140 | } 141 | 142 | foreach (var flowConfigurator in flowConfigurators.Where(c => c.State == FlowConfiguratorStates.FlowInstantiationFailed)) 143 | { 144 | // TODO Handle failed flow instantiations. 145 | } 146 | 147 | foreach (var flowConfigurator in flowConfigurators.Where(c => c.State == FlowConfiguratorStates.FlowConfigurationFailed)) 148 | { 149 | // TODO Handle failed flow configurations. 150 | } 151 | 152 | foreach (var flowConfigurator in flowConfigurators.Where(c => c.State == FlowConfiguratorStates.FlowConfigurationSuccessful)) 153 | { 154 | // TODO Handle successful flow configurators. 155 | } 156 | } 157 | } 158 | } 159 | --------------------------------------------------------------------------------