├── .cursor └── rules │ ├── code-practices.mdc │ ├── flow.mdc │ └── test-practices.mdc ├── .github └── workflows │ ├── myget-publish.yml │ └── test-with-code-coverage.yml ├── .gitignore ├── CodeCoverage.runsettings ├── Directory.Build.props ├── Directory.Packages.props ├── LICENSE ├── README.md ├── aevatar-framework.sln ├── ai └── project_tracker.md ├── codecov.yml ├── common.props ├── docs ├── Aevatar.Core.md ├── Aevatar.EventSourcing.Core.md ├── Aevatar.PermissionManagement.md ├── Aevatar.Plugins.md ├── DIRECTORY_STRUCTURE.md └── MODULE_DOCUMENTATION.md ├── samples ├── ArtifactGAgent │ ├── ArtifactGAgent.Client │ │ ├── ArtifactGAgent.Client.csproj │ │ └── Program.cs │ └── ArtifactGAgent.Silo │ │ ├── ArtifactGAgent.Silo.csproj │ │ ├── Program.cs │ │ └── appsettings.json ├── BroadCastGAgentDemo │ ├── BroadCastGAgentDemo.csproj │ └── Program.cs ├── MessagingGAgent.Client │ ├── MessagingGAgent.Client.csproj │ └── Program.cs ├── MessagingGAgent.Grains │ ├── Agents │ │ ├── Events │ │ │ ├── MessagingEvent.cs │ │ │ └── SendEvent.cs │ │ ├── Group │ │ │ ├── GroupAgentState.cs │ │ │ ├── GroupGAgent.cs │ │ │ └── GroupStateLogEvent.cs │ │ └── Messaging │ │ │ ├── DemoGAgent.cs │ │ │ ├── MessagingGAgent.cs │ │ │ ├── MessagingGState.cs │ │ │ ├── MessagingStateLogEvent.cs │ │ │ └── SpecializedGrain.cs │ └── MessagingGAgent.Grains.csproj ├── MessagingGAgent.Silo │ ├── MessagingGAgent.Silo.csproj │ └── Program.cs ├── PluginGAgent │ ├── PluginGAgent.Client │ │ ├── PluginGAgent.Client.csproj │ │ ├── Program.cs │ │ ├── appsettings.json │ │ └── plugins │ │ │ ├── Aevatar.dll │ │ │ ├── PluginGAgent.Grains.dll │ │ │ ├── PluginGAgent.GrainsDup.dll │ │ │ └── RestSharp.dll │ ├── PluginGAgent.Grains │ │ ├── CommanderGAgent.cs │ │ ├── PluginGAgent.Grains.csproj │ │ └── WorkerGAgent.cs │ ├── PluginGAgent.GrainsDup │ │ ├── CommanderGAgent.cs │ │ ├── PluginGAgent.GrainsDup.csproj │ │ └── WorkerGAgent.cs │ └── PluginGAgent.Silo │ │ ├── Extensions │ │ └── OrleansHostExtension.cs │ │ ├── PluginGAgent.Silo.csproj │ │ ├── PluginGAgentTestHostedService.cs │ │ ├── PluginGAgentTestModule.cs │ │ ├── Program.cs │ │ └── appsettings.json ├── PubSubDemoWithoutGroup │ ├── Program.cs │ └── PubSubDemoWithoutGroup.csproj └── SiloNamePatternPlacementExamples │ ├── Program.cs │ └── SiloNamePatternPlacementExamples.csproj ├── src ├── Aevatar.Core.Abstractions │ ├── Aevatar.Core.Abstractions.csproj │ ├── AevatarCoreConstants.cs │ ├── AevatarOptions.cs │ ├── Application │ │ ├── BroadCastGState.cs │ │ ├── RequestAllDescriptionsEvent.cs │ │ └── SubscribedEventListEvent.cs │ ├── ConfigurationBase.cs │ ├── EventHandlerExceptionEvent.cs │ ├── Events │ │ ├── EventBase.cs │ │ ├── EventWithResponseBase.cs │ │ ├── EventWrapper.cs │ │ └── EventWrapperBase.cs │ ├── Exceptions │ │ ├── ArtifactGAgentException.cs │ │ ├── EventHandlingException.cs │ │ ├── EventPublishingException.cs │ │ └── StateTransitionException.cs │ ├── Extensions │ │ ├── AssemblyExtensions.cs │ │ └── StringExtensions.cs │ ├── IArtifact.cs │ ├── IArtifactGAgent.cs │ ├── IEventDispatcher.cs │ ├── IExtGAgent.cs │ ├── IGAgentFactory.cs │ ├── IGAgentManager.cs │ ├── IPublishingGAgent.cs │ ├── IStateAgent.cs │ ├── IStateDispatcher.cs │ ├── IStateProjector.cs │ ├── Infrastructure │ │ ├── AllEventHandlerAttribute.cs │ │ ├── EventHandlerAttribute.cs │ │ ├── GAgentAttribute.cs │ │ ├── StateHandlerAttribute.cs │ │ └── StateLogEventHandlerAttribute.cs │ ├── Plugin │ │ ├── AddExistedPluginDto.cs │ │ ├── AddPluginDto.cs │ │ ├── IPluginGAgentManager.cs │ │ ├── PluginLoadStatus.cs │ │ ├── PluginsInformation.cs │ │ ├── RemovePluginDto.cs │ │ └── UpdatePluginDto.cs │ ├── Projections │ │ └── IProjectionGrain.cs │ ├── StateBase.cs │ ├── StateLogEventBase.cs │ ├── StateWrapper.cs │ ├── StateWrapperBase.cs │ └── SyncWorker │ │ └── IAevatarSyncWorker.cs ├── Aevatar.Core │ ├── Aevatar.Core.csproj │ ├── AevatarGAgentConstants.cs │ ├── AevatarGrainActivator.cs │ ├── AevatarSyncWorker.cs │ ├── ArtifactGAgent.cs │ ├── AsyncTaskRunner.cs │ ├── BroadCastGAgentBase.cs │ ├── EventWrapperBaseAsyncObserver.cs │ ├── EventWrapperHelper.cs │ ├── Extensions │ │ ├── MethodInfoExtensions.cs │ │ └── ReflectionExtensions.cs │ ├── GAgentAsyncObserver.cs │ ├── GAgentBase.Observers.cs │ ├── GAgentBase.Publish.cs │ ├── GAgentBase.Subscribe.cs │ ├── GAgentBase.SyncWorker.cs │ ├── GAgentBase.cs │ ├── GAgentBaseExceptionEvent.cs │ ├── GAgentFactory.cs │ ├── GAgentManager.cs │ ├── Observability │ │ ├── ActivityHelper.cs │ │ ├── OpenTelemetryConstants.cs │ │ └── OpenTelemetryScope.cs │ ├── Placement │ │ └── SiloNamePatternPlacement.cs │ ├── Projections │ │ └── StateProjectionGrain.cs │ ├── PublishingGAgent.cs │ ├── StateBaseAsyncObserver.cs │ ├── StateDispatcher.cs │ ├── StateProjectionAsyncObserver.cs │ └── StateProjectionGAgentBase.cs ├── Aevatar.EventSourcing.Core │ ├── Aevatar.EventSourcing.Core.csproj │ ├── AevatarEventSourcingConstants.cs │ ├── Exceptions │ │ ├── ReadFromLogStorageFailed.cs │ │ ├── ReadFromSnapshotStorageFailed.cs │ │ ├── UpdateLogStorageFailed.cs │ │ └── UpdateSnapshotStorageFailed.cs │ ├── Hosting │ │ ├── InMemoryStorageServiceCollectionExtensions.cs │ │ └── InMemoryStorageSiloBuilderExtensions.cs │ ├── InMemoryLogConsistentStorage.cs │ ├── InMemoryLogConsistentStorageFactory.cs │ ├── LogConsistency │ │ └── DefaultProtocolServices.cs │ ├── Snapshot │ │ ├── ViewStateSnapshot.cs │ │ └── ViewStateSnapshotWithMetadata.cs │ └── Storage │ │ ├── ILogConsistentStorage.cs │ │ ├── LogConsistencyProvider.cs │ │ ├── LogConsistencyProviderFactory.cs │ │ ├── LogViewAdaptor.EventSourcing.cs │ │ └── LogViewAdaptor.Notifications.cs ├── Aevatar.EventSourcing.MongoDB │ ├── Aevatar.EventSourcing.MongoDB.csproj │ ├── Configuration │ │ └── MongoDBGrainStorageConfigurator.cs │ ├── GrainTypeBsonSerializer.cs │ ├── Hosting │ │ ├── MongoDbStorageServiceCollectionExtensions.cs │ │ └── MongoDbStorageSiloBuilderExtensions.cs │ ├── IdSpanBsonSerializer.cs │ ├── MongoDbLogConsistentStorage.cs │ ├── MongoDbLogConsistentStorageFactory.cs │ ├── MongoDbStorageException.cs │ ├── Options │ │ ├── MongoDbStorageOptions.cs │ │ └── MongoDbStorageOptionsValidator.cs │ └── Serializers │ │ └── BsonGrainSerializer.cs ├── Aevatar.PermissionManagement │ ├── Aevatar.PermissionManagement.csproj │ ├── AevatarPermissionManagementModule.cs │ ├── Extensions │ │ └── OrleansHostExtensions.cs │ ├── GAgentPermissionHelper.cs │ ├── IPermissionInfoProvider.cs │ ├── PermissionAttribute.cs │ ├── PermissionCheckFilter.cs │ ├── PermissionGAgentBase.cs │ ├── PermissionInfo.cs │ ├── PermissionStateBase.cs │ └── UserContext.cs ├── Aevatar.Plugins │ ├── Aevatar.Plugins.csproj │ ├── AevatarPluginsModule.cs │ ├── DbContexts │ │ ├── AevatarMongoDbContextBase.cs │ │ ├── PluginCodeStorageMongoDbContext.cs │ │ ├── PluginLoadStatusMongoDbContext.cs │ │ └── TenantPluginCodeMongoDbContext.cs │ ├── Entities │ │ ├── PluginCodeStorage.cs │ │ ├── PluginLoadStatus.cs │ │ └── TenantPluginCode.cs │ ├── Extensions │ │ └── AbpApplicationExtensions.cs │ ├── GAgents │ │ ├── PluginCodeStorageGAgent.cs │ │ └── TenantPluginCodeGAgent.cs │ ├── PluginGAgentLoadOptions.cs │ ├── PluginGAgentManager.cs │ ├── PluginLoader.cs │ └── Repositories │ │ ├── IPluginCodeStorageRepository.cs │ │ ├── IPluginLoadStatusRepository.cs │ │ ├── ITenantPluginCodeRepository.cs │ │ ├── PluginCodeStorageRepository.cs │ │ ├── PluginLoadStatusRepository.cs │ │ └── TenantPluginCodeRepository.cs ├── Aevatar.TestKit │ ├── Aevatar.TestKit.csproj │ ├── DefaultTestKitBase.cs │ ├── EventSourcing │ │ ├── TestCloner.cs │ │ ├── TestCodec.cs │ │ ├── TestCodecProvider.cs │ │ ├── TestLogConsistencyProtocolServices.cs │ │ ├── TestLogConsistencyProvider.cs │ │ └── TestLogViewAdaptor.cs │ ├── Extensions │ │ ├── GrainProbeExtensions.cs │ │ ├── TestKitSiloExtensions.GrainContext.cs │ │ └── TestKitSiloExtensions.GrainCreation.cs │ ├── GlobalUsing.cs │ ├── README.md │ ├── Reminders │ │ ├── ReminderContextHandler.cs │ │ ├── ReminderContextHolder.cs │ │ ├── ReminderExtensions.cs │ │ ├── TestReminder.cs │ │ └── TestReminderRegistry.cs │ ├── Services │ │ ├── TestServiceProvider.cs │ │ └── TestServicesExtensions.cs │ ├── Storage │ │ ├── IStorageStats.cs │ │ ├── StorageExtensions.cs │ │ ├── StorageManager.cs │ │ ├── TestPersistentStateAttributeToFactoryMapper.cs │ │ ├── TestStorage.cs │ │ └── TestStorageStats.cs │ ├── Streams │ │ ├── StreamExtensions.cs │ │ ├── TestStream.cs │ │ ├── TestStreamProvider.cs │ │ ├── TestStreamProviderManager.cs │ │ └── TestStreamSubscriptionHandle.cs │ ├── TestGrainActivationContext.cs │ ├── TestGrainContextAccessor.cs │ ├── TestGrainCreator.cs │ ├── TestGrainFactory.cs │ ├── TestGrainLifecycle.cs │ ├── TestGrainRuntime.cs │ ├── TestGrainStorage.cs │ ├── TestKitBase.cs │ ├── TestKitOptions.cs │ ├── TestKitSilo.cs │ ├── Timers │ │ ├── TestTimer.cs │ │ ├── TestTimerRegistry.cs │ │ └── TimerExtensions.cs │ ├── TypeHelper.cs │ └── Utilities │ │ ├── LambdaDisposable.cs │ │ └── RuntimeContextManager.cs └── Aevatar │ ├── Aevatar.csproj │ ├── AevatarDefaultConventionalRegistrar.cs │ ├── AevatarModule.cs │ ├── ConfigureAevatarGrainActivator.cs │ └── Extensions │ └── OrleansHostExtensions.cs └── test ├── Aevatar.Core.Tests ├── Aevatar.Core.Tests.csproj ├── AevatarTestKitSilo.cs ├── ContextPropagationTests.cs ├── EventHandlingTests.cs ├── EventSourcingTests.cs ├── GAgentTestKitBase.cs ├── GroupingTests.cs ├── Placement │ ├── SiloNamePatternPlacementTests.cs │ └── SiloNamePatternRegistrationTests.cs ├── PublishingTests.cs ├── TestArtifacts │ └── MyArtifact.cs ├── TestEvents │ ├── DevelopTaskTestEvent.cs │ ├── GroupReloadTestEvent.cs │ ├── IncorrectTestEvent.cs │ ├── InvestorFeedbackTestEvent.cs │ ├── NaiveTestEvent.cs │ ├── NewDemandTestEvent.cs │ ├── NewFeatureCompletedTestEvent.cs │ ├── NotImplEventBaseTestEvent.cs │ ├── ReceiveMessageTestEvent.cs │ ├── ResponseTestEvent.cs │ ├── SendMessageTestEvent.cs │ ├── SocialTestEvent.cs │ └── WorkingOnTestEvent.cs ├── TestGAgents │ ├── BadEventHandlerTestGAgent.cs │ ├── ConfigurationTestGAgent.cs │ ├── DeveloperTestGAgent.cs │ ├── DevelopingLeaderTestGAgent.cs │ ├── EventHandlerTestGAgent.cs │ ├── EventHandlerWithResponseTestGAgent.cs │ ├── ExceptionHandlingTestGAgent.cs │ ├── FatalEventHandlerTestGAgent.cs │ ├── GroupGAgent.cs │ ├── GroupTestGAgent.cs │ ├── InvestorTestGAgent.cs │ ├── LogViewAdaptorTestGAgent.cs │ ├── LongRunTaskTestGAgent.cs │ ├── MarketingLeaderTestGAgent.cs │ ├── NaiveTestGAgent.cs │ ├── PermissionTestGAgent.cs │ ├── SampleAIGAgent.cs │ ├── SubscribeTestGAgent.cs │ ├── TestStateProjectionGAgent.cs │ └── TokenUsageProjectionGAgent.cs ├── TestHelper.cs ├── TestStateLogEvents │ ├── MessageStateLogEvent.cs │ └── ReceiveMessageTestStateLogEvent.cs └── TestStateProjector.cs ├── Aevatar.EventSourcing.MongoDB.Tests ├── Aevatar.EventSourcing.MongoDB.Tests.csproj ├── AevatarMongoDbFixture.cs ├── BsonGrainSerializerTests.cs ├── GrainTypeBsonSerializer.cs ├── IdSpanBsonSerializer.cs ├── MongoDbLogConsistentStorageMongoTests.cs ├── MongoDbLogConsistentStorageTests.cs ├── MongoDbTestCollection.cs └── TestSiloLifecycle.cs ├── Aevatar.GAgents.Plugins ├── Aevatar.GAgents.Plugins.csproj └── PluginTestGAgent.cs ├── Aevatar.GAgents.Tests ├── Aevatar.GAgents.Tests.csproj ├── AevatarGAgentsTestBase.cs ├── AevatarGAgentsTestModule.cs ├── AssemblyInfo.cs ├── EventDispatcherTests.cs ├── GAgentBaseTests.cs ├── GAgentFactoryTests.cs ├── GAgentPermissionTests.cs ├── InMemoryPluginCodeStorageRepository.cs ├── InMemoryPluginLoadStatusRepository.cs ├── InMemoryTenantPluginCodeRepository.cs ├── MockPermissionGrantRepository.cs ├── PermissionGAgentBaseTests.cs ├── PluginGAgentManagerTests.cs ├── PluginTestCollection.cs ├── Plugins │ └── RestSharp.dll ├── StateProjectionGAgentBaseTests.cs ├── TestPermissionDefinitionProvider.cs └── appsettings.json ├── Aevatar.TestBase ├── Aevatar.TestBase.csproj ├── AevatarTestBase.cs ├── AevatarTestBaseModule.cs ├── ClusterCollection.cs ├── ClusterFixture.cs ├── InterfaceImplementationsFinder.cs ├── MockLoggerProvider.cs └── appsettings.json └── Aevatar.TestKit.Tests ├── Aevatar.TestKit.Tests.csproj ├── GlobalSuppressions.cs ├── GlobalUsing.cs ├── Grains ├── ActivationCountWithReminder.cs ├── BasicGrains.cs ├── ChatMessage.cs ├── Chatty.cs ├── ColorGrain.cs ├── ColorGrainState.cs ├── ColorRankingGrain.cs ├── ContextConstructorGrain.cs ├── DeactivationGrain.cs ├── DependencyGrain.cs ├── DeviceAndroidGrain.cs ├── DeviceIosGrain.cs ├── DeviceManagerGrain.cs ├── GrainContextGrain.cs ├── GreetingArchiveGrain.cs ├── HelloGrain.cs ├── HelloGrainWithServiceDependency.cs ├── HelloReminders.cs ├── HelloTimers.cs ├── IUnknownGrainResolver.cs ├── LifecycleGrain.cs ├── Listener.cs ├── PersistentListenerWithHandleInState.cs ├── PersistentListenerWithoutHandleInState.cs ├── PingGrain.cs ├── StatefulActivationGrain.cs ├── StatefulNonNewActivationGrain.cs └── UnknownGrainResolver.cs ├── Interfaces ├── Color.cs ├── IBasicGrains.cs ├── IChatty.cs ├── IColorGrain.cs ├── IColorRankingGrain.cs ├── IDateTimeService.cs ├── IDeactivationGrain.cs ├── IDependencyGrain.cs ├── IDevice.cs ├── IDeviceManager.cs ├── IGreetingArchiveGrain.cs ├── IHello.cs ├── ILifecycleGrain.cs ├── IListener.cs ├── IPing.cs ├── IPong.cs ├── IPong2.cs ├── IPongCompound.cs └── IUnknownGrain.cs └── Tests ├── ActivationGrainTests.cs ├── BasicGrainTests.cs ├── ContextConstructorGrainTests.cs ├── DeactivationGrainTests.cs ├── DependencyGrainTests.cs ├── GrainContextTests.cs ├── GrainIdTests.cs ├── GrainProbeTests.cs ├── LoggerTests.cs ├── PersistantStreamNotWithinGrainStateTests.cs ├── PersistentStreamWithinGrainStateTests.cs ├── ReminderTests.cs ├── ServiceProbeTests.cs ├── StorageFacetTests.cs ├── StorageTests.cs ├── StreamBatchTests.cs ├── StreamProbeStringTests.cs ├── StreamTests.cs ├── StrictGrainProbeTests.cs ├── StrictStreamTests.cs └── TimerTests.cs /.cursor/rules/code-practices.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | 7 | # Coding Best Practices 8 | 9 | Iterate until the implementation adheres to the following: 10 | - SOLID principles 11 | - Add logging at important checkpoints of the code 12 | - Ensure scalability and performance 13 | 14 | Whenever `dotnet build` or `dotnet run` is executed, fix the compile error if there are any. 15 | When referencing code, make sure that the referenced code exists. -------------------------------------------------------------------------------- /.cursor/rules/test-practices.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: implement unit test 3 | globs: 4 | alwaysApply: false 5 | --- 6 | 7 | # Unit Test Writing Best Practices 8 | 9 | When working on unit tests, do the following: 10 | 1. Output "!!!Implementing Unit Test Cases!!!". 11 | 2. Make sure to cover the following test cases: 12 | 1. Positive Test Cases 13 | 2. Negative Test Cases 14 | 3. Boundary Test Cases 15 | 4. Exception Test Cases 16 | 3. Use Arrange, Act and Assert format. 17 | 4. Implement using SOLID principles. 18 | 5. Only edit test files. 19 | 20 | You MUST iterate implementing and fixing unit tests until all the following criteria are met: 21 | 1. At least 80% code coverage. 22 | 2. All unit test cases passes. 23 | 3. Execute `dotnet test` and it yields no compile error. -------------------------------------------------------------------------------- /.github/workflows/test-with-code-coverage.yml: -------------------------------------------------------------------------------- 1 | name: Test with code coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | 8 | env: 9 | DOTNET_INSTALL_DIR: "./.dotnet" 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-22.04 14 | permissions: 15 | pull-requests: write 16 | contents: write 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Setup dotnet 21 | uses: actions/setup-dotnet@v4 22 | with: 23 | dotnet-version: '9.0.x' 24 | - name: chown 25 | run: | 26 | sudo chown -R $USER:$USER /home/runneradmin 27 | - name: Install dependencies 28 | run: dotnet restore --verbosity quiet 29 | 30 | - name: Build 31 | run: dotnet build --no-restore /clp:ErrorsOnly /p:GeneratePackageOnBuild=false --verbosity quiet 32 | 33 | - name: Test 34 | run: | 35 | dotnet test --no-restore --no-build --logger trx --settings CodeCoverage.runsettings --results-directory coverage --collect:"XPlat Code Coverage" 36 | 37 | - name: Upload coverage reports to Codecov 38 | uses: codecov/codecov-action@v4 39 | with: 40 | fail_ci_if_error: true 41 | files: ./coverage/*/coverage.cobertura.xml 42 | env: 43 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} -------------------------------------------------------------------------------- /CodeCoverage.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | cobertura 9 | [xunit.*]*,[*Tests]* 10 | **/test/**/*.cs,**/samples/**/*.cs 11 | Obsolete,GeneratedCodeAttribute 12 | false 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | net9.0 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 AElfProject 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | after_n_builds: 1 4 | coverage: 5 | status: 6 | project: 7 | default: 8 | target: auto -------------------------------------------------------------------------------- /common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | 1.0.0 5 | $(NoWarn);CS1591 6 | app 7 | 8 | 9 | 10 | 11 | $(NoWarn);0436 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/MODULE_DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | # Aevatar Framework Module Documentation 2 | 3 | This directory contains detailed technical documentation for the major modules of the Aevatar Framework. 4 | 5 | ## Available Module Documentation 6 | 7 | - [Aevatar.Core](./Aevatar.Core.md) - Core framework components for Generative Agents 8 | - [Aevatar.EventSourcing.Core](./Aevatar.EventSourcing.Core.md) - Event sourcing implementation 9 | - [Aevatar.Plugins](./Aevatar.Plugins.md) - Plugin system for extending agent capabilities 10 | - [Aevatar.PermissionManagement](./Aevatar.PermissionManagement.md) - Access control system for agents and resources 11 | 12 | ## Documentation Format 13 | 14 | Each module documentation includes: 15 | 16 | 1. **Data Flow Sequence Diagram** - Illustrates the typical flow of data and interactions between components 17 | 2. **Relationship Diagram** - Shows the class relationships and dependencies within the module 18 | 3. **Module Explanation** - Detailed overview of the module's purpose, components, and capabilities 19 | 20 | The diagrams are provided in Mermaid format, which can be rendered in GitHub and other Markdown viewers that support Mermaid syntax. -------------------------------------------------------------------------------- /samples/ArtifactGAgent/ArtifactGAgent.Client/ArtifactGAgent.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(DefaultTargetFramework) 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/ArtifactGAgent/ArtifactGAgent.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestArtifacts; 3 | using Aevatar.Extensions; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Hosting; 7 | 8 | var builder = Host.CreateDefaultBuilder(args) 9 | .UseOrleansClient(client => 10 | { 11 | client.UseLocalhostClustering() 12 | .AddMemoryStreams(AevatarCoreConstants.StreamProvider) 13 | .UseAevatar(); 14 | }) 15 | .ConfigureLogging(logging => logging.AddConsole()) 16 | .UseConsoleLifetime(); 17 | 18 | using IHost host = builder.Build(); 19 | await host.StartAsync(); 20 | 21 | var gAgentFactory = host.Services.GetRequiredService(); 22 | var gAgentManager = host.Services.GetRequiredService(); 23 | 24 | var allGAgents = gAgentManager.GetAvailableGAgentTypes(); 25 | Console.WriteLine("All types:"); 26 | foreach (var gAgent in allGAgents) 27 | { 28 | Console.WriteLine(gAgent.FullName); 29 | } 30 | 31 | Console.WriteLine(); 32 | 33 | { 34 | Console.WriteLine("Get GAgent from Type:"); 35 | var myArtifactGAgent = 36 | await gAgentFactory.GetArtifactGAgentAsync(); 37 | var description = await myArtifactGAgent.GetDescriptionAsync(); 38 | Console.WriteLine(description); 39 | } 40 | 41 | Console.ReadKey(); -------------------------------------------------------------------------------- /samples/ArtifactGAgent/ArtifactGAgent.Silo/ArtifactGAgent.Silo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(DefaultTargetFramework) 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/ArtifactGAgent/ArtifactGAgent.Silo/Program.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Extensions; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | var builder = Host.CreateDefaultBuilder(args) 8 | .ConfigureAppConfiguration((_, config) => 9 | { 10 | config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); 11 | }) 12 | .UseOrleans(silo => 13 | { 14 | silo.AddMemoryGrainStorage("Default") 15 | .AddMemoryStreams(AevatarCoreConstants.StreamProvider) 16 | .AddMemoryGrainStorage("PubSubStore") 17 | .AddLogStorageBasedLogConsistencyProvider("LogStorage") 18 | .UseLocalhostClustering() 19 | .UseAevatar() 20 | .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Information).AddConsole()); 21 | }) 22 | .UseConsoleLifetime(); 23 | 24 | using var host = builder.Build(); 25 | 26 | await host.RunAsync(); -------------------------------------------------------------------------------- /samples/ArtifactGAgent/ArtifactGAgent.Silo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "Default": "mongodb://127.0.0.1:27017/AevatarDb" 4 | } 5 | } -------------------------------------------------------------------------------- /samples/BroadCastGAgentDemo/BroadCastGAgentDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/MessagingGAgent.Client/MessagingGAgent.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(DefaultTargetFramework) 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Events/MessagingEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Events; 4 | 5 | [GenerateSerializer] 6 | public class MessagingEvent : EventBase 7 | { 8 | [Id(0)] public string Message { get; set; } = string.Empty; 9 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Events/SendEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Events; 4 | 5 | [GenerateSerializer] 6 | public class SendEvent : EventBase 7 | { 8 | [Id(0)] public string Message { get; set; } = string.Empty; 9 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Group/GroupAgentState.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Group; 4 | 5 | [GenerateSerializer] 6 | public class GroupAgentState : StateBase 7 | { 8 | [Id(0)] public int RegisteredAgents { get; set; } = 0; 9 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Group/GroupGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Aevatar.Core.Abstractions; 3 | using Microsoft.Extensions.Logging; 4 | using Orleans.Providers; 5 | 6 | namespace MessagingGAgent.Grains.Agents.Group; 7 | 8 | public interface IGroupGAgent : IGAgent 9 | { 10 | } 11 | [GrainType("MessagingGroupGAgent")] 12 | [StorageProvider(ProviderName = "PubSubStore")] 13 | [LogConsistencyProvider(ProviderName = "LogStorage")] 14 | public class GroupGAgent : GAgentBase, IGroupGAgent 15 | { 16 | public override Task GetDescriptionAsync() 17 | { 18 | return Task.FromResult("An agent to inform other agents when a social event is published."); 19 | } 20 | 21 | protected override Task OnRegisterAgentAsync(GrainId agentGuid) 22 | { 23 | ++State.RegisteredAgents; 24 | return Task.CompletedTask; 25 | } 26 | 27 | protected override Task OnUnregisterAgentAsync(GrainId agentGuid) 28 | { 29 | --State.RegisteredAgents; 30 | return Task.CompletedTask; 31 | } 32 | 33 | protected override async Task OnGAgentActivateAsync(CancellationToken cancellationToken) 34 | { 35 | State.RegisteredAgents = 0; 36 | } 37 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Group/GroupStateLogEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Group; 4 | 5 | public class GroupStateLogEvent : StateLogEventBase 6 | { 7 | 8 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Messaging/MessagingGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Aevatar.Core.Abstractions; 3 | using MessagingGAgent.Grains.Agents.Events; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace MessagingGAgent.Grains.Agents.Messaging; 7 | 8 | public interface IMessagingGAgent : IGAgent 9 | { 10 | Task GetReceivedMessagesAsync(); 11 | } 12 | 13 | public class MessagingGAgent : GAgentBase, IMessagingGAgent 14 | { 15 | public override Task GetDescriptionAsync() 16 | { 17 | return Task.FromResult("Agent for messaging."); 18 | } 19 | 20 | [EventHandler] 21 | public async Task OnSendEvent(SendEvent @event) 22 | { 23 | await Task.Delay(1000); 24 | await PublishAsync(new MessagingEvent() 25 | { 26 | Message = $"{this.GetGrainId().ToString()} sent a message." 27 | }); 28 | } 29 | 30 | [EventHandler] 31 | public async Task OnMessagingEvent(MessagingEvent @event) 32 | { 33 | RaiseEvent(new MessagingStateLogEvent()); 34 | await ConfirmEvents(); 35 | } 36 | 37 | public Task GetReceivedMessagesAsync() 38 | { 39 | return Task.FromResult(State.ReceivedMessages); 40 | } 41 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Messaging/MessagingGState.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Messaging; 4 | 5 | public class MessagingGState : StateBase 6 | { 7 | public int ReceivedMessages { get; set; } = 0; 8 | 9 | public void Apply(MessagingStateLogEvent message) 10 | { 11 | ReceivedMessages++; 12 | } 13 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Messaging/MessagingStateLogEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace MessagingGAgent.Grains.Agents.Messaging; 4 | 5 | public class MessagingStateLogEvent : StateLogEventBase 6 | { 7 | 8 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/Agents/Messaging/SpecializedGrain.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Placement; 2 | 3 | namespace MessagingGAgent.Grains; 4 | /// 5 | /// Example of a grain class that uses the SiloNamePatternPlacement attribute to specify placement. 6 | /// 7 | [SiloNamePatternPlacement("Analytics")] 8 | public class SpecializedGrain : Orleans.Grain, ISpecializedGrain 9 | { 10 | public Task DoSomethingAsync() 11 | { 12 | // This grain will be activated on a silo whose name begins with "Analytics" if available 13 | // For example, it will match "AnalyticsSilo-01", "DataAnalytics", etc. 14 | Console.WriteLine($"Grain activated on silo: {this.RuntimeIdentity}"); 15 | return Task.CompletedTask; 16 | } 17 | } 18 | 19 | /// 20 | /// Interface for the specialized grain example. 21 | /// 22 | public interface ISpecializedGrain : Orleans.IGrainWithGuidKey 23 | { 24 | Task DoSomethingAsync(); 25 | } -------------------------------------------------------------------------------- /samples/MessagingGAgent.Grains/MessagingGAgent.Grains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/MessagingGAgent.Silo/MessagingGAgent.Silo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(DefaultTargetFramework) 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/MessagingGAgent.Silo/Program.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Orleans.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using Aevatar.Core.Placement; 6 | 7 | /// Set your Orleans Silo name pattern in the environment variable SILO_NAME_PATTERN. 8 | 9 | var siloNamePattern = "No-Analytics-Silo"; 10 | var builder = Host.CreateDefaultBuilder(args) 11 | .UseOrleans(silo => 12 | { 13 | silo.AddMemoryGrainStorage("Default") 14 | .AddMemoryStreams(AevatarCoreConstants.StreamProvider) 15 | .AddMemoryGrainStorage("PubSubStore") 16 | .AddLogStorageBasedLogConsistencyProvider("LogStorage") 17 | .UseLocalhostClustering() 18 | .Configure(options => 19 | { 20 | options.SiloName = $"{siloNamePattern}-{Guid.NewGuid().ToString("N").Substring(0, 6)}"; 21 | }) 22 | .ConfigureLogging(logging => logging.AddConsole()); 23 | }) 24 | .UseConsoleLifetime(); 25 | 26 | builder.ConfigureServices((context, services) => 27 | { 28 | // Register the SiloNamePatternPlacement director 29 | services.AddPlacementDirector(); 30 | 31 | }); 32 | 33 | using var host = builder.Build(); 34 | 35 | await host.RunAsync(); -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/PluginGAgent.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | $(DefaultTargetFramework) 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Always 28 | 29 | 30 | 31 | 32 | 33 | 34 | Always 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "Properties": { 4 | "Application": "PluginGAgent.Silo", 5 | "Environment": "Development" 6 | }, 7 | "MinimumLevel": { 8 | "Default": "Debug", 9 | "Override": { 10 | "Default": "Debug", 11 | "System": "Warning", 12 | "Microsoft": "Warning", 13 | "Orleans": "Warning" 14 | } 15 | }, 16 | "WriteTo": [ 17 | { 18 | "Name": "Console", 19 | "Args": { 20 | "formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact" 21 | } 22 | }, 23 | { 24 | "Name": "RollingFile", 25 | "Args": { 26 | "pathFormat": "Logs/log-{Date}.log", 27 | "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{Offset:zzz}][{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}", 28 | "rollOnFileSizeLimit": true, 29 | "rollingInterval": "Day", 30 | "retainedFileCountLimit": 15 31 | } 32 | } 33 | ] 34 | }, 35 | "Plugins": { 36 | "TenantId": "cd6b8f09214673d3cade4e832627b4f6" 37 | }, 38 | "ConnectionStrings": { 39 | "Default": "mongodb://localhost:27017/Aevatar", 40 | "Orleans": "mongodb://localhost:27017/AevatarDb" 41 | }, 42 | "Aevatar": { 43 | "StreamNamespace": "Aevatar" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/plugins/Aevatar.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aevatarAI/aevatar-framework/b4e62d20ab438dc664a3026e07af96dcba57965c/samples/PluginGAgent/PluginGAgent.Client/plugins/Aevatar.dll -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/plugins/PluginGAgent.Grains.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aevatarAI/aevatar-framework/b4e62d20ab438dc664a3026e07af96dcba57965c/samples/PluginGAgent/PluginGAgent.Client/plugins/PluginGAgent.Grains.dll -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/plugins/PluginGAgent.GrainsDup.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aevatarAI/aevatar-framework/b4e62d20ab438dc664a3026e07af96dcba57965c/samples/PluginGAgent/PluginGAgent.Client/plugins/PluginGAgent.GrainsDup.dll -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Client/plugins/RestSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aevatarAI/aevatar-framework/b4e62d20ab438dc664a3026e07af96dcba57965c/samples/PluginGAgent/PluginGAgent.Client/plugins/RestSharp.dll -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Grains/CommanderGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Aevatar.Core.Abstractions; 3 | 4 | namespace PluginGAgent.Grains; 5 | 6 | [GenerateSerializer] 7 | public class CommanderGAgentState : StateBase 8 | { 9 | 10 | } 11 | 12 | [GenerateSerializer] 13 | public class CommanderStateLogEvent : StateLogEventBase 14 | { 15 | 16 | } 17 | 18 | [GenerateSerializer] 19 | public class Command : EventBase 20 | { 21 | [Id(0)] public string Content { get; set; } 22 | } 23 | 24 | [GAgent("commander", "pluginTest")] 25 | public class CommanderGAgent : GAgentBase 26 | { 27 | public override Task GetDescriptionAsync() 28 | { 29 | return Task.FromResult("This is a GAgent for"); 30 | } 31 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Grains/PluginGAgent.Grains.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Grains/WorkerGAgent.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | using Aevatar.Core; 4 | using Aevatar.Core.Abstractions; 5 | using Microsoft.Extensions.Logging; 6 | using RestSharp; 7 | 8 | namespace PluginGAgent.Grains; 9 | 10 | [GenerateSerializer] 11 | 12 | public class WorkerGAgentState : StateBase; 13 | 14 | [GenerateSerializer] 15 | public class WorkerStateLogEvent : StateLogEventBase; 16 | 17 | [GAgent("worker", "pluginTest")] 18 | public class WorkerGAgent : GAgentBase 19 | { 20 | public override Task GetDescriptionAsync() 21 | { 22 | return Task.FromResult("This is a GAgent for"); 23 | } 24 | 25 | [EventHandler] 26 | public async Task HandleEventAsync(Command data) 27 | { 28 | Logger.LogInformation("Received command: {0}", data.Content); 29 | 30 | var client = new RestClient("https://www.hao123.com"); 31 | var request = new RestRequest("/api/gethitthecity"); 32 | var response = await client.GetAsync(request); 33 | Logger.LogInformation($"Response: {response.Content}"); 34 | } 35 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.GrainsDup/CommanderGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Aevatar.Core.Abstractions; 3 | 4 | namespace PluginGAgent.Grains; 5 | 6 | [GenerateSerializer] 7 | public class CommanderGAgentState : StateBase 8 | { 9 | 10 | } 11 | 12 | [GenerateSerializer] 13 | public class CommanderStateLogEvent : StateLogEventBase 14 | { 15 | 16 | } 17 | 18 | [GenerateSerializer] 19 | public class Command : EventBase 20 | { 21 | [Id(0)] public string Content { get; set; } 22 | } 23 | 24 | [GAgent("commander", "pluginTest")] 25 | public class CommanderGAgent : GAgentBase 26 | { 27 | public override Task GetDescriptionAsync() 28 | { 29 | return Task.FromResult("This is a GAgent for"); 30 | } 31 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.GrainsDup/PluginGAgent.GrainsDup.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.GrainsDup/WorkerGAgent.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | using Aevatar.Core; 4 | using Aevatar.Core.Abstractions; 5 | using Microsoft.Extensions.Logging; 6 | using RestSharp; 7 | 8 | namespace PluginGAgent.Grains; 9 | 10 | [GenerateSerializer] 11 | 12 | public class WorkerGAgentState : StateBase; 13 | 14 | [GenerateSerializer] 15 | public class WorkerStateLogEvent : StateLogEventBase; 16 | 17 | [GAgent("worker", "pluginTest")] 18 | public class WorkerGAgent : GAgentBase 19 | { 20 | public override Task GetDescriptionAsync() 21 | { 22 | return Task.FromResult("This is a GAgent for"); 23 | } 24 | 25 | [EventHandler] 26 | public async Task HandleEventAsync(Command data) 27 | { 28 | Logger.LogInformation("Received command: {0}", data.Content); 29 | 30 | var client = new RestClient("https://www.hao123.com"); 31 | var request = new RestRequest("/api/gethitthecity"); 32 | var response = await client.GetAsync(request); 33 | Logger.LogInformation($"Response: {response.Content}"); 34 | } 35 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Silo/PluginGAgentTestHostedService.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions.Extensions; 2 | using Aevatar.Core.Abstractions.Plugin; 3 | using Aevatar.Plugins.Extensions; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Volo.Abp; 7 | using Volo.Abp.PermissionManagement; 8 | 9 | namespace PluginGAgent.Silo; 10 | 11 | public class PluginGAgentTestHostedService : IHostedService 12 | { 13 | private readonly IAbpApplicationWithExternalServiceProvider _application; 14 | private readonly IServiceProvider _serviceProvider; 15 | 16 | public PluginGAgentTestHostedService( 17 | IAbpApplicationWithExternalServiceProvider application, 18 | IServiceProvider serviceProvider) 19 | { 20 | _application = application; 21 | _serviceProvider = serviceProvider; 22 | } 23 | 24 | public async Task StartAsync(CancellationToken cancellationToken) 25 | { 26 | await _application.InitializeAsync(_serviceProvider); 27 | } 28 | 29 | public Task StopAsync(CancellationToken cancellationToken) 30 | { 31 | _application.Shutdown(); 32 | return Task.CompletedTask; 33 | } 34 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Silo/PluginGAgentTestModule.cs: -------------------------------------------------------------------------------- 1 | using Aevatar; 2 | using Aevatar.Core.Abstractions; 3 | using Aevatar.PermissionManagement; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Serilog; 6 | using Volo.Abp.AspNetCore.Serilog; 7 | using Volo.Abp.Autofac; 8 | using Volo.Abp.AutoMapper; 9 | using Volo.Abp.Modularity; 10 | using Volo.Abp.PermissionManagement; 11 | 12 | namespace PluginGAgent.Silo; 13 | 14 | [DependsOn( 15 | typeof(AbpAspNetCoreSerilogModule), 16 | typeof(AbpAutofacModule), 17 | typeof(AbpAutoMapperModule), 18 | // typeof(AevatarPermissionManagementModule), 19 | typeof(AevatarModule) 20 | )] 21 | public class PluginGAgentTestModule : AbpModule 22 | { 23 | public override void ConfigureServices(ServiceConfigurationContext context) 24 | { 25 | Configure(options => { options.AddMaps(); }); 26 | context.Services.AddHostedService(); 27 | context.Services.AddSerilog(_ => {}, 28 | true, writeToProviders: true); 29 | context.Services.AddHttpClient(); 30 | context.Services.AddSingleton(); 31 | // Configure(options => 32 | // { 33 | // options.IsDynamicPermissionStoreEnabled = true; 34 | // }); 35 | } 36 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Silo/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using PluginGAgent.Silo.Extensions; 5 | using Serilog; 6 | 7 | namespace PluginGAgent.Silo; 8 | 9 | public class Program 10 | { 11 | public static async Task Main(string[] args) 12 | { 13 | var configuration = new ConfigurationBuilder() 14 | .AddJsonFile("appsettings.json") 15 | .AddJsonFile("appsettings.secrets.json", optional: true) 16 | .Build(); 17 | Log.Logger = new LoggerConfiguration() 18 | .Enrich.FromLogContext() 19 | .ReadFrom.Configuration(configuration) 20 | .CreateLogger(); 21 | 22 | try 23 | { 24 | Log.Information("Starting Silo"); 25 | await CreateHostBuilder(args).RunConsoleAsync(); 26 | return 0; 27 | } 28 | catch (Exception ex) 29 | { 30 | Log.Fatal(ex, "Host terminated unexpectedly!"); 31 | return 1; 32 | } 33 | finally 34 | { 35 | Log.CloseAndFlush(); 36 | } 37 | } 38 | 39 | internal static IHostBuilder CreateHostBuilder(string[] args) => 40 | Host.CreateDefaultBuilder(args) 41 | .ConfigureServices((_, services) => { services.AddApplication(); }) 42 | .UseOrleansConfiguration() 43 | .UseAutofac() 44 | .UseSerilog(); 45 | } -------------------------------------------------------------------------------- /samples/PluginGAgent/PluginGAgent.Silo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "Properties": { 4 | "Application": "PluginGAgent.Silo", 5 | "Environment": "Development" 6 | }, 7 | "MinimumLevel": { 8 | "Default": "Debug", 9 | "Override": { 10 | "Default": "Debug", 11 | "System": "Warning", 12 | "Microsoft": "Warning", 13 | "Orleans": "Warning" 14 | } 15 | }, 16 | "WriteTo": [ 17 | { 18 | "Name": "Console", 19 | "Args": { 20 | "formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact" 21 | } 22 | }, 23 | { 24 | "Name": "RollingFile", 25 | "Args": { 26 | "pathFormat": "Logs/log-{Date}.log", 27 | "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{Offset:zzz}][{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}", 28 | "rollOnFileSizeLimit": true, 29 | "rollingInterval": "Day", 30 | "retainedFileCountLimit": 15 31 | } 32 | } 33 | ] 34 | }, 35 | "Plugins": { 36 | "TenantId": "cd6b8f09214673d3cade4e832627b4f6", 37 | "ConnectionString": "mongodb://localhost:27017/AevatarDb" 38 | }, 39 | "ConnectionStrings": { 40 | "Default": "mongodb://localhost:27017/Aevatar", 41 | "Orleans": "mongodb://localhost:27017/AevatarDb" 42 | }, 43 | "Aevatar": { 44 | "StreamNamespace": "Aevatar" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/PubSubDemoWithoutGroup/PubSubDemoWithoutGroup.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/SiloNamePatternPlacementExamples/SiloNamePatternPlacementExamples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Aevatar.Core.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar.Core.Abstractions 9 | aevatar Framework 10 | AElf 11 | The abstraction package to the Aevatar.Core framework. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/AevatarCoreConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public class AevatarCoreConstants 4 | { 5 | public const int DefaultNumOfProjectorPerAgentType = 8; 6 | public const string StreamProvider = "Aevatar"; 7 | public const char GAgentNamespaceSeparator = '.'; 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/AevatarOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public class AevatarOptions 4 | { 5 | public string StreamNamespace { get; set; } = "Aevatar"; 6 | public string StateProjectionStreamNamespace { get; set; } = "AevatarStateProjection"; 7 | 8 | public string BroadCastStreamNamespace { get; set; } = "AevatarBroadCast"; 9 | //public int ElasticSearchProcessors { get; set; } = 10; 10 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Application/BroadCastGState.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | using Orleans.Streams; 4 | 5 | [GenerateSerializer] 6 | public class BroadCastGState : StateBase 7 | { 8 | [Id(0)]public Dictionary Subscription = []; 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Application/RequestAllDescriptionsEvent.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [GenerateSerializer] 5 | public class RequestAllSubscriptionsEvent : EventWithResponseBase 6 | { 7 | [Id(0)] public Type? RequestFromGAgentType { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Application/SubscribedEventListEvent.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [GenerateSerializer] 5 | public class SubscribedEventListEvent : EventBase 6 | { 7 | /// 8 | /// Key: GAgent Type. 9 | /// Value: Subscribed Event Types. 10 | /// 11 | [Id(0)] public required Dictionary> Value { get; set; } 12 | 13 | [Id(1)] public required Type GAgentType { get; set; } 14 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/ConfigurationBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public abstract class ConfigurationBase : EventBase; -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/EventHandlerExceptionEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public class EventHandlerExceptionEvent : EventBase 5 | { 6 | [Id(0)] public required GrainId GrainId { get; set; } 7 | [Id(1)] public required Type HandleEventType { get; set; } 8 | [Id(2)] public required string ExceptionMessage { get; set; } 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Events/EventBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [GenerateSerializer] 5 | public abstract class EventBase 6 | { 7 | [Id(0)] public Guid? CorrelationId { get; set; } 8 | [Id(1)] public GrainId PublisherGrainId { get; set; } 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Events/EventWithResponseBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [GenerateSerializer] 5 | public abstract class EventWithResponseBase : EventBase where TResponseEvent : EventBase; -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Events/EventWrapper.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | using System.Diagnostics; 5 | 6 | [GenerateSerializer] 7 | public class EventWrapper : EventWrapperBase where T : EventBase 8 | { 9 | [Id(0)] public T Event { get; private set; } 10 | [Id(1)] public Guid EventId { get; private set; } 11 | [Id(2)] public GrainId GrainId { get; private set; } 12 | [Id(3)] public Guid? CorrelationId { get; set; } 13 | [Id(4)] public GrainId PublisherGrainId { get; set; } 14 | 15 | public EventWrapper(T @event, Guid eventId, GrainId grainId) 16 | { 17 | Event = @event; 18 | EventId = eventId; 19 | GrainId = grainId; 20 | CorrelationId = @event.CorrelationId; 21 | PublisherGrainId = @event.PublisherGrainId; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Events/EventWrapperBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | 3 | using System.Diagnostics; 4 | 5 | namespace Aevatar.Core.Abstractions; 6 | 7 | using System.Collections.Generic; 8 | 9 | [GenerateSerializer] 10 | public abstract class EventWrapperBase 11 | { 12 | // Constants for context metadata keys 13 | public const string TraceIdKey = "TraceId"; 14 | public const string SpanIdKey = "SpanId"; 15 | public const string TraceFlagsKey = "TraceFlags"; 16 | public const string BaggagePrefixKey = "Baggage."; 17 | 18 | [Id(0)] 19 | public Dictionary ContextMetadata { get; set; } = new Dictionary(); 20 | 21 | protected EventWrapperBase() 22 | { 23 | // Initialize ContextMetadata 24 | ContextMetadata = new Dictionary(); 25 | 26 | // Simple context injection - in real implementation, this would be 27 | // enhanced with DistributedContextPropagator usage in GAgentAsyncObserver 28 | var activity = Activity.Current; 29 | if (activity != null) 30 | { 31 | ContextMetadata[TraceIdKey] = activity.TraceId.ToString(); 32 | ContextMetadata[SpanIdKey] = activity.SpanId.ToString(); 33 | ContextMetadata[TraceFlagsKey] = activity.ActivityTraceFlags.ToString(); 34 | 35 | // Add baggage items 36 | foreach (var baggage in activity.Baggage) 37 | { 38 | ContextMetadata[$"{BaggagePrefixKey}{baggage.Key}"] = baggage.Value; 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Exceptions/ArtifactGAgentException.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Exceptions; 2 | 3 | public class ArtifactGAgentException(string message, Exception inner) : Exception(message, inner); -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Exceptions/EventHandlingException.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Exceptions; 2 | 3 | public class EventHandlingException(string message, Exception ex) : Exception(message, ex); -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Exceptions/EventPublishingException.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Exceptions; 2 | 3 | public class EventPublishingException(string message, Exception ex) : Exception(message, ex); 4 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Exceptions/StateTransitionException.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Exceptions; 2 | 3 | public class StateTransitionException(string message, Exception inner) : Exception(message, inner); 4 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Extensions/AssemblyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Aevatar.Core.Abstractions.Extensions; 4 | 5 | public static class AssemblyExtensions 6 | { 7 | public static Type[] GetTypesIgnoringLoadException(this Assembly? assembly) 8 | { 9 | if (assembly is null) return []; 10 | Type[] types; 11 | try 12 | { 13 | types = assembly.GetTypes(); 14 | } 15 | catch (ReflectionTypeLoadException ex) 16 | { 17 | types = ex.Types.Where(t => t != null).ToArray()!; 18 | } 19 | 20 | return types; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace Aevatar.Core.Abstractions.Extensions; 5 | 6 | public static class StringExtensions 7 | { 8 | public static Guid ToGuid(this string str) 9 | { 10 | var hash = MD5.HashData(Encoding.UTF8.GetBytes(str)); 11 | return new Guid(hash); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IArtifact.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IArtifact 4 | where TState : StateBase 5 | where TStateLogEvent : StateLogEventBase 6 | { 7 | void TransitionState(TState state, StateLogEventBase stateLogEvent); 8 | string GetDescription(); 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IArtifactGAgent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IArtifactGAgent : IGAgent 4 | where TArtifact : IArtifact 5 | where TState : StateBase 6 | where TStateLogEvent : StateLogEventBase 7 | { 8 | Task GetArtifactAsync(); 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IExtGAgent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | /// 4 | /// Represents an extended GAgent which intends to boosts the performance of the current GAgent. 5 | /// It should be merged back to IGAgent after the performance is improved. 6 | /// 7 | public interface IExtGAgent : IGAgent 8 | { 9 | 10 | /// 11 | /// Register many GAgents as the next level of the current GAgent. 12 | /// To compare with RegisterAsync, this method is more efficient via batch processing. 13 | /// 14 | /// 15 | /// 16 | Task RegisterManyAsync(List gAgents); 17 | 18 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IGAgentFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IGAgentFactory 4 | { 5 | Task GetGAgentAsync(GrainId grainId, ConfigurationBase? configuration = null); 6 | 7 | Task GetGAgentAsync(Guid primaryKey, string alias, 8 | string ns, ConfigurationBase? configuration = null); 9 | 10 | Task GetGAgentAsync(string alias, string ns, 11 | ConfigurationBase? configuration = null); 12 | 13 | Task GetGAgentAsync(Guid primaryKey, Type gAgentType, ConfigurationBase? configuration = null); 14 | 15 | Task GetGAgentAsync(Type gAgentType, ConfigurationBase? configuration = null); 16 | 17 | Task GetGAgentAsync(Guid primaryKey, 18 | ConfigurationBase? configuration = null) 19 | where TGrainInterface : IGAgent; 20 | 21 | Task GetGAgentAsync(GrainId grainId, 22 | ConfigurationBase? configuration = null) 23 | where TGrainInterface : IGAgent; 24 | 25 | Task GetGAgentAsync(ConfigurationBase? configuration = null) 26 | where TGrainInterface : IGAgent; 27 | 28 | Task> 29 | GetArtifactGAgentAsync(ConfigurationBase? configuration = null) 30 | where TArtifact : IArtifact, new() 31 | where TState : StateBase, new() 32 | where TStateLogEvent : StateLogEventBase; 33 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IGAgentManager.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IGAgentManager 4 | { 5 | List GetAvailableGAgentTypes(); 6 | List GetAvailableEventTypes(); 7 | List GetAvailableGAgentGrainTypes(); 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IPublishingGAgent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IPublishingGAgent : IGAgent 4 | { 5 | Task PublishEventAsync(T @event, params IGAgent[] agents) where T : EventBase; 6 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IStateDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IStateDispatcher 4 | { 5 | Task PublishAsync(GrainId grainId, StateWrapper stateWrapper) where TState : StateBase; 6 | Task PublishSingleAsync(GrainId grainId, StateWrapper stateWrapper) where TState : StateBase; 7 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/IStateProjector.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | public interface IStateProjector 4 | { 5 | Task ProjectAsync(T state) where T : StateWrapperBase; 6 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Infrastructure/AllEventHandlerAttribute.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 5 | public class AllEventHandlerAttribute(int priority = 10, bool allowSelfHandling = false) : Attribute 6 | { 7 | public int Priority { get; } = priority; 8 | public bool AllowSelfHandling { get; } = allowSelfHandling; 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Infrastructure/EventHandlerAttribute.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 5 | public class EventHandlerAttribute(int priority = 100, bool allowSelfHandling = false) : Attribute 6 | { 7 | public int Priority { get; } = priority; 8 | public bool AllowSelfHandling { get; } = allowSelfHandling; 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Infrastructure/GAgentAttribute.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Metadata; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace Aevatar.Core.Abstractions; 5 | 6 | [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] 7 | public sealed class GAgentAttribute : Attribute, IGrainTypeProviderAttribute 8 | { 9 | private readonly string? _ns; 10 | private readonly string? _alias; 11 | 12 | public GAgentAttribute() 13 | { 14 | 15 | } 16 | 17 | public GAgentAttribute(string alias) 18 | { 19 | _alias = alias; 20 | } 21 | 22 | public GAgentAttribute(string alias, string ns) 23 | { 24 | _alias = alias; 25 | _ns = ns; 26 | } 27 | 28 | public GrainType GetGrainType(IServiceProvider services, Type type) 29 | { 30 | if (_alias == null) // Use ctor with 0 parameters. 31 | { 32 | return GrainType.Create($"{type.Namespace}{AevatarCoreConstants.GAgentNamespaceSeparator}{type.Name}"); 33 | } 34 | 35 | if (_ns == null) 36 | { 37 | return GrainType.Create($"{type.Namespace}{AevatarCoreConstants.GAgentNamespaceSeparator}{_alias}"); 38 | } 39 | 40 | return GrainType.Create($"{_ns}{AevatarCoreConstants.GAgentNamespaceSeparator}{_alias}"); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Infrastructure/StateHandlerAttribute.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 5 | public class StateHandlerAttribute : Attribute; 6 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Infrastructure/StateLogEventHandlerAttribute.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace Aevatar.Core.Abstractions; 3 | 4 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 5 | public class StateLogEventHandlerAttribute : Attribute; 6 | -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/AddExistedPluginDto.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | [GenerateSerializer] 4 | public class AddExistedPluginDto 5 | { 6 | [Id(0)] public required Guid PluginCodeId { get; set; } 7 | [Id(1)] public required Guid TenantId { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/AddPluginDto.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | [GenerateSerializer] 4 | public class AddPluginDto 5 | { 6 | [Id(0)] public required byte[] Code { get; set; } = []; 7 | [Id(1)] public required Guid TenantId { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/IPluginGAgentManager.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Aevatar.Core.Abstractions.Plugin; 4 | 5 | public interface IPluginGAgentManager 6 | { 7 | Task AddPluginAsync(AddPluginDto addPluginDto); 8 | Task> GetPluginsAsync(Guid tenantId); 9 | Task GetPluginsWithDescriptionAsync(Guid tenantId); 10 | Task> GetPluginDescriptions(Guid pluginCodeId); 11 | Task RemovePluginAsync(RemovePluginDto removePluginDto); 12 | Task UpdatePluginAsync(UpdatePluginDto updatePluginDto); 13 | Task AddExistedPluginAsync(AddExistedPluginDto addExistedPluginDto); 14 | Task> GetPluginAssembliesAsync(Guid tenantId); 15 | Task> GetCurrentTenantPluginAssembliesAsync(); 16 | /// 17 | /// Query plugin DLL load status for this startup. 18 | /// 19 | /// Tenant ID 20 | /// Dictionary: key is DLL name, value is load status and error reason if failed 21 | Task> GetPluginLoadStatusAsync(Guid? tenantId = null); 22 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/PluginLoadStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | /// 4 | /// Plugin DLL load status information. 5 | /// 6 | public class PluginLoadStatus 7 | { 8 | /// 9 | /// Load status: Success or Failed. 10 | /// 11 | public LoadStatus Status { get; set; } 12 | 13 | /// 14 | /// Failure reason (only set if Status is Failed). 15 | /// 16 | public string? Reason { get; set; } 17 | } 18 | 19 | public enum LoadStatus 20 | { 21 | Unload = -1, 22 | Success = 0, 23 | GAgentDuplicated, 24 | AlreadyLoaded, 25 | Error 26 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/PluginsInformation.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | [GenerateSerializer] 4 | public class PluginsInformation 5 | { 6 | /// 7 | /// PluginCodeId -> Description. 8 | /// 9 | [Id(0)] 10 | public Dictionary> Value { get; set; } = new(); 11 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/RemovePluginDto.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | [GenerateSerializer] 4 | public class RemovePluginDto 5 | { 6 | [Id(0)] public required Guid TenantId { get; set; } 7 | [Id(1)] public required Guid PluginCodeId { get; set; } 8 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Plugin/UpdatePluginDto.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Plugin; 2 | 3 | [GenerateSerializer] 4 | public class UpdatePluginDto 5 | { 6 | [Id(0)] public required Guid TenantId { get; set; } 7 | [Id(1)] public required Guid PluginCodeId { get; set; } 8 | [Id(2)] public required byte[] Code { get; set; } 9 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/Projections/IProjectionGrain.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions.Projections; 2 | 3 | public interface IProjectionGrain : IGrainWithGuidKey 4 | where TState : StateBase, new() 5 | { 6 | Task ActivateAsync(); 7 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/StateBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public abstract class StateBase 5 | { 6 | [Id(0)] public List Children { get; set; } = []; 7 | [Id(1)] public GrainId? Parent { get; set; } 8 | [Id(2)] public string? GAgentCreator { get; set; } 9 | 10 | public void Apply(StateLogEventBase @stateLogEvent) 11 | { 12 | // Just to avoid exception on GAgentBase.TransitionState. 13 | } 14 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/StateLogEventBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public abstract class StateLogEventBase 5 | { 6 | [Id(0)] public virtual Guid Id { get; set; } 7 | [Id(1)] public DateTime Ctime { get; set; } 8 | } 9 | 10 | [GenerateSerializer] 11 | public abstract class StateLogEventBase : StateLogEventBase 12 | where T:StateLogEventBase; -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/StateWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public class StateWrapper(GrainId grainId, T state, int version) : StateWrapperBase 5 | where T : StateBase 6 | { 7 | [Id(0)] public GrainId GrainId { get; private set; } = grainId; 8 | [Id(1)] public T State { get; private set; } = state; 9 | [Id(2)] public int Version { get; private set; } = version; 10 | } -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/StateWrapperBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Abstractions; 2 | 3 | [GenerateSerializer] 4 | public abstract class StateWrapperBase; -------------------------------------------------------------------------------- /src/Aevatar.Core.Abstractions/SyncWorker/IAevatarSyncWorker.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Streams; 2 | using Orleans.SyncWork; 3 | 4 | namespace Aevatar.Core.Abstractions.SyncWorker; 5 | 6 | public interface IAevatarSyncWorker : ISyncWorker, IGrainWithStringKey 7 | where TRequest : EventBase 8 | where TResponse : EventBase 9 | { 10 | Task SetLongRunTaskAsync(IAsyncStream callbackStream); 11 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/Aevatar.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar.Core 9 | aevatar Framework 10 | AElf 11 | A distributed AI agent-based framework built on Microsoft Orleans for building scalable event-sourced applications. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Aevatar.Core/AevatarGAgentConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core; 2 | 3 | public static class AevatarGAgentConstants 4 | { 5 | public const string EventHandlerDefaultMethodName = "HandleEventAsync"; 6 | public const string StateHandlerDefaultMethodName = "HandleStateAsync"; 7 | public const string ConfigDefaultMethodName = "PerformConfigAsync"; 8 | public const string ForwardEventMethodName = "ForwardEventAsync"; 9 | public const int MaxSyncWorkConcurrency = 4; 10 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/AsyncTaskRunner.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace Aevatar.Core; 4 | 5 | public static class AsyncTaskRunner 6 | { 7 | public static void RunSafely(Func taskFactory, ILogger logger) 8 | { 9 | Task.Run(async () => 10 | { 11 | try 12 | { 13 | await taskFactory().ConfigureAwait(false); 14 | } 15 | catch (Exception ex) 16 | { 17 | logger.LogCritical(ex, "Unhandled async exception"); 18 | } 19 | }); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/EventWrapperHelper.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core; 4 | 5 | /// 6 | /// Utility methods for working with EventWrapper objects 7 | /// 8 | internal static class EventWrapperHelper 9 | { 10 | /// 11 | /// Extracts a property from an EventWrapper using reflection 12 | /// 13 | public static T? GetProperty(EventWrapperBase wrapper, string propertyName) where T : class 14 | { 15 | return wrapper.GetType().GetProperty(propertyName)?.GetValue(wrapper) as T; 16 | } 17 | 18 | /// 19 | /// Extracts common properties from an EventWrapper 20 | /// 21 | public static (EventBase eventType, string? eventId) ExtractProperties(EventWrapperBase wrapper) 22 | { 23 | var eventType = GetProperty(wrapper, nameof(EventWrapper.Event))!; 24 | var eventId = GetProperty(wrapper, nameof(EventWrapper.EventId))?.ToString(); 25 | 26 | return (eventType, eventId); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/Extensions/MethodInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Aevatar.Core.Abstractions; 3 | 4 | namespace Aevatar.Core.Extensions; 5 | 6 | internal static class MethodInfoExtensions 7 | { 8 | internal static (Type, bool) AnalysisMethodMetadata(this MethodInfo method) 9 | { 10 | var paramInfo = method.GetParameters()[0]; 11 | var paramType = paramInfo.ParameterType; 12 | var isResponse = paramInfo.ParameterType.BaseType is { IsGenericType: true } && 13 | paramInfo.ParameterType.BaseType.GetGenericTypeDefinition() == typeof(EventWithResponseBase<>); 14 | return (paramType, isResponse); 15 | } 16 | 17 | internal static bool IsSelfHandlingAllowed(this MethodInfo method) 18 | { 19 | return (method.GetCustomAttribute()?.AllowSelfHandling ?? 20 | method.GetCustomAttribute()?.AllowSelfHandling) ?? false; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/Extensions/ReflectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Aevatar.Core.Extensions; 4 | 5 | internal static class ReflectionExtensions 6 | { 7 | public static bool HasAttribute(this MethodInfo method) where T : Attribute => 8 | method.GetCustomAttribute() != null; 9 | } 10 | -------------------------------------------------------------------------------- /src/Aevatar.Core/GAgentBase.SyncWorker.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Abstractions.SyncWorker; 3 | using Microsoft.Extensions.Logging; 4 | using Orleans.SyncWork; 5 | 6 | namespace Aevatar.Core; 7 | 8 | public abstract partial class GAgentBase 9 | { 10 | protected async Task CreateLongRunTaskAsync(TRequest request) 11 | where TRequest : EventBase 12 | where TResponse : EventBase 13 | { 14 | try 15 | { 16 | var grainId = $"{typeof(TRequest).Name}_to_{typeof(TResponse).Name}/{Guid.NewGuid()}"; 17 | var syncWorker = GrainFactory.GetGrain>(grainId); 18 | await syncWorker.SetLongRunTaskAsync(GetEventBaseStream(GrainId)); 19 | await syncWorker.Start(request); 20 | } 21 | catch (Exception ex) 22 | { 23 | Logger.LogError($"Error creating long run task: {ex.Message}"); 24 | throw; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/GAgentBaseExceptionEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core; 4 | 5 | [GenerateSerializer] 6 | public class GAgentBaseExceptionEvent : EventBase 7 | { 8 | [Id(0)] public required GrainId GrainId { get; set; } 9 | [Id(1)] public required string ExceptionMessage { get; set; } 10 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/PublishingGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Aevatar.Core; 5 | 6 | [GenerateSerializer] 7 | public class PublishingAgentState : StateBase 8 | { 9 | } 10 | 11 | [GenerateSerializer] 12 | public class PublishingStateLogEvent : StateLogEventBase 13 | { 14 | } 15 | 16 | [GAgent] 17 | public class PublishingGAgent : GAgentBase, IPublishingGAgent 18 | { 19 | public override Task GetDescriptionAsync() 20 | { 21 | return Task.FromResult("Agent to be used for publishing new events."); 22 | } 23 | 24 | public async Task PublishEventAsync(T @event, params IGAgent[] agents) where T : EventBase 25 | { 26 | if (@event == null) 27 | { 28 | throw new ArgumentNullException(nameof(@event)); 29 | } 30 | 31 | await RegisterManyAsync(agents.ToList()); 32 | 33 | Logger.LogInformation($"PublishingAgent publish {@event}"); 34 | await PublishAsync(@event); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/StateBaseAsyncObserver.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Orleans.Streams; 3 | 4 | namespace Aevatar.Core; 5 | 6 | public class StateBaseAsyncObserver : IAsyncObserver 7 | { 8 | private readonly Func _func; 9 | 10 | public Guid GAgentGuid { get; set; } 11 | 12 | public StateBaseAsyncObserver(Func func) 13 | { 14 | _func = func; 15 | } 16 | 17 | public async Task OnNextAsync(StateWrapperBase item, StreamSequenceToken? token = null) 18 | { 19 | await _func(item); 20 | } 21 | 22 | public Task OnCompletedAsync() 23 | { 24 | return Task.CompletedTask; 25 | } 26 | 27 | public Task OnErrorAsync(Exception ex) 28 | { 29 | return Task.CompletedTask; 30 | } 31 | } -------------------------------------------------------------------------------- /src/Aevatar.Core/StateProjectionAsyncObserver.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using Orleans.Streams; 5 | 6 | namespace Aevatar.Core; 7 | 8 | public class StateProjectionAsyncObserver : IAsyncObserver 9 | { 10 | private readonly IEnumerable _stateProjectors; 11 | private readonly ILogger _logger; 12 | 13 | public StateProjectionAsyncObserver(IEnumerable stateProjectors, 14 | ILogger logger) 15 | { 16 | _stateProjectors = stateProjectors; 17 | _logger = logger; 18 | } 19 | 20 | public static StateProjectionAsyncObserver Create(IEnumerable stateProjectors, 21 | IServiceProvider serviceProvider) 22 | { 23 | var logger = serviceProvider.GetService()?.CreateLogger(); 24 | return new StateProjectionAsyncObserver(stateProjectors, logger); 25 | } 26 | 27 | public Task OnNextAsync(StateWrapperBase item, StreamSequenceToken? token = null) 28 | { 29 | return Task.WhenAll(_stateProjectors.Select(projector => projector.ProjectAsync(item))); 30 | } 31 | 32 | public Task OnCompletedAsync() 33 | { 34 | return Task.CompletedTask; 35 | } 36 | 37 | public Task OnErrorAsync(Exception ex) 38 | { 39 | _logger?.LogError(ex, "Error invoking state projector."); 40 | return Task.CompletedTask; 41 | } 42 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Aevatar.EventSourcing.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar.EventSourcing.Core 9 | Event Sourcing Abstraction for Orleans 10 | AElf 11 | The abstraction of event sourcing for Orleans to be used in aevatar Framework. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/AevatarEventSourcingConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.EventSourcing.Core; 2 | 3 | // ReSharper disable once InconsistentNaming 4 | public class AevatarEventSourcingConstants 5 | { 6 | public const int MaxEntriesInNotifications = 256; 7 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Exceptions/ReadFromLogStorageFailed.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.EventSourcing.Common; 3 | 4 | namespace Aevatar.EventSourcing.Core.Exceptions; 5 | 6 | [Serializable] 7 | [GenerateSerializer] 8 | public sealed class ReadFromLogStorageFailed : PrimaryOperationFailed 9 | { 10 | /// 11 | public override string ToString() 12 | { 13 | return $"read logs from storage failed: caught {Exception.GetType().Name}: {Exception.Message}"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Exceptions/ReadFromSnapshotStorageFailed.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.EventSourcing.Common; 3 | 4 | namespace Aevatar.EventSourcing.Core.Exceptions; 5 | 6 | [Serializable] 7 | [GenerateSerializer] 8 | public sealed class ReadFromSnapshotStorageFailed : PrimaryOperationFailed 9 | { 10 | /// 11 | public override string ToString() 12 | { 13 | return $"read state from snapshot storage failed: caught {Exception.GetType().Name}: {Exception.Message}"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Exceptions/UpdateLogStorageFailed.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.EventSourcing.Common; 3 | 4 | namespace Aevatar.EventSourcing.Core.Exceptions; 5 | 6 | [Serializable] 7 | [GenerateSerializer] 8 | public sealed class UpdateLogStorageFailed : PrimaryOperationFailed 9 | { 10 | /// 11 | public override string ToString() 12 | { 13 | return $"write logs to storage failed: caught {Exception.GetType().Name}: {Exception.Message}"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Exceptions/UpdateSnapshotStorageFailed.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.EventSourcing.Common; 3 | 4 | namespace Aevatar.EventSourcing.Core.Exceptions; 5 | 6 | [Serializable] 7 | [GenerateSerializer] 8 | public sealed class UpdateSnapshotStorageFailed : PrimaryOperationFailed 9 | { 10 | /// 11 | public override string ToString() 12 | { 13 | return $"write state to snapshot storage failed: caught {Exception.GetType().Name}: {Exception.Message}"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Hosting/InMemoryStorageSiloBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Hosting; 2 | using Orleans.Providers; 3 | 4 | namespace Aevatar.EventSourcing.Core.Hosting; 5 | 6 | public static class InMemoryStorageSiloBuilderExtensions 7 | { 8 | public static ISiloBuilder AddInMemoryBasedLogConsistencyProviderAsDefault(this ISiloBuilder builder) 9 | { 10 | return builder.AddInMemoryBasedLogConsistencyProvider(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME); 11 | } 12 | 13 | public static ISiloBuilder AddInMemoryBasedLogConsistencyProvider(this ISiloBuilder builder, string name) 14 | { 15 | return builder.ConfigureServices(services => services.AddInMemoryBasedLogConsistencyProvider(name)); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/InMemoryLogConsistentStorageFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Aevatar.EventSourcing.Core; 4 | 5 | public static class InMemoryLogConsistentStorageFactory 6 | { 7 | public static InMemoryLogConsistentStorage Create(IServiceProvider serviceProvider, object? name) 8 | { 9 | return ActivatorUtilities.CreateInstance(serviceProvider); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Snapshot/ViewStateSnapshot.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | 3 | namespace Aevatar.EventSourcing.Core.Snapshot; 4 | 5 | [Serializable] 6 | [GenerateSerializer] 7 | public sealed class ViewStateSnapshot : IGrainState> 8 | where TLogView : class, new() 9 | { 10 | [Id(0)] public string ETag { get; set; } = null!; 11 | [Id(1)] public bool RecordExists { get; set; } 12 | [Id(2)] public ViewStateSnapshotWithMetadata State { get; set; } = new(); 13 | 14 | public ViewStateSnapshot() 15 | { 16 | 17 | } 18 | 19 | public ViewStateSnapshot(TLogView initialView) 20 | { 21 | State = new ViewStateSnapshotWithMetadata(initialView); 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return $"v{State.SnapshotVersion} Flags={State.WriteVector} ETag={ETag} Data={State.Snapshot}"; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Snapshot/ViewStateSnapshotWithMetadata.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using Orleans.EventSourcing.Common; 3 | 4 | namespace Aevatar.EventSourcing.Core.Snapshot; 5 | 6 | [Serializable] 7 | [GenerateSerializer] 8 | public sealed class ViewStateSnapshotWithMetadata 9 | where TLogView : class, new() 10 | { 11 | [Id(0)] public TLogView Snapshot { get; set; } = new(); 12 | [Id(1)] public int SnapshotVersion { get; set; } = 0; 13 | [Id(2)] public string WriteVector { get; set; } = string.Empty; 14 | 15 | public ViewStateSnapshotWithMetadata() 16 | { 17 | 18 | } 19 | 20 | public ViewStateSnapshotWithMetadata(TLogView initialState) 21 | { 22 | Snapshot = initialState; 23 | } 24 | 25 | public bool GetBit(string replica) 26 | { 27 | return StringEncodedWriteVector.GetBit(WriteVector, replica); 28 | } 29 | 30 | public bool FlipBit(string replica) 31 | { 32 | var str = WriteVector; 33 | var result = StringEncodedWriteVector.FlipBit(ref str, replica); 34 | WriteVector = str; 35 | return result; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Storage/ILogConsistentStorage.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | 3 | namespace Aevatar.EventSourcing.Core.Storage; 4 | 5 | public interface ILogConsistentStorage 6 | { 7 | /// 8 | /// Read event logs from database. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | Task> ReadAsync(string grainTypeName, GrainId grainId, int fromVersion, 17 | int maxCount); 18 | 19 | /// 20 | /// Get stored event logs count. 21 | /// 22 | /// 23 | /// 24 | /// 25 | Task GetLastVersionAsync(string grainTypeName, GrainId grainId); 26 | 27 | /// 28 | /// Save event logs to database. 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | Task AppendAsync(string grainTypeName, GrainId grainId, IList entries, 37 | int expectedVersion); 38 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Storage/LogConsistencyProvider.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.EventSourcing.Core.Storage; 2 | using Orleans.EventSourcing; 3 | using Orleans.Serialization; 4 | using Orleans.Storage; 5 | 6 | namespace Aevatar.EventSourcing.Core.Storage; 7 | 8 | public class LogConsistencyProvider : ILogViewAdaptorFactory 9 | { 10 | private readonly ILogConsistentStorage _logConsistentStorage; 11 | private readonly DeepCopier _deepCopier; 12 | private readonly IServiceProvider _serviceProvider; 13 | 14 | public bool UsesStorageProvider => true; 15 | 16 | public LogConsistencyProvider(ILogConsistentStorage logConsistentStorage, DeepCopier deepCopier, IServiceProvider serviceProvider) 17 | { 18 | _logConsistentStorage = logConsistentStorage; 19 | _deepCopier = deepCopier; 20 | _serviceProvider = serviceProvider; 21 | } 22 | 23 | public ILogViewAdaptor MakeLogViewAdaptor( 24 | ILogViewAdaptorHost hostGrain, TLogView initialState, 25 | string grainTypeName, IGrainStorage grainStorage, ILogConsistencyProtocolServices services) 26 | where TLogView : class, new() 27 | where TLogEntry : class 28 | { 29 | return new LogViewAdaptor(hostGrain, initialState, grainStorage, grainTypeName, services, 30 | _logConsistentStorage, _deepCopier); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.Core/Storage/LogConsistencyProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Orleans.EventSourcing; 3 | 4 | namespace Aevatar.EventSourcing.Core.Storage; 5 | 6 | public static class LogConsistencyProviderFactory 7 | { 8 | public static ILogViewAdaptorFactory Create(IServiceProvider serviceProvider, object? name) 9 | { 10 | var logConsistentStorage = serviceProvider.GetRequiredKeyedService(name); 11 | return ActivatorUtilities.CreateInstance(serviceProvider, logConsistentStorage); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/Aevatar.EventSourcing.MongoDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar.EventSourcing.MongoDB 9 | aevatar Framework 10 | AElf 11 | The MongoDb implemetion of event sourcing for orleans. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/Configuration/MongoDBGrainStorageConfigurator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Options; 3 | using Orleans.Providers.MongoDB.StorageProviders.Serializers; 4 | using System; 5 | 6 | using Aevatar.EventSourcing.MongoDB.Options; 7 | 8 | namespace Aevatar.EventSourcing.MongoDB.Configuration 9 | { 10 | internal class MongoDBGrainStorageConfigurator : IPostConfigureOptions 11 | { 12 | private readonly IServiceProvider _serviceProvider; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The service provider. 18 | public MongoDBGrainStorageConfigurator(IServiceProvider serviceProvider) 19 | { 20 | _serviceProvider = serviceProvider; 21 | } 22 | 23 | /// 24 | public void PostConfigure(string name, MongoDbStorageOptions options) 25 | { 26 | if (options.GrainStateSerializer == default) 27 | { 28 | // First, try to get a IGrainStateSerializer that was registered with the same name as the State provider 29 | // If none is found, fallback to system wide default 30 | options.GrainStateSerializer = _serviceProvider.GetKeyedService(name) ?? _serviceProvider.GetRequiredService(); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/GrainTypeBsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson.Serialization; 2 | using MongoDB.Bson.Serialization.Serializers; 3 | 4 | namespace Aevatar.EventSourcing.MongoDB; 5 | 6 | using Orleans.Runtime; 7 | 8 | public class GrainTypeBsonSerializer : SerializerBase 9 | { 10 | public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, GrainType value) 11 | { 12 | context.Writer.WriteStartDocument(); 13 | context.Writer.WriteName("Value"); 14 | context.Writer.WriteString(value.Value.ToString()); 15 | context.Writer.WriteEndDocument(); 16 | } 17 | 18 | public override GrainType Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 19 | { 20 | context.Reader.ReadStartDocument(); 21 | var value = context.Reader.ReadString(); 22 | context.Reader.ReadEndDocument(); 23 | return GrainType.Create(value); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/Hosting/MongoDbStorageSiloBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.EventSourcing.MongoDB.Options; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace Aevatar.EventSourcing.MongoDB.Hosting; 5 | 6 | public static class MongoDbStorageSiloBuilderExtensions 7 | { 8 | public static ISiloBuilder AddMongoDbStorageBasedLogConsistencyProvider(this ISiloBuilder builder, string name, 9 | Action configureOptions) 10 | { 11 | return builder.ConfigureServices(service => 12 | service.AddMongoDbBasedLogConsistencyProvider(name, configureOptions)); 13 | } 14 | 15 | public static ISiloBuilder AddMongoDbStorageBasedLogConsistencyProvider(this ISiloBuilder builder, 16 | Action>? configureOptions = null) 17 | { 18 | return builder.ConfigureServices(service => 19 | service.AddMongoDbBasedLogConsistencyProvider("LogStorage", configureOptions)); 20 | } 21 | 22 | public static ISiloBuilder AddMongoDbStorageBasedLogConsistencyProvider(this ISiloBuilder builder, string name, 23 | Action>? configureOptions = null) 24 | { 25 | return builder.ConfigureServices(service => 26 | service.AddMongoDbBasedLogConsistencyProvider(name, configureOptions)); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/IdSpanBsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson.Serialization; 2 | using MongoDB.Bson.Serialization.Serializers; 3 | 4 | public class IdSpanBsonSerializer : SerializerBase 5 | { 6 | public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, IdSpan value) 7 | { 8 | context.Writer.WriteStartDocument(); 9 | context.Writer.WriteName("Value"); 10 | context.Writer.WriteString(value.ToString()); 11 | context.Writer.WriteEndDocument(); 12 | } 13 | 14 | public override IdSpan Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 15 | { 16 | context.Reader.ReadStartDocument(); 17 | var value = context.Reader.ReadString(); 18 | context.Reader.ReadEndDocument(); 19 | return IdSpan.Create(value); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/MongoDbLogConsistentStorageFactory.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.EventSourcing.MongoDB.Options; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Options; 4 | 5 | namespace Aevatar.EventSourcing.MongoDB; 6 | 7 | public static class MongoDbLogConsistentStorageFactory 8 | { 9 | public static MongoDbLogConsistentStorage Create(IServiceProvider serviceProvider, object name) 10 | { 11 | var options = serviceProvider.GetRequiredService>(); 12 | return ActivatorUtilities.CreateInstance(serviceProvider, name, 13 | options.Get(name as string)); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/MongoDbStorageException.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.EventSourcing.MongoDB; 2 | 3 | public class MongoDbStorageException : Exception 4 | { 5 | public MongoDbStorageException() 6 | { 7 | 8 | } 9 | 10 | public MongoDbStorageException(string message) : base(message) 11 | { 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/Options/MongoDbStorageOptions.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Driver; 2 | using Orleans.Storage; 3 | using Orleans.Providers.MongoDB.StorageProviders.Serializers; 4 | 5 | namespace Aevatar.EventSourcing.MongoDB.Options; 6 | 7 | public class MongoDbStorageOptions : IStorageProviderSerializerOptions 8 | { 9 | public int InitStage { get; set; } = ServiceLifecycleStage.ApplicationServices; 10 | 11 | /// 12 | /// This is not in use, it is only for compatibility 13 | /// 14 | public IGrainStorageSerializer GrainStorageSerializer { get; set; } = null!; 15 | 16 | /// 17 | /// The serializer to use for the grain state. 18 | /// 19 | public IGrainStateSerializer GrainStateSerializer { get; set; } = null!; 20 | 21 | [Redact] public MongoClientSettings ClientSettings { get; set; } = null!; 22 | 23 | public string? Database { get; set; } 24 | 25 | [Redact] public MongoCredential? Credentials { get; set; } 26 | } -------------------------------------------------------------------------------- /src/Aevatar.EventSourcing.MongoDB/Options/MongoDbStorageOptionsValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.EventSourcing.MongoDB.Options; 2 | 3 | public class MongoDbStorageOptionsValidator : IConfigurationValidator 4 | { 5 | private readonly MongoDbStorageOptions _options; 6 | private readonly string _name; 7 | 8 | public MongoDbStorageOptionsValidator(MongoDbStorageOptions options, string name) 9 | { 10 | _options = options ?? throw new OrleansConfigurationException( 11 | $"Invalid MongoDbStorageOptions for MongoDbLogConsistentStorage {name}. Options is required."); 12 | _name = name; 13 | } 14 | 15 | public void ValidateConfiguration() 16 | { 17 | if (_options.ClientSettings == null) 18 | { 19 | throw new OrleansConfigurationException( 20 | $"Invalid configuration for {nameof(MongoDbLogConsistentStorage)} with name {_name}. {nameof(MongoDbStorageOptions)}.{nameof(_options.ClientSettings)} is required."); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/AevatarPermissionManagementModule.cs: -------------------------------------------------------------------------------- 1 | using Volo.Abp.Authorization; 2 | using Volo.Abp.Modularity; 3 | using Volo.Abp.PermissionManagement; 4 | using Volo.Abp.PermissionManagement.MongoDB; 5 | 6 | namespace Aevatar.PermissionManagement; 7 | 8 | [DependsOn( 9 | typeof(AbpPermissionManagementApplicationModule), 10 | typeof(AbpPermissionManagementMongoDbModule) 11 | )] 12 | public class AevatarPermissionManagementModule : AbpModule 13 | { 14 | public override void ConfigureServices(ServiceConfigurationContext context) 15 | { 16 | Configure(options => 17 | { 18 | options.IsDynamicPermissionStoreEnabled = true; 19 | }); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/Extensions/OrleansHostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection.Extensions; 2 | using Orleans.Hosting; 3 | using Volo.Abp; 4 | 5 | namespace Aevatar.PermissionManagement.Extensions; 6 | 7 | public static class OrleansHostExtensions 8 | { 9 | public static ISiloBuilder UseAevatarPermissionManagement(this ISiloBuilder builder) 10 | { 11 | var abpApplication = AbpApplicationFactory.Create(); 12 | abpApplication.Initialize(); 13 | return builder 14 | .AddIncomingGrainCallFilter() 15 | .ConfigureServices(services => 16 | { 17 | services.TryAdd(abpApplication.Services); 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/IPermissionInfoProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.PermissionManagement; 2 | 3 | public interface IPermissionInfoProvider 4 | { 5 | List GetAllPermissionInfos(); 6 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/PermissionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.PermissionManagement; 2 | 3 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] 4 | public class PermissionAttribute : Attribute 5 | { 6 | public string Name { get; } 7 | public string? GroupName { get; } 8 | public string? DisplayName { get; } 9 | 10 | public PermissionAttribute(string name, string? groupName = null, string? displayName = null) 11 | { 12 | Name = name; 13 | GroupName = groupName; 14 | DisplayName = displayName; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/PermissionInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.PermissionManagement; 2 | 3 | public class PermissionInfo 4 | { 5 | public required string Type { get; set; } 6 | public required string GroupName { get; set; } 7 | public required string Name { get; set; } 8 | public string DisplayName { get; set; } = string.Empty; 9 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/PermissionStateBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Aevatar.Core.Abstractions; 4 | 5 | namespace Aevatar.PermissionManagement; 6 | 7 | public interface IPermissionState 8 | { 9 | bool IsPublic { get; set; } 10 | HashSet AuthorizedUserIds { get; set; } 11 | } 12 | 13 | [GenerateSerializer] 14 | public abstract class PermissionStateBase : StateBase, IPermissionState 15 | { 16 | [Id(0)] public bool IsPublic { get; set; } = true; 17 | [Id(1)] public HashSet AuthorizedUserIds { get; set; } = new(); 18 | } -------------------------------------------------------------------------------- /src/Aevatar.PermissionManagement/UserContext.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.PermissionManagement; 2 | 3 | [GenerateSerializer] 4 | public class UserContext 5 | { 6 | [Id(0)] public Guid UserId { get; set; } 7 | [Id(1)] public string[] Roles { get; set; } = []; 8 | [Id(2)] public string UserName { get; set; } = string.Empty; 9 | [Id(3)] public string Email { get; set; } = string.Empty; 10 | [Id(4)] public string ClientId { get; set; } = string.Empty; 11 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Aevatar.Plugins.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar.Plugins 9 | aevatar Framework 10 | AElf 11 | The plugin GAgent mechanism implementation for aevatar framework. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Aevatar.Plugins/DbContexts/AevatarMongoDbContextBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Options; 3 | using MongoDB.Driver; 4 | using Volo.Abp.DependencyInjection; 5 | using Volo.Abp.MongoDB; 6 | 7 | namespace Aevatar.Plugins.DbContexts; 8 | 9 | public class AevatarMongoDbContextBase : AbpMongoDbContext 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | 13 | public AevatarMongoDbContextBase(IServiceProvider serviceProvider) 14 | { 15 | _serviceProvider = serviceProvider; 16 | LazyServiceProvider = new AbpLazyServiceProvider(serviceProvider); 17 | } 18 | 19 | protected void InitializeModelSource() 20 | { 21 | ModelSource = new MongoModelSource(); 22 | ModelSource.GetModel(this); 23 | } 24 | 25 | public override void InitializeDatabase(IMongoDatabase database, IMongoClient client, 26 | IClientSessionHandle? sessionHandle) 27 | { 28 | var options = _serviceProvider.GetRequiredService>().Value; 29 | base.InitializeDatabase(database, client, sessionHandle); 30 | } 31 | 32 | protected string GetCollectionPrefix() 33 | { 34 | var pluginsOptions = _serviceProvider.GetRequiredService>().Value; 35 | var hostId = pluginsOptions.HostId; 36 | return hostId.IsNullOrEmpty() ? "StreamStorage" : $"Stream{hostId}"; 37 | } 38 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/DbContexts/PluginCodeStorageMongoDbContext.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.Entities; 2 | using Aevatar.Plugins.GAgents; 3 | using MongoDB.Driver; 4 | using Volo.Abp.Data; 5 | using Volo.Abp.MongoDB; 6 | 7 | namespace Aevatar.Plugins.DbContexts; 8 | 9 | [ConnectionStringName("Orleans")] 10 | public class PluginCodeStorageMongoDbContext(IServiceProvider serviceProvider) 11 | : AevatarMongoDbContextBase(serviceProvider) 12 | { 13 | public IMongoCollection PluginCodeStorage 14 | { 15 | get 16 | { 17 | InitializeModelSource(); 18 | return Collection(); 19 | } 20 | } 21 | 22 | protected override void CreateModel(IMongoModelBuilder modelBuilder) 23 | { 24 | base.CreateModel(modelBuilder); 25 | 26 | modelBuilder.Entity(b => 27 | { 28 | var prefix = GetCollectionPrefix(); 29 | b.CollectionName = $"{prefix}{typeof(PluginCodeStorageGAgent).FullName!}"; 30 | }); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/DbContexts/PluginLoadStatusMongoDbContext.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.Entities; 2 | using MongoDB.Driver; 3 | using Volo.Abp.Data; 4 | using Volo.Abp.MongoDB; 5 | 6 | namespace Aevatar.Plugins.DbContexts; 7 | 8 | [ConnectionStringName("Orleans")] 9 | public class PluginLoadStatusMongoDbContext(IServiceProvider serviceProvider) 10 | : AevatarMongoDbContextBase(serviceProvider) 11 | { 12 | public IMongoCollection PluginLoadStatus 13 | { 14 | get 15 | { 16 | InitializeModelSource(); 17 | return Collection(); 18 | } 19 | } 20 | 21 | protected override void CreateModel(IMongoModelBuilder modelBuilder) 22 | { 23 | base.CreateModel(modelBuilder); 24 | modelBuilder.Entity(b => 25 | { 26 | var prefix = GetCollectionPrefix(); 27 | b.CollectionName = $"{prefix}PluginLoadStatus"; 28 | }); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/DbContexts/TenantPluginCodeMongoDbContext.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.Entities; 2 | using Aevatar.Plugins.GAgents; 3 | using MongoDB.Driver; 4 | using Volo.Abp.Data; 5 | using Volo.Abp.MongoDB; 6 | 7 | namespace Aevatar.Plugins.DbContexts; 8 | 9 | [ConnectionStringName("Orleans")] 10 | public class TenantPluginCodeMongoDbContext(IServiceProvider serviceProvider) 11 | : AevatarMongoDbContextBase(serviceProvider) 12 | { 13 | public IMongoCollection TenantPluginCode 14 | { 15 | get 16 | { 17 | InitializeModelSource(); 18 | return Collection(); 19 | } 20 | } 21 | 22 | protected override void CreateModel(IMongoModelBuilder modelBuilder) 23 | { 24 | base.CreateModel(modelBuilder); 25 | modelBuilder.Entity(b => 26 | { 27 | var prefix = GetCollectionPrefix(); 28 | b.CollectionName = $"{prefix}{typeof(TenantPluginCodeGAgent).FullName!}"; 29 | }); 30 | } 31 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Entities/PluginLoadStatus.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions.Plugin; 2 | using MongoDB.Bson.Serialization.Attributes; 3 | using Volo.Abp.Domain.Entities; 4 | 5 | namespace Aevatar.Plugins.Entities; 6 | 7 | [BsonIgnoreExtraElements] 8 | public class PluginLoadStatusDocument : Entity 9 | { 10 | [BsonElement("TenantId")] public Guid TenantId { get; set; } 11 | 12 | [BsonElement("LoadStatus")] public Dictionary LoadStatus { get; set; } = new(); 13 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Entities/TenantPluginCode.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson.Serialization.Attributes; 2 | using Volo.Abp.Domain.Entities; 3 | 4 | namespace Aevatar.Plugins.Entities; 5 | 6 | [BsonIgnoreExtraElements] 7 | public class TenantPluginCodeSnapshotDocument : Entity 8 | { 9 | 10 | [BsonElement("_etag")] public string Etag { get; set; } 11 | 12 | [BsonElement("_doc")] public TenantPluginCodeDocEntity Doc { get; set; } 13 | } 14 | 15 | [BsonIgnoreExtraElements] 16 | public class TenantPluginCodeDocEntity 17 | { 18 | [BsonElement("__id")] 19 | public string InternalId { get; set; } 20 | 21 | [BsonElement("__type")] 22 | public string Type { get; set; } 23 | 24 | [BsonElement("Snapshot")] 25 | public TenantPluginCodeSnapshotEntity Snapshot { get; set; } 26 | } 27 | 28 | [BsonIgnoreExtraElements] 29 | public class TenantPluginCodeSnapshotEntity 30 | { 31 | [BsonElement("__id")] 32 | public string InternalId { get; set; } 33 | 34 | [BsonElement("__type")] 35 | public string Type { get; set; } 36 | 37 | [BsonElement("CodeStorageGuids")] 38 | public CodeStorageGuidList CodeStorageGuids { get; set; } 39 | } 40 | 41 | [BsonIgnoreExtraElements] 42 | public class CodeStorageGuidList 43 | { 44 | [BsonElement("__type")] public string Type { get; set; } 45 | 46 | [BsonElement("__values")] public List Values { get; set; } = new(); 47 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/PluginGAgentLoadOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Plugins; 2 | 3 | public class PluginGAgentLoadOptions 4 | { 5 | public Guid TenantId { get; set; } 6 | public string? HostId { get; set; } 7 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/PluginLoader.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Plugins; 2 | 3 | /// 4 | /// Used to load plugins from a directory. 5 | /// Keep this helper for testing. 6 | /// 7 | public static class PluginLoader 8 | { 9 | public static List LoadPlugins(string pluginsDirectory) 10 | { 11 | var pluginCodeList = new List(); 12 | 13 | if (Directory.Exists(pluginsDirectory)) 14 | { 15 | var dllFiles = Directory.GetFiles(pluginsDirectory, "*.dll"); 16 | 17 | foreach (var dllFile in dllFiles) 18 | { 19 | var bytes = File.ReadAllBytes(dllFile); 20 | pluginCodeList.Add(bytes); 21 | } 22 | } 23 | 24 | return pluginCodeList; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Repositories/IPluginCodeStorageRepository.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.Entities; 2 | using Volo.Abp.Domain.Repositories; 3 | 4 | namespace Aevatar.Plugins.Repositories; 5 | 6 | public interface IPluginCodeStorageRepository : IRepository 7 | { 8 | Task GetPluginCodeByGAgentPrimaryKey(Guid primaryKey); 9 | Task> GetPluginDescriptionsByGAgentPrimaryKey(Guid primaryKey); 10 | Task> GetPluginCodesByGAgentPrimaryKeys(IReadOnlyList primaryKeys); 11 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Repositories/IPluginLoadStatusRepository.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions.Plugin; 2 | using Aevatar.Plugins.Entities; 3 | using Volo.Abp.Domain.Repositories; 4 | 5 | namespace Aevatar.Plugins.Repositories; 6 | 7 | public interface IPluginLoadStatusRepository : IRepository 8 | { 9 | /// 10 | /// Get plugin load status dictionary by plugin code primary key. 11 | /// 12 | Task> GetPluginLoadStatusAsync(Guid tenantId); 13 | 14 | /// 15 | /// Set plugin load status dictionary by plugin code primary key. 16 | /// 17 | Task SetPluginLoadStatusAsync(Guid tenantId, Dictionary status); 18 | 19 | Task ClearPluginLoadStatusAsync(); 20 | } -------------------------------------------------------------------------------- /src/Aevatar.Plugins/Repositories/ITenantPluginCodeRepository.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.Entities; 2 | using Volo.Abp.Domain.Repositories; 3 | 4 | namespace Aevatar.Plugins.Repositories; 5 | 6 | public interface ITenantPluginCodeRepository : IRepository 7 | { 8 | Task?> GetGAgentPrimaryKeysByTenantIdAsync(Guid tenantId); 9 | } -------------------------------------------------------------------------------- /src/Aevatar.TestKit/DefaultTestKitBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit; 2 | 3 | public class DefaultTestKitBase : TestKitBase; -------------------------------------------------------------------------------- /src/Aevatar.TestKit/EventSourcing/TestCloner.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Serialization.Cloning; 2 | 3 | public class TestCloner : IDeepCopier 4 | { 5 | public T DeepCopy(T input, CopyContext context) 6 | { 7 | // Implement deep copy logic here 8 | return input; 9 | } 10 | } -------------------------------------------------------------------------------- /src/Aevatar.TestKit/EventSourcing/TestCodec.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Serialization.Codecs; 2 | using Orleans.Serialization.Buffers; 3 | using System.Buffers; 4 | using Orleans.Serialization.WireProtocol; 5 | 6 | public class TestCodec : IFieldCodec 7 | { 8 | public void WriteField(ref Writer writer, uint fieldIdDelta, Type expectedType, T value) where TBufferWriter : IBufferWriter 9 | { 10 | // Implement serialization logic here 11 | } 12 | 13 | public T ReadValue(ref Reader reader, Field field) 14 | { 15 | return default; 16 | } 17 | } -------------------------------------------------------------------------------- /src/Aevatar.TestKit/EventSourcing/TestLogConsistencyProtocolServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Orleans.EventSourcing; 3 | 4 | namespace Aevatar.TestKit; 5 | 6 | public class TestLogConsistencyProtocolServices : ILogConsistencyProtocolServices 7 | { 8 | public T DeepCopy(T value) 9 | { 10 | // Implement a simple deep copy logic or use a library like Newtonsoft.Json 11 | return value; // Simplified for demonstration 12 | } 13 | 14 | public void ProtocolError(string msg, bool throwexception) 15 | { 16 | if (throwexception) 17 | { 18 | throw new InvalidOperationException(msg); 19 | } 20 | else 21 | { 22 | Console.WriteLine($"Protocol Error: {msg}"); 23 | } 24 | } 25 | 26 | public void CaughtException(string where, Exception e) 27 | { 28 | Console.WriteLine($"Exception caught in {where}: {e}"); 29 | } 30 | 31 | public void CaughtUserCodeException(string callback, string where, Exception e) 32 | { 33 | Console.WriteLine($"User code exception in {callback} at {where}: {e}"); 34 | } 35 | 36 | public void Log(LogLevel level, string format, params object[] args) 37 | { 38 | Console.WriteLine($"Log [{level}]: {string.Format(format, args)}"); 39 | } 40 | 41 | public GrainId GrainId { get; } 42 | public string MyClusterId { get; } = "TestCluster"; 43 | } 44 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/EventSourcing/TestLogConsistencyProvider.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.EventSourcing.Core; 2 | using Aevatar.EventSourcing.Core.Storage; 3 | using Orleans.EventSourcing; 4 | using Orleans.Storage; 5 | 6 | namespace Aevatar.TestKit; 7 | 8 | public class TestLogConsistencyProvider : ILogViewAdaptorFactory 9 | { 10 | private readonly IGrainStorage _grainStorage; 11 | 12 | public TestLogConsistencyProvider(IGrainStorage grainStorage) 13 | { 14 | _grainStorage = grainStorage; 15 | } 16 | 17 | public ILogViewAdaptor MakeLogViewAdaptor( 18 | ILogViewAdaptorHost hostGrain, TLogView initialState, 19 | string grainTypeName, IGrainStorage grainStorage, ILogConsistencyProtocolServices services) 20 | where TLogView : class, new() where TLogEntry : class 21 | { 22 | return new LogViewAdaptor(hostGrain, initialState, _grainStorage, grainTypeName, 23 | new TestLogConsistencyProtocolServices(), new InMemoryLogConsistentStorage(), null); 24 | } 25 | 26 | public bool UsesStorageProvider => true; 27 | } 28 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/GlobalUsing.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Threading; 3 | global using System.Threading.Tasks; 4 | global using System.Collections.Generic; 5 | global using System.Linq; 6 | global using Orleans.Runtime; 7 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/README.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | - [OrleansTestKit](https://github.com/OrleansContrib/OrleansTestKit) -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Reminders/ReminderContextHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Orleans.Runtime; 5 | 6 | namespace Aevatar.TestKit.Reminders; 7 | 8 | public class ReminderContextHandler : IDisposable 9 | { 10 | private bool _disposed; 11 | 12 | /// 13 | public void Dispose() 14 | { 15 | Dispose(true); 16 | GC.SuppressFinalize(this); 17 | } 18 | 19 | public Task SetActivationContext(IGrainContext context, CancellationToken token = default) => 20 | _disposed 21 | ? throw new ObjectDisposedException(nameof(ReminderContextHandler)) 22 | : ReminderContextHolder.Instance.SetReminderContext(context, token); 23 | 24 | protected virtual void Dispose(bool disposing) 25 | { 26 | if (!_disposed) 27 | { 28 | if (disposing) 29 | { 30 | ReminderContextHolder.Instance.ReleaseReminderContext(); 31 | } 32 | 33 | _disposed = true; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Reminders/ReminderContextHolder.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Orleans.Runtime; 3 | 4 | namespace Aevatar.TestKit.Reminders; 5 | 6 | public sealed class ReminderContextHolder : IDisposable 7 | { 8 | private const string RuntimeMethod = "SetExecutionContext"; 9 | 10 | private const string RuntimeNamespace = "Orleans.Runtime.RuntimeContext"; 11 | 12 | private static readonly Lazy Lazy = new(() => new ReminderContextHolder()); 13 | 14 | private readonly MethodInfo _runtimeContextMethod; 15 | 16 | private readonly SemaphoreSlim _slim; 17 | 18 | private ReminderContextHolder() 19 | { 20 | _slim = new SemaphoreSlim(1); 21 | var assembly = typeof(GrainId).Assembly; 22 | var contextType = assembly.GetType(RuntimeNamespace)!; 23 | _runtimeContextMethod = contextType.GetMethod(RuntimeMethod, BindingFlags.NonPublic | BindingFlags.Static)!; 24 | } 25 | 26 | public static ReminderContextHolder Instance => 27 | Lazy.Value; 28 | 29 | public void Dispose() => 30 | _slim.Dispose(); 31 | 32 | public void ReleaseReminderContext() 33 | { 34 | try 35 | { 36 | _runtimeContextMethod.Invoke(null, new object?[] { null, null }); 37 | } 38 | finally 39 | { 40 | _slim.Release(); 41 | } 42 | } 43 | 44 | public async Task SetReminderContext(IGrainContext context, CancellationToken token = default) 45 | { 46 | await _slim.WaitAsync(token); 47 | _runtimeContextMethod.Invoke(null, new object?[] { context, null }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Reminders/ReminderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | 3 | namespace Aevatar.TestKit; 4 | 5 | /// 6 | /// Extensions for test kit reminder firing. 7 | /// 8 | public static class ReminderExtensions 9 | { 10 | /// 11 | /// Fire all reminders for this silo. 12 | /// 13 | /// The test kit silo. 14 | /// The tick status. 15 | /// A representing the result of the asynchronous operation. 16 | public static Task FireAllReminders(this TestKitSilo silo, TickStatus tickStatus = default) 17 | { 18 | ArgumentNullException.ThrowIfNull(silo); 19 | 20 | return silo.ReminderRegistry.FireAllReminders(tickStatus); 21 | } 22 | 23 | /// 24 | /// Fire specific reminder for this silo. 25 | /// 26 | /// The test kit silo. 27 | /// The reminder to fire. 28 | /// The tick status. 29 | /// A representing the result of the asynchronous operation. 30 | public static Task FireReminder(this TestKitSilo silo, string reminderName, TickStatus tickStatus = default) 31 | { 32 | ArgumentNullException.ThrowIfNull(silo); 33 | ArgumentNullException.ThrowIfNull(reminderName); 34 | 35 | return silo.ReminderRegistry.FireReminder(reminderName, tickStatus); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Reminders/TestReminder.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | 3 | namespace Aevatar.TestKit.Reminders; 4 | 5 | public sealed class TestReminder : 6 | IGrainReminder 7 | { 8 | public TestReminder(string reminderName, TimeSpan dueTime, TimeSpan period) 9 | { 10 | ReminderName = reminderName ?? throw new ArgumentNullException(nameof(reminderName)); 11 | DueTime = dueTime; 12 | Period = period; 13 | } 14 | 15 | public TimeSpan DueTime { get; } 16 | 17 | public TimeSpan Period { get; } 18 | 19 | public string ReminderName { get; } 20 | } 21 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Storage/IStorageStats.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit.Storage; 2 | 3 | public interface IStorageStats 4 | { 5 | TestStorageStats Stats { get; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Storage/TestStorage.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit.Storage; 2 | 3 | internal class TestStorage : IStorageStats, IPersistentState 4 | { 5 | public TestStorage() : this(CreateState()) 6 | { 7 | } 8 | 9 | public TestStorage(TState state) 10 | { 11 | Stats = new TestStorageStats { Reads = -1 }; 12 | State = state; 13 | } 14 | 15 | public string Etag => string.Empty; 16 | 17 | public bool RecordExists { get; private set; } 18 | 19 | public TState State { get; set; } 20 | 21 | public Task ClearStateAsync() 22 | { 23 | State = CreateState(); 24 | Stats.Clears++; 25 | RecordExists = false; 26 | return Task.CompletedTask; 27 | } 28 | 29 | public Task ReadStateAsync() 30 | { 31 | Stats.Reads++; 32 | return Task.CompletedTask; 33 | } 34 | 35 | public Task WriteStateAsync() 36 | { 37 | Stats.Writes++; 38 | RecordExists = true; 39 | return Task.CompletedTask; 40 | } 41 | 42 | public TestStorageStats Stats { get; } 43 | 44 | private static TState CreateState() 45 | { 46 | if (!typeof(TState).IsValueType && typeof(TState).GetConstructor(Type.EmptyTypes) == null) 47 | { 48 | throw new NotSupportedException( 49 | $"No parameterless constructor defined for {typeof(TState).Name}. This is currently not supported"); 50 | } 51 | 52 | return Activator.CreateInstance(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Storage/TestStorageStats.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit.Storage; 2 | 3 | public sealed class TestStorageStats 4 | { 5 | public int Clears { get; set; } 6 | 7 | public int Reads { get; set; } 8 | 9 | public int Writes { get; set; } 10 | 11 | public void ResetCounts() 12 | { 13 | Writes = 0; 14 | Reads = 0; 15 | Clears = 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Streams/TestStreamProvider.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | using Orleans.Streams; 3 | 4 | namespace Aevatar.TestKit.Streams; 5 | 6 | public sealed class TestStreamProvider : IStreamProvider 7 | { 8 | private readonly TestKitOptions _options; 9 | 10 | private readonly Dictionary _streams = new(); 11 | 12 | public TestStreamProvider(TestKitOptions options) => 13 | _options = options ?? throw new ArgumentNullException(nameof(options)); 14 | 15 | /// 16 | public bool IsRewindable { get; } 17 | 18 | /// 19 | public string Name { get; private set; } = string.Empty; 20 | 21 | public TestStream AddStreamProbe(StreamId streamId) 22 | { 23 | var stream = new TestStream(streamId, Name); 24 | _streams.Add(streamId, stream); 25 | return stream; 26 | } 27 | 28 | /// 29 | public IAsyncStream GetStream(StreamId streamId) 30 | { 31 | if (_streams.TryGetValue(streamId, out var stream)) 32 | { 33 | return (IAsyncStream)stream; 34 | } 35 | 36 | if (_options.StrictStreamProbes) 37 | { 38 | throw new Exception($"Unable to find stream {streamId}. Ensure a stream probe was added"); 39 | } 40 | 41 | stream = AddStreamProbe(streamId); 42 | return (IAsyncStream)stream; 43 | } 44 | 45 | public Task Init(string name) 46 | { 47 | Name = name ?? throw new ArgumentNullException(nameof(name)); 48 | return Task.CompletedTask; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/TestGrainContextAccessor.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit; 2 | 3 | public class TestGrainContextAccessor : IGrainContextAccessor 4 | { 5 | public IGrainContext GrainContext { get; } 6 | 7 | public TestGrainContextAccessor(IGrainContext grainContext) => GrainContext = grainContext; 8 | } 9 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/TestGrainStorage.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Storage; 2 | using Aevatar.TestKit.Storage; 3 | 4 | namespace Aevatar.TestKit; 5 | 6 | public class TestGrainStorage : IGrainStorage 7 | { 8 | private readonly StorageManager _storageManager; 9 | 10 | public TestGrainStorage(StorageManager storageManager) 11 | { 12 | _storageManager = storageManager; 13 | } 14 | 15 | public Task ReadStateAsync(string stateName, GrainId grainId, IGrainState grainState) 16 | { 17 | stateName = $"{grainId.ToString()}.{stateName}"; 18 | var storage = _storageManager.GetStorage(stateName); 19 | grainState.State = storage.State; 20 | return Task.CompletedTask; 21 | } 22 | 23 | public Task WriteStateAsync(string stateName, GrainId grainId, IGrainState grainState) 24 | { 25 | stateName = $"{grainId.ToString()}.{stateName}"; 26 | var storage = _storageManager.GetStorage(stateName); 27 | storage.State = grainState.State; 28 | _storageManager.AddStorage(storage, stateName); 29 | return Task.CompletedTask; 30 | } 31 | 32 | public Task ClearStateAsync(string stateName, GrainId grainId, IGrainState grainState) 33 | { 34 | stateName = $"{grainId.ToString()}.{stateName}"; 35 | var storage = _storageManager.GetStorage(stateName); 36 | storage.State = default!; 37 | return Task.CompletedTask; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Aevatar.TestKit/TestKitBase.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit; 2 | 3 | /// A unit test base class that provides a default mock grain activation context. 4 | public abstract class TestKitBase where TSilo : TestKitSilo, new() 5 | { 6 | protected TSilo Silo { get; } = new(); 7 | } -------------------------------------------------------------------------------- /src/Aevatar.TestKit/TestKitOptions.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Core; 2 | using Aevatar.TestKit.Storage; 3 | 4 | namespace Aevatar.TestKit; 5 | 6 | public sealed class TestKitOptions 7 | { 8 | /// 9 | /// Gets or sets a custom object factory. The default 10 | /// is used if . The custom 11 | /// object may implement to capture and report 12 | /// statistics. 13 | /// 14 | public Func? StorageFactory { get; set; } 15 | 16 | /// 17 | /// Gets or sets a value indicating whether strict Moq probes are enabled for grains. When 18 | /// , all probes must be explicitly added to be used. 19 | /// 20 | public bool StrictGrainProbes { get; set; } 21 | 22 | /// 23 | /// Gets or sets a value indicating whether strict Moq probes are enabled for services. When 24 | /// , all probes must be explicitly added to be used. 25 | /// 26 | public bool StrictServiceProbes { get; set; } 27 | 28 | /// 29 | /// Gets or sets a value indicating whether strict Moq probes are enabled for streams. When 30 | /// , all probes must be explicitly added to be used. 31 | /// 32 | public bool StrictStreamProbes { get; set; } 33 | } 34 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Timers/TimerExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit; 2 | 3 | public static class TimerExtensions 4 | { 5 | public static Task FireAllTimersAsync(this TestKitSilo silo) 6 | { 7 | if (silo == null) 8 | { 9 | throw new ArgumentNullException(nameof(silo)); 10 | } 11 | 12 | return silo.TimerRegistry.FireAllAsync(); 13 | } 14 | 15 | public static Task FireTimerAsync(this TestKitSilo silo, int index) 16 | { 17 | if (silo == null) 18 | { 19 | throw new ArgumentNullException(nameof(silo)); 20 | } 21 | 22 | return silo.TimerRegistry.FireAsync(index); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/TypeHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit; 2 | 3 | public static class TypeHelper 4 | { 5 | /// 6 | /// Alternative version of that supports raw generic types (generic types 7 | /// without any type parameters). 8 | /// 9 | /// To type to determine for whether it derives from . 10 | /// The base type class for which the check is made. 11 | /// Result of the check. 12 | public static bool IsSubclassOfRawGeneric(this Type toCheck, Type baseType) 13 | { 14 | if (toCheck == null) 15 | { 16 | throw new ArgumentNullException(nameof(toCheck)); 17 | } 18 | 19 | if (baseType == null) 20 | { 21 | throw new ArgumentNullException(nameof(baseType)); 22 | } 23 | 24 | while (toCheck != typeof(object)) 25 | { 26 | var cur = toCheck != null && toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 27 | if (baseType == cur) 28 | { 29 | return true; 30 | } 31 | 32 | toCheck = toCheck?.BaseType!; 33 | } 34 | 35 | return false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Utilities/LambdaDisposable.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestKit.Utilities; 2 | 3 | internal sealed class LambdaDisposable(Action action) : IDisposable 4 | { 5 | public void Dispose() 6 | { 7 | action?.Invoke(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Aevatar.TestKit/Utilities/RuntimeContextManager.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Aevatar.TestKit.Reminders; 4 | 5 | internal sealed class RuntimeContextManager 6 | { 7 | public delegate void SetExecutionContextAction(IGrainContext? newContext, out IGrainContext currentContext); 8 | 9 | private RuntimeContextManager() 10 | { 11 | var assembly = typeof(GrainId).Assembly; 12 | var contextType = assembly.GetType("Orleans.Runtime.RuntimeContext")!; 13 | 14 | SetExecutionContext = contextType 15 | .GetMethod("SetExecutionContext", BindingFlags.NonPublic | BindingFlags.Static)! 16 | .CreateDelegate() 17 | ; 18 | } 19 | 20 | public static RuntimeContextManager Instance { get; } = new RuntimeContextManager(); 21 | 22 | public SetExecutionContextAction SetExecutionContext { get; } 23 | 24 | public static IDisposable StartExecutionContext(IGrainContext context) => new RuntimeContextScope(context); 25 | 26 | public void ResetExecutionContext() => SetExecutionContext(null, out _); 27 | 28 | private sealed class RuntimeContextScope : IDisposable 29 | { 30 | public RuntimeContextScope(IGrainContext context) => Instance.SetExecutionContext(context, out _); 31 | 32 | public void Dispose() => Instance.ResetExecutionContext(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Aevatar/Aevatar.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | Dependency 8 | Aevatar 9 | aevatar Framework 10 | AElf 11 | A distributed AI agent-based framework built on Microsoft Orleans for building scalable event-sourced applications. 12 | agent 13 | https://github.com/aevatarAI/aevatar-framework 14 | git 15 | https://github.com/aevatarAI/aevatar-framework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Aevatar/AevatarDefaultConventionalRegistrar.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Volo.Abp.DependencyInjection; 3 | 4 | namespace Aevatar; 5 | 6 | public class AevatarDefaultConventionalRegistrar : DefaultConventionalRegistrar 7 | { 8 | private readonly List _transientTypeSuffixes = 9 | ["Service", "Provider", "Manager", "Factory", "GAgent"]; 10 | 11 | protected override ServiceLifetime? GetServiceLifetimeFromClassHierarchy(Type type) 12 | { 13 | // Get ABP lifetime from ABP interface, ITransientDependency, ISingletonDependency or IScopedDependency 14 | var lifeTime = base.GetServiceLifetimeFromClassHierarchy(type); 15 | if (lifeTime != null) 16 | { 17 | return null; 18 | } 19 | 20 | // If no lifetime interface was found, try to get class with the same interface, 21 | // HelloService -> IHelloService 22 | // HelloManager -> IHelloManager 23 | var interfaceName = "I" + type.Name; 24 | 25 | if (type.GetInterfaces().Any(p => p.Name == interfaceName)) 26 | if (_transientTypeSuffixes.Any(suffix => type.Name.EndsWith(suffix))) 27 | return ServiceLifetime.Transient; 28 | 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /src/Aevatar/ConfigureAevatarGrainActivator.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Orleans.Metadata; 3 | 4 | namespace Aevatar; 5 | 6 | public class ConfigureAevatarGrainActivator : IConfigureGrainTypeComponents 7 | { 8 | private readonly IServiceProvider _serviceProvider; 9 | private readonly GrainClassMap _grainClassMap; 10 | 11 | public ConfigureAevatarGrainActivator(GrainClassMap grainClassMap, IServiceProvider serviceProvider) 12 | { 13 | _serviceProvider = serviceProvider; 14 | _grainClassMap = grainClassMap; 15 | } 16 | 17 | public void Configure(GrainType grainType, GrainProperties properties, GrainTypeSharedContext shared) 18 | { 19 | if (!_grainClassMap.TryGetGrainClass(grainType, out var grainClass)) 20 | { 21 | return; 22 | } 23 | 24 | var instanceActivator = new AevatarGrainActivator(_serviceProvider, grainClass); 25 | shared.SetComponent(instanceActivator); 26 | } 27 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/Aevatar.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(DefaultTargetFramework) 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/AevatarTestKitSilo.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.TestKit; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Aevatar.Core.Tests; 6 | 7 | public class AevatarTestKitSilo : TestKitSilo 8 | { 9 | protected override void ConfigureServices(IServiceCollection services) 10 | { 11 | services.AddTransient(); 12 | services.AddTransient(); 13 | } 14 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestArtifacts/MyArtifact.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestArtifacts; 4 | 5 | [GenerateSerializer] 6 | public class MyArtifactGAgentState : StateBase; 7 | 8 | [GenerateSerializer] 9 | public class MyArtifactStateLogEvent : StateLogEventBase; 10 | 11 | [GenerateSerializer] 12 | public class MyArtifactEvent : EventBase 13 | { 14 | [Id(0)] public Dictionary Content { get; set; } 15 | } 16 | 17 | public interface IMyArtifact : IArtifact 18 | { 19 | string TestMethod(); 20 | } 21 | 22 | [GenerateSerializer] 23 | public class MyArtifact : IMyArtifact 24 | { 25 | public string GetDescription() => "MyArtifact Description, this is for testing."; 26 | 27 | [EventHandler] 28 | public async Task HandleEventAsync(MyArtifactEvent eventData) 29 | { 30 | await Task.Yield(); 31 | } 32 | 33 | public string TestMethod() 34 | { 35 | return "Test"; 36 | } 37 | 38 | public void TransitionState(MyArtifactGAgentState state, StateLogEventBase stateLogEvent) 39 | { 40 | /* custom logic */ 41 | } 42 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/DevelopTaskTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class DevelopTaskTestEvent : EventWithResponseBase 7 | { 8 | [Id(0)] public string Description { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/GroupReloadTestEvent.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Aevatar.Core.Abstractions; 3 | 4 | namespace Aevatar.Core.Tests.TestEvents; 5 | 6 | [Description("Developer Base Event.")] 7 | [GenerateSerializer] 8 | public class GroupReloadTestEvent : EventBase 9 | { 10 | [Id(0)] public Guid GroupManagerGuid { get; set; } 11 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/IncorrectTestEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Tests.TestEvents; 2 | 3 | [GenerateSerializer] 4 | public class IncorrectTestEvent 5 | { 6 | [Id(0)] public string Greeting { get; set; } 7 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/InvestorFeedbackTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class InvestorFeedbackTestEvent : EventBase 7 | { 8 | [Id(0)] public string Content { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/NaiveTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class NaiveTestEvent : EventBase 7 | { 8 | [Id(0)] public string Greeting { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/NewDemandTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class NewDemandTestEvent : EventBase 7 | { 8 | [Id(0)] public string Description { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/NewFeatureCompletedTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class NewFeatureCompletedTestEvent : EventBase 7 | { 8 | [Id(0)] public string PullRequestUrl { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/NotImplEventBaseTestEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Tests.TestEvents; 2 | 3 | [GenerateSerializer] 4 | public class NotImplEventBaseTestEvent 5 | { 6 | [Id(0)] public string Greeting { get; set; } 7 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/ReceiveMessageTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class ReceiveMessageTestEvent : EventBase 7 | { 8 | [Id(0)] public string? MessageId { get; set; } 9 | [Id(1)] public string? ChatId { get; set; } 10 | [Id(2)] public string? Message { get; set; } 11 | [Id(3)] public string? BotName { get; set; } 12 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/ResponseTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class ResponseTestEvent : EventWithResponseBase 7 | { 8 | [Id(0)] public string Greeting { get; set; } 9 | } 10 | 11 | [GenerateSerializer] 12 | public class AnotherResponseTestEvent : EventWithResponseBase 13 | { 14 | [Id(0)] public string Greeting { get; set; } 15 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/SendMessageTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class SendMessageTestEvent : EventBase 7 | { 8 | [Id(0)] public string ChatId { get; set; } 9 | [Id(1)] public string Message { get; set; } 10 | [Id(2)] public string Photo { get; set; } 11 | [Id(3)] public string? Caption { get; set; } 12 | [Id(4)] public string? ReplyMessageId { get; set; } 13 | [Id(5)] public string BotName { get; set; } 14 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/SocialTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class SocialTestEvent : EventBase 7 | { 8 | [Id(0)] public string MessageId { get; set; } 9 | [Id(1)] public string Message { get; set; } 10 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestEvents/WorkingOnTestEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestEvents; 4 | 5 | [GenerateSerializer] 6 | public class WorkingOnTestEvent : EventBase 7 | { 8 | [Id(0)] public string Description { get; set; } 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/ConfigurationTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Aevatar.Core.Tests.TestGAgents; 5 | 6 | [GenerateSerializer] 7 | public class ConfigurationTestGAgentState : StateBase 8 | { 9 | [Id(0)] public List Content { get; set; } 10 | } 11 | 12 | [GenerateSerializer] 13 | public class ConfigurationTestStateLogEvent : StateLogEventBase 14 | { 15 | [Id(0)] public Guid Id { get; set; } 16 | } 17 | 18 | [GenerateSerializer] 19 | public class Configuration : ConfigurationBase 20 | { 21 | [Id(0)] public string InitialGreeting { get; set; } 22 | } 23 | 24 | [GAgent("configurationTest")] 25 | public class ConfigurationTestGAgent : GAgentBase 27 | { 28 | protected override Task PerformConfigAsync(Configuration configuration) 29 | { 30 | if (State.Content.IsNullOrEmpty()) 31 | { 32 | State.Content = []; 33 | } 34 | 35 | State.Content.Add(configuration.InitialGreeting); 36 | return Task.CompletedTask; 37 | } 38 | 39 | public override Task GetDescriptionAsync() 40 | { 41 | return Task.FromResult("This is a test GAgent for initialization testing."); 42 | } 43 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/DeveloperTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | public interface IDeveloperTestGAgent : IGAgent; 8 | 9 | [GenerateSerializer] 10 | public class DeveloperTestGAgentState : NaiveTestGAgentState; 11 | 12 | [GAgent("developer", "test")] 13 | public class DeveloperTestGAgent : GAgentBase, IDeveloperTestGAgent 14 | { 15 | public override Task GetDescriptionAsync() 16 | { 17 | return Task.FromResult("This GAgent acts as a developer."); 18 | } 19 | 20 | public async Task HandleEventAsync(DevelopTaskTestEvent eventData) 21 | { 22 | if (State.Content.IsNullOrEmpty()) 23 | { 24 | State.Content = []; 25 | } 26 | 27 | State.Content.Add(eventData.Description); 28 | 29 | Logger.LogInformation("TEST"); 30 | return new NewFeatureCompletedTestEvent 31 | { 32 | PullRequestUrl = $"PR for {eventData.Description}" 33 | }; 34 | } 35 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/DevelopingLeaderTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | public interface IDevelopingLeaderTestGAgent : IGAgent; 8 | 9 | [GenerateSerializer] 10 | public class DevelopingLeaderTestGAgentState : NaiveTestGAgentState; 11 | 12 | [GAgent("developingLeader", "test")] 13 | public class DevelopingLeaderTestGAgent : GAgentBase, IDevelopingLeaderTestGAgent 14 | { 15 | public override Task GetDescriptionAsync() 16 | { 17 | return Task.FromResult("This GAgent acts as a developing leader."); 18 | } 19 | 20 | public async Task HandleEventAsync(NewDemandTestEvent eventData) 21 | { 22 | await PublishAsync(new DevelopTaskTestEvent 23 | { 24 | Description = $"This is the demand for the task: {eventData.Description}" 25 | }); 26 | } 27 | 28 | public async Task HandleEventAsync(NewFeatureCompletedTestEvent eventData) 29 | { 30 | if (State.Content.IsNullOrEmpty()) 31 | { 32 | State.Content = []; 33 | } 34 | 35 | State.Content.Add(eventData.PullRequestUrl); 36 | 37 | if (State.Content.Count == 3) 38 | { 39 | await PublishAsync(new NewFeatureCompletedTestEvent 40 | { 41 | PullRequestUrl = string.Join("\n", State.Content) 42 | }); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/EventHandlerWithResponseTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | [GenerateSerializer] 8 | public class EventHandlerWithResponseTestGAgentState : StateBase 9 | { 10 | [Id(0)] public List Content { get; set; } 11 | } 12 | 13 | public class EventHandlerWithResponseTestStateLogEvent : StateLogEventBase; 14 | 15 | [GAgent("eventHandlerWithResponseTest", "test")] 16 | public class 17 | EventHandlerWithResponseTestGAgent : GAgentBase 19 | { 20 | public override Task GetDescriptionAsync() 21 | { 22 | return Task.FromResult("This GAgent is used for testing event handler with response."); 23 | } 24 | 25 | [EventHandler] 26 | public async Task ExecuteAsync(ResponseTestEvent responseTestEvent) 27 | { 28 | return new NaiveTestEvent 29 | { 30 | Greeting = responseTestEvent.Greeting 31 | }; 32 | } 33 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/ExceptionHandlingTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestGAgents; 4 | 5 | [GenerateSerializer] 6 | public class ExceptionHandlingTestGAgentState : StateBase 7 | { 8 | [Id(0)] public List ErrorMessages { get; set; } = []; 9 | } 10 | 11 | [GenerateSerializer] 12 | public class ExceptionHandlingTestStateLogEvent : StateLogEventBase 13 | { 14 | 15 | } 16 | 17 | [GAgent] 18 | public class ExceptionHandlingTestGAgent : GAgentBase 19 | { 20 | public override Task GetDescriptionAsync() 21 | { 22 | return Task.FromResult("This is a GAgent for testing exception handling."); 23 | } 24 | 25 | [EventHandler] 26 | public async Task HandleEventHandlingExceptionAsync(EventHandlerExceptionEvent @event) 27 | { 28 | State.ErrorMessages.Add(@event.ExceptionMessage); 29 | } 30 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/FatalEventHandlerTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | [GenerateSerializer] 8 | public class FatalEventHandlerTestGAgentState 9 | { 10 | [Id(0)] public List Content { get; set; } 11 | } 12 | 13 | public class FatalEventHandlerTestStateLogEvent : StateLogEventBase; 14 | 15 | [GAgent] 16 | public class FatalEventHandlerTestGAgent : GAgentBase 17 | { 18 | public override Task GetDescriptionAsync() 19 | { 20 | return Task.FromResult("This GAgent is used for testing invalid event handlers."); 21 | } 22 | 23 | // This will be recognized as an event handler, 24 | // but will throw an exception because NotImplEventBaseTestEvent is not derived from EventBase. 25 | public Task HandleEventAsync(ResponseTestEvent eventData) 26 | { 27 | return Task.FromResult(new NotImplEventBaseTestEvent()); 28 | } 29 | 30 | // This will be recognized as an event handler, 31 | // but will throw an exception because this method doesn't have response event. 32 | public Task HandleEventAsync(AnotherResponseTestEvent eventData) 33 | { 34 | return Task.CompletedTask; 35 | } 36 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/GroupTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core; 2 | using Aevatar.Core.Abstractions; 3 | using Aevatar.Core.Tests.TestEvents; 4 | using Aevatar.Core.Tests.TestStateLogEvents; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Aevatar.Core.Tests.TestGAgents; 8 | 9 | [GenerateSerializer] 10 | public class GroupTestGAgentState : StateBase 11 | { 12 | [Id(0)] public Guid GroupManagerGuid { get; set; } 13 | [Id(1)] public int CalledCount { get; set; } 14 | } 15 | 16 | [GAgent("groupTest", "test")] 17 | public class GroupTestGAgent: GAgentBase 18 | { 19 | public override Task GetDescriptionAsync() 20 | { 21 | return Task.FromResult("For testing reload group."); 22 | } 23 | 24 | public async Task HandleEventAsync(GroupReloadTestEvent eventData) 25 | { 26 | State.GroupManagerGuid = eventData.GroupManagerGuid; 27 | State.CalledCount++; 28 | } 29 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/InvestorTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | public interface IInvestorTestGAgent: IGAgent 8 | { 9 | 10 | } 11 | 12 | [GenerateSerializer] 13 | public class InvestorTestGAgentState : NaiveTestGAgentState 14 | { 15 | 16 | } 17 | 18 | [GAgent("investor", "test")] 19 | public class InvestorTestGAgent : GAgentBase, IInvestorTestGAgent 20 | { 21 | public override Task GetDescriptionAsync() 22 | { 23 | return Task.FromResult("This GAgent acts as a investor."); 24 | } 25 | 26 | public async Task HandleEventAsync(WorkingOnTestEvent eventData) 27 | { 28 | if (State.Content.IsNullOrEmpty()) 29 | { 30 | State.Content = []; 31 | } 32 | 33 | State.Content.Add(eventData.Description); 34 | 35 | await PublishAsync(new InvestorFeedbackTestEvent 36 | { 37 | Content = $"This is the feedback for the event: {eventData.Description}" 38 | }); 39 | } 40 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/MarketingLeaderTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestEvents; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Aevatar.Core.Tests.TestGAgents; 6 | 7 | public interface IMarketingLeaderTestGAgent : IGAgent; 8 | [GenerateSerializer] 9 | public class MarketingLeaderTestGAgentState : NaiveTestGAgentState; 10 | 11 | [GAgent("marketingLeader", "test")] 12 | public class MarketingLeaderTestGAgent : GAgentBase, IMarketingLeaderTestGAgent 13 | { 14 | public override Task GetDescriptionAsync() 15 | { 16 | return Task.FromResult("This GAgent acts as a marketing leader."); 17 | } 18 | 19 | public async Task HandleEventAsync(NewDemandTestEvent eventData) 20 | { 21 | await PublishAsync(new WorkingOnTestEvent 22 | { 23 | Description = $"Working on `{eventData.Description}`", 24 | }); 25 | } 26 | 27 | public async Task HandleEventAsync(NewFeatureCompletedTestEvent eventData) 28 | { 29 | await PublishAsync(new WorkingOnTestEvent 30 | { 31 | Description = $"Working completed: {eventData.PullRequestUrl}" 32 | }); 33 | } 34 | 35 | public async Task HandleEventAsync(InvestorFeedbackTestEvent eventData) 36 | { 37 | if (State.Content.IsNullOrEmpty()) 38 | { 39 | State.Content = []; 40 | } 41 | 42 | State.Content.Add($"Feedback from investor: {eventData.Content}"); 43 | } 44 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/NaiveTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestGAgents; 4 | 5 | [GenerateSerializer] 6 | public class NaiveTestGAgentState : StateBase 7 | { 8 | [Id(0)] public List Content { get; set; } 9 | } 10 | 11 | public class NaiveTestStateLogEvent : StateLogEventBase 12 | { 13 | [Id(0)] public Guid Id { get; set; } 14 | } 15 | 16 | [GenerateSerializer] 17 | public class NaiveGAgentConfiguration : ConfigurationBase 18 | { 19 | [Id(0)] public string Greeting { get; set; } = string.Empty; 20 | } 21 | 22 | [GAgent("naiveTest")] 23 | public class 24 | NaiveTestGAgent : GAgentBase 25 | { 26 | public override Task GetDescriptionAsync() 27 | { 28 | return Task.FromResult("This is a naive test GAgent"); 29 | } 30 | 31 | protected override async Task PerformConfigAsync(NaiveGAgentConfiguration configuration) 32 | { 33 | if (State.Content.IsNullOrEmpty()) 34 | { 35 | State.Content = []; 36 | } 37 | 38 | State.Content.Add(configuration.Greeting); 39 | } 40 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/PermissionTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.PermissionManagement; 3 | 4 | namespace Aevatar.Core.Tests.TestGAgents; 5 | 6 | [GenerateSerializer] 7 | public class PermissionGAgentState : StateBase 8 | { 9 | 10 | } 11 | 12 | [GenerateSerializer] 13 | public class PermissionStateLogEvent : StateLogEventBase 14 | { 15 | 16 | } 17 | 18 | public interface IPermissionGAgent : IGAgent 19 | { 20 | Task DoSomething1Async(); 21 | Task DoSomething2Async(); 22 | Task DoSomething3Async(); 23 | } 24 | 25 | [GAgent] 26 | public class PermissionGAgent : GAgentBase, IPermissionGAgent 27 | { 28 | public override Task GetDescriptionAsync() 29 | { 30 | return Task.FromResult("This is a GAgent for testing permissions."); 31 | } 32 | 33 | [Permission("AbpIdentity.Roles.Create", displayName: "Only for testing.")] 34 | public Task DoSomething1Async() 35 | { 36 | return Task.CompletedTask; 37 | } 38 | 39 | [Permission("DoSomething2", groupName: "DefaultGroup")] 40 | public Task DoSomething2Async() 41 | { 42 | return Task.CompletedTask; 43 | } 44 | 45 | [Permission("DoSomething3", groupName: "AnotherGroup")] 46 | public Task DoSomething3Async() 47 | { 48 | return Task.CompletedTask; 49 | } 50 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/SubscribeTestGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Aevatar.Core.Tests.TestGAgents; 5 | 6 | [GenerateSerializer] 7 | public class SubscribeTestGAgentState : StateBase 8 | { 9 | [Id(0)] public Dictionary> SubscriptionInfo { get; set; } 10 | } 11 | 12 | public class SubscribeTestStateLogEvent : StateLogEventBase; 13 | 14 | [GAgent("subscribeTest", "test")] 15 | public class SubscribeTestGAgent : GAgentBase 16 | { 17 | public override Task GetDescriptionAsync() 18 | { 19 | return Task.FromResult("This GAgent is used to test implementation of GetAllSubscribedEventsAsync."); 20 | } 21 | 22 | public async Task HandleEventAsync(SubscribedEventListEvent eventData) 23 | { 24 | if (State.SubscriptionInfo.IsNullOrEmpty()) 25 | { 26 | State.SubscriptionInfo = eventData.Value; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestGAgents/TestStateProjectionGAgent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestGAgents; 4 | 5 | [GenerateSerializer] 6 | public class TestStateProjectionGAgentState : StateBase 7 | { 8 | [Id(0)] public bool StateHandlerCalled { get; set; } 9 | } 10 | 11 | [GenerateSerializer] 12 | public class TestStateProjectionStateLogEvent : StateLogEventBase; 13 | 14 | public interface ITestStateProjectionGAgent : IStateGAgent; 15 | 16 | [GAgent] 17 | public class TestStateProjectionGAgent : StateProjectionGAgentBase, ITestStateProjectionGAgent 19 | { 20 | public override Task GetDescriptionAsync() 21 | { 22 | return Task.FromResult("This is a GAgent for testing state projection gagent base."); 23 | } 24 | 25 | protected override async Task HandleStateAsync(StateWrapper projectionState) 26 | { 27 | RaiseEvent(new CallStateHandlerStateLogEvent()); 28 | await ConfirmEvents(); 29 | } 30 | 31 | protected override void GAgentTransitionState(TestStateProjectionGAgentState state, 32 | StateLogEventBase @event) 33 | { 34 | if (@event is CallStateHandlerStateLogEvent) 35 | { 36 | State.StateHandlerCalled = true; 37 | } 38 | } 39 | 40 | [GenerateSerializer] 41 | public class CallStateHandlerStateLogEvent : StateLogEventBase; 42 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.GAgents.Tests; 2 | 3 | public static class TestHelper 4 | { 5 | public static async Task WaitUntilAsync(Func> predicate, TimeSpan? timeout = null, 6 | TimeSpan? delayOnFail = null) 7 | { 8 | timeout ??= TimeSpan.FromSeconds(10); 9 | delayOnFail ??= TimeSpan.FromSeconds(1); 10 | var keepGoing = new[] { true }; 11 | 12 | var task = Loop(); 13 | try 14 | { 15 | await Task.WhenAny(task, Task.Delay(timeout.Value)); 16 | } 17 | finally 18 | { 19 | keepGoing[0] = false; 20 | } 21 | 22 | await task; 23 | return; 24 | 25 | async Task Loop() 26 | { 27 | bool passed; 28 | do 29 | { 30 | // need to wait a bit to before re-checking the condition. 31 | await Task.Delay(delayOnFail.Value); 32 | passed = await predicate(false); 33 | } while (!passed && keepGoing[0]); 34 | 35 | if (!passed) 36 | { 37 | await predicate(true); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestStateLogEvents/MessageStateLogEvent.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | 3 | namespace Aevatar.Core.Tests.TestStateLogEvents; 4 | 5 | [GenerateSerializer] 6 | public class MessageStateLogEvent : StateLogEventBase 7 | { 8 | [Id(0)] public Guid Id { get; set; }= Guid.NewGuid(); 9 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestStateLogEvents/ReceiveMessageTestStateLogEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.Core.Tests.TestStateLogEvents; 2 | 3 | [GenerateSerializer] 4 | public class ReceiveMessageTestStateLogEvent : MessageStateLogEvent 5 | { 6 | [Id(0)] public string MessageId { get; set; } 7 | [Id(1)] public string ChatId { get; set; } 8 | [Id(2)] public string Message { get; set; } 9 | [Id(3)] public string NeedReplyBotName { get; set; } 10 | } -------------------------------------------------------------------------------- /test/Aevatar.Core.Tests/TestStateProjector.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Core.Abstractions; 2 | using Aevatar.Core.Tests.TestGAgents; 3 | 4 | namespace Aevatar.Core.Tests; 5 | 6 | public class TestStateProjector : IStateProjector 7 | { 8 | public async Task ProjectAsync(T state) where T : StateWrapperBase 9 | { 10 | if (state is StateWrapper wrapper) 11 | { 12 | var grainId = wrapper.GrainId; 13 | var groupGAgentState = wrapper.State; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /test/Aevatar.EventSourcing.MongoDB.Tests/Aevatar.EventSourcing.MongoDB.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | false 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/Aevatar.EventSourcing.MongoDB.Tests/AevatarMongoDbFixture.cs: -------------------------------------------------------------------------------- 1 | using EphemeralMongo; 2 | 3 | namespace Aevatar.EventSourcing.MongoDB.Tests; 4 | 5 | public class AevatarMongoDbFixture : IDisposable 6 | { 7 | private static readonly IMongoRunner MongoDbRunner; 8 | 9 | static AevatarMongoDbFixture() 10 | { 11 | MongoDbRunner = MongoRunner.Run(new MongoRunnerOptions 12 | { 13 | UseSingleNodeReplicaSet = true, 14 | KillMongoProcessesWhenCurrentProcessExits = true 15 | }); 16 | } 17 | 18 | public static string GetRandomConnectionString() 19 | { 20 | return GetConnectionString("Db_" + Guid.NewGuid().ToString("N")); 21 | } 22 | 23 | public static string GetConnectionString(string databaseName) 24 | { 25 | var stringArray = MongoDbRunner.ConnectionString.Split('?'); 26 | var connectionString = stringArray[0] + (stringArray[0].EndsWith('/') ? "" : '/') + databaseName + "/?" + 27 | stringArray[1]; 28 | return connectionString; 29 | } 30 | 31 | public void Dispose() 32 | { 33 | MongoDbRunner?.Dispose(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Aevatar.EventSourcing.MongoDB.Tests/GrainTypeBsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.IO; 3 | using MongoDB.Bson.Serialization; 4 | using MongoDB.Bson.Serialization.Serializers; 5 | using Orleans.Runtime; 6 | 7 | namespace Aevatar.EventSourcing.MongoDB.Tests; 8 | 9 | public static class GrainTypeBsonSerializerConfig 10 | { 11 | private static readonly object LockObj = new(); 12 | private static bool _isRegistered; 13 | 14 | public static void Configure() 15 | { 16 | if (_isRegistered) return; 17 | 18 | lock (LockObj) 19 | { 20 | if (_isRegistered) return; 21 | 22 | if (!BsonClassMap.IsClassMapRegistered(typeof(GrainType))) 23 | { 24 | BsonSerializer.RegisterSerializer(new GrainTypeBsonSerializer()); 25 | } 26 | 27 | _isRegistered = true; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /test/Aevatar.EventSourcing.MongoDB.Tests/IdSpanBsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Bson.Serialization; 3 | using MongoDB.Bson.Serialization.Serializers; 4 | using Orleans.Runtime; 5 | 6 | namespace Aevatar.EventSourcing.MongoDB.Tests; 7 | 8 | public static class IdSpanBsonSerializerConfig 9 | { 10 | private static readonly object LockObj = new(); 11 | private static bool _isRegistered; 12 | 13 | public static void Configure() 14 | { 15 | if (_isRegistered) return; 16 | 17 | lock (LockObj) 18 | { 19 | if (_isRegistered) return; 20 | 21 | if (!BsonClassMap.IsClassMapRegistered(typeof(IdSpan))) 22 | { 23 | BsonSerializer.RegisterSerializer(new IdSpanBsonSerializer()); 24 | } 25 | 26 | _isRegistered = true; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /test/Aevatar.EventSourcing.MongoDB.Tests/MongoDbTestCollection.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson.Serialization; 2 | using Orleans.Runtime; 3 | using Xunit; 4 | 5 | namespace Aevatar.EventSourcing.MongoDB.Tests; 6 | 7 | [CollectionDefinition(nameof(MongoDbTestCollection))] 8 | public class MongoDbTestCollection : ICollectionFixture 9 | { 10 | public MongoDbTestCollection() 11 | { 12 | GrainTypeBsonSerializerConfig.Configure(); 13 | IdSpanBsonSerializerConfig.Configure(); 14 | } 15 | } -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Plugins/Aevatar.GAgents.Plugins.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/AevatarGAgentsTestBase.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.TestBase; 2 | 3 | namespace Aevatar.GAgents.Tests; 4 | 5 | public abstract class AevatarGAgentsTestBase : AevatarTestBase 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/AevatarGAgentsTestModule.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.Plugins.DbContexts; 2 | using Aevatar.Plugins.Repositories; 3 | using Aevatar.TestBase; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Volo.Abp.Authorization.Permissions; 6 | using Volo.Abp.AutoMapper; 7 | using Volo.Abp.EventBus; 8 | using Volo.Abp.Modularity; 9 | using Volo.Abp.PermissionManagement; 10 | 11 | namespace Aevatar.GAgents.Tests; 12 | 13 | [DependsOn( 14 | typeof(AevatarTestBaseModule), 15 | typeof(AbpEventBusModule), 16 | typeof(AbpPermissionManagementDomainModule) 17 | )] 18 | public class AevatarGAgentsTestModule : AbpModule 19 | { 20 | public override void ConfigureServices(ServiceConfigurationContext context) 21 | { 22 | base.ConfigureServices(context); 23 | Configure(options => 24 | { 25 | }); 26 | Configure(options => 27 | { 28 | options.DefinitionProviders.Add(); 29 | }); 30 | Configure(options => { options.AddMaps(); }); 31 | context.Services.AddTransient(); 32 | context.Services.AddSingleton(); 33 | context.Services.AddSingleton(); 34 | context.Services.AddSingleton(); 35 | } 36 | } -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: CollectionBehavior(DisableTestParallelization = true)] -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/PluginTestCollection.cs: -------------------------------------------------------------------------------- 1 | using Aevatar.TestBase; 2 | 3 | namespace Aevatar.GAgents.Tests; 4 | 5 | [CollectionDefinition("PluginTests", DisableParallelization = true)] 6 | public class PluginTestCollection : ICollectionFixture 7 | { 8 | // This class has no code, and is never created. Its purpose is simply 9 | // to be the place to apply [CollectionDefinition] and all the 10 | // ICollectionFixture<> interfaces. 11 | } -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/Plugins/RestSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aevatarAI/aevatar-framework/b4e62d20ab438dc664a3026e07af96dcba57965c/test/Aevatar.GAgents.Tests/Plugins/RestSharp.dll -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/TestPermissionDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | using Volo.Abp.Authorization.Permissions; 2 | 3 | namespace Aevatar.GAgents.Tests; 4 | 5 | public class TestPermissionDefinitionProvider : PermissionDefinitionProvider 6 | { 7 | public override void Define(IPermissionDefinitionContext context) 8 | { 9 | var testGroup = context.AddGroup("TestGroup"); 10 | 11 | testGroup.AddPermission( 12 | name: "DoSomething1", 13 | isEnabled: true 14 | ); 15 | testGroup.AddPermission( 16 | name: "DoSomething2", 17 | isEnabled: true 18 | ); 19 | testGroup.AddPermission( 20 | name: "DoSomething3", 21 | isEnabled: true 22 | ); 23 | } 24 | } -------------------------------------------------------------------------------- /test/Aevatar.GAgents.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "Default": "mongodb://127.0.0.1:27017/TestAevatar" 4 | }, 5 | "Aevatar": { 6 | "StreamNamespace": "AevatarTest" 7 | }, 8 | "Orleans":{ 9 | "MongoDBClient":"mongodb://127.0.0.1:27017", 10 | "DataBase":"TestAevatar" 11 | } 12 | } -------------------------------------------------------------------------------- /test/Aevatar.TestBase/AevatarTestBase.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Orleans.TestingHost; 3 | using Volo.Abp.Modularity; 4 | using Volo.Abp.Testing; 5 | 6 | namespace Aevatar.TestBase; 7 | 8 | public abstract class AevatarTestBase : AbpIntegratedTest 9 | where TStartupModule : IAbpModule 10 | { 11 | protected readonly TestCluster Cluster; 12 | 13 | protected AevatarTestBase() 14 | { 15 | Cluster = GetRequiredService().Cluster; 16 | } 17 | } -------------------------------------------------------------------------------- /test/Aevatar.TestBase/ClusterCollection.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestBase; 2 | 3 | [CollectionDefinition(Name)] 4 | public class ClusterCollection : ICollectionFixture 5 | { 6 | public const string Name = "ClusterCollection"; 7 | } -------------------------------------------------------------------------------- /test/Aevatar.TestBase/InterfaceImplementationsFinder.cs: -------------------------------------------------------------------------------- 1 | namespace Aevatar.TestBase; 2 | 3 | public static class InterfaceImplementationsFinder 4 | { 5 | public static IEnumerable GetImplementations() 6 | { 7 | var interfaceType = typeof(TInterface); 8 | return AppDomain.CurrentDomain.GetAssemblies() 9 | .SelectMany(assembly => assembly.GetTypes()) 10 | .Where(type => interfaceType.IsAssignableFrom(type) && type is { IsClass: true, IsAbstract: false }) 11 | .Select(type => (TInterface)Activator.CreateInstance(type)!); 12 | } 13 | } -------------------------------------------------------------------------------- /test/Aevatar.TestBase/MockLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Concurrent; 4 | 5 | public class MockLoggerProvider : ILoggerProvider 6 | { 7 | public ConcurrentQueue Logs { get; } = new(); 8 | private readonly string _namespace; 9 | 10 | public MockLoggerProvider(string @namespace) 11 | { 12 | _namespace = @namespace; 13 | } 14 | 15 | public ILogger CreateLogger(string categoryName) 16 | { 17 | return new MockLogger(Logs, categoryName, _namespace); 18 | } 19 | 20 | public void Dispose() 21 | { 22 | } 23 | 24 | private class MockLogger : ILogger 25 | { 26 | private readonly ConcurrentQueue _logs; 27 | private readonly string _categoryName; 28 | private readonly string _namespace; 29 | 30 | public MockLogger(ConcurrentQueue logs, string categoryName, string @namespace) 31 | { 32 | _logs = logs; 33 | _categoryName = categoryName; 34 | _namespace = @namespace; 35 | } 36 | 37 | public IDisposable BeginScope(TState state) => null; 38 | 39 | public bool IsEnabled(LogLevel logLevel) => logLevel == LogLevel.Information; 40 | 41 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, 42 | Func formatter) 43 | { 44 | if (IsEnabled(logLevel) && _categoryName.StartsWith(_namespace)) 45 | { 46 | _logs.Enqueue(formatter(state, exception)); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /test/Aevatar.TestBase/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Aevatar": { 3 | "StreamNamespace": "AINamespace" 4 | } 5 | } -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Tests")] 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/GlobalUsing.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Threading; 3 | global using System.Threading.Tasks; 4 | global using System.Collections.Generic; 5 | global using System.Linq; 6 | global using Orleans; 7 | global using Orleans.Runtime; 8 | global using FluentAssertions; 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/ActivationCountWithReminder.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | using Orleans.Streams; 3 | 4 | namespace TestGrains; 5 | 6 | public sealed class ActivationCountWithReminder : Grain, IGrainWithIntegerKey, IRemindable 7 | { 8 | private int _activationCount; 9 | 10 | public override Task OnActivateAsync(CancellationToken cancellationToken) 11 | { 12 | _activationCount++; 13 | return base.OnActivateAsync(cancellationToken); 14 | } 15 | 16 | public Task GetActivationCount() => Task.FromResult(_activationCount); 17 | 18 | 19 | Task IRemindable.ReceiveReminder(string reminderName, TickStatus status) 20 | { 21 | return Task.CompletedTask; 22 | } 23 | 24 | public Task RegisterReminder(string reminderName, TimeSpan dueTime, TimeSpan period) 25 | { 26 | return this.RegisterOrUpdateReminder(reminderName, dueTime, period); 27 | } 28 | 29 | public async Task UnregisterReminder(string reminderName) 30 | { 31 | var reminder = await this.GetReminder(reminderName); 32 | 33 | if (reminder != null) 34 | { 35 | await this.UnregisterReminder(reminder); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | namespace TestGrains; 2 | 3 | public class ChatMessage 4 | { 5 | public ChatMessage(string msg) 6 | { 7 | Msg = msg; 8 | } 9 | 10 | public string Msg { get; } 11 | } 12 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/ColorGrainState.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using TestInterfaces; 3 | 4 | namespace TestGrains; 5 | 6 | /// The persisted state of a grain that tracks a color. 7 | [DebuggerStepThrough] 8 | public sealed class ColorGrainState 9 | { 10 | /// Gets or sets the color. 11 | public Color Color { get; set; } 12 | 13 | /// Gets or sets the unique ID. 14 | public Guid Id { get; set; } 15 | 16 | /// 17 | /// Gets a value indicating whether the persisted state has been initialized. This is true when 18 | /// is not equal to . 19 | /// 20 | public bool Initialized => this.Id != Guid.Empty; 21 | } 22 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/ContextConstructorGrain.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Orleans.Runtime; 3 | 4 | namespace OrleansTestKit.Tests.Grains; 5 | public class ContextConstructorGrain : Grain, IGrainWithIntegerKey 6 | { 7 | public ContextConstructorGrain(IGrainContext injectedContext) 8 | { 9 | injectedContext.Should().NotBeNull(); 10 | 11 | var context = ((IGrainBase)this).GrainContext; 12 | 13 | context.Should().NotBeNull(); 14 | 15 | //context.Should().Be(injectedContext); 16 | 17 | context.GrainId.Should().NotBeNull(); 18 | 19 | GrainFactory.Should().NotBeNull(); 20 | } 21 | } 22 | 23 | // TODO -- re-architect service provider to use automocking solution instead of of rolling by hand to allow for bigger "units" of testing 24 | public class LifecycleComponent : ILifecycleParticipant 25 | { 26 | private readonly IGrainContext _context; 27 | 28 | public LifecycleComponent(IGrainContext context) => _context = context; 29 | 30 | public bool IsInitialized { get; private set; } 31 | 32 | public void Participate(IGrainLifecycle observer) => _context.ObservableLifecycle.Subscribe(nameof(LifecycleComponent), GrainLifecycleStage.Activate - 1, PreActivateAsync); 33 | 34 | private Task PreActivateAsync(CancellationToken arg) 35 | { 36 | IsInitialized = true; 37 | return Task.CompletedTask; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/DeactivationGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public class DeactivationGrain : Grain, IDeactivationGrain 6 | { 7 | public new Task DeactivateOnIdle() 8 | { 9 | base.DeactivateOnIdle(); 10 | 11 | return Task.CompletedTask; 12 | } 13 | 14 | public new Task DelayDeactivation(TimeSpan timeSpan) 15 | { 16 | base.DelayDeactivation(timeSpan); 17 | 18 | return Task.CompletedTask; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/DependencyGrain.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using TestInterfaces; 3 | 4 | namespace TestGrains; 5 | 6 | public interface IDependency 7 | { 8 | string? GetValue(); 9 | } 10 | 11 | public sealed class DependencyGrain : Grain, IDependencyGrain 12 | { 13 | private readonly IDependency? _firstKeyedDependency; 14 | 15 | private readonly IDependency? _secondKeyedDependency; 16 | 17 | private readonly IDependency? _unkeyedDependency; 18 | 19 | public DependencyGrain( 20 | IDependency? unkeyedDependency, 21 | [FromKeyedServices("first")] IDependency? firstKeyedDependency, 22 | [FromKeyedServices("second")] IDependency? secondKeyedDependency) 23 | { 24 | _unkeyedDependency = unkeyedDependency; 25 | _firstKeyedDependency = firstKeyedDependency; 26 | _secondKeyedDependency = secondKeyedDependency; 27 | } 28 | 29 | public Task GetFirstKeyedServiceValue() => 30 | Task.FromResult(_firstKeyedDependency?.GetValue()); 31 | 32 | public Task GetSecondKeyedServiceValue() => 33 | Task.FromResult(_secondKeyedDependency?.GetValue()); 34 | 35 | public Task GetUnkeyedServiceValue() => 36 | Task.FromResult(_unkeyedDependency?.GetValue()); 37 | } 38 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/DeviceAndroidGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | using Orleans.Runtime; 6 | 7 | public class DeviceAndroidGrain : IGrainBase, IDevice 8 | { 9 | public Task GetDeviceType() 10 | { 11 | return Task.FromResult("Android"); 12 | } 13 | 14 | public IGrainContext GrainContext { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/DeviceIosGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | using Orleans.Runtime; 6 | 7 | public class DeviceIosGrain : IGrainBase, IDevice 8 | { 9 | public Task GetDeviceType() 10 | { 11 | return Task.FromResult("IOS"); 12 | } 13 | 14 | public IGrainContext GrainContext { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/DeviceManagerGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | using Orleans.Runtime; 6 | 7 | public abstract class DerivedGrain : IGrainBase 8 | { 9 | public IGrainContext GrainContext { get; } 10 | } 11 | 12 | public class DeviceManagerGrain : DerivedGrain, IDeviceManager 13 | { 14 | private readonly IGrainFactory _grainFactory; 15 | 16 | public DeviceManagerGrain(IGrainFactory grainFactory) 17 | { 18 | _grainFactory = grainFactory; 19 | } 20 | 21 | public Task GetDeviceGrain(string deviceType) 22 | { 23 | switch (deviceType) 24 | { 25 | case "IOS": 26 | return Task.FromResult(_grainFactory.GetGrain(deviceType, "TestGrains.DeviceIosGrain")); 27 | 28 | case "Android": 29 | return Task.FromResult(_grainFactory.GetGrain(deviceType, "TestGrains.DeviceAndroidGrain")); 30 | 31 | default: 32 | throw new InvalidOperationException($"Unknown device type {deviceType}"); 33 | } 34 | } 35 | 36 | public async Task GetDeviceType(string deviceType) 37 | { 38 | var device = await this.GetDeviceGrain(deviceType); 39 | return await device.GetDeviceType(); 40 | } 41 | 42 | //public IGrainContext GrainContext { get; set; } 43 | } 44 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/GrainContextGrain.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | 3 | namespace Aevatar.TestKit.Tests.Grains; 4 | internal class GrainContextGrain : Grain, IGrainWithGuidKey 5 | { 6 | public IGrainContext Context { get; set; } 7 | 8 | public GrainContextGrain(IGrainContext context) 9 | { 10 | Context = context; 11 | } 12 | } 13 | 14 | internal sealed class GrainContextGrain2 : GrainContextGrain 15 | { 16 | public GrainContextGrain2(IGrainContext context) : base(context) { } 17 | } 18 | 19 | internal sealed class GrainContextIntegerGrain : Grain, IGrainWithIntegerKey 20 | { } 21 | 22 | internal sealed class GrainContextStringGrain : Grain, IGrainWithStringKey 23 | { } 24 | 25 | internal sealed class GrainContextGuidGrain : Grain, IGrainWithGuidKey 26 | { } 27 | 28 | internal sealed class GrainContextGuidCompoundGrain : Grain, IGrainWithGuidCompoundKey 29 | { } 30 | 31 | internal sealed class GrainContextIntegerCompoundGrain : Grain, IGrainWithIntegerCompoundKey 32 | { } 33 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/GreetingArchiveGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public sealed class GreetingArchiveGrain : Grain, IGreetingArchiveGrain 6 | { 7 | public Task AddGreeting(string greeting) 8 | { 9 | State.Greetings.Add(greeting); 10 | return WriteStateAsync(); 11 | } 12 | 13 | public Task> GetGreetings() => 14 | Task.FromResult>(State.Greetings); 15 | 16 | public Task ResetGreetings() => ClearStateAsync(); 17 | } 18 | 19 | public sealed class GreetingArchiveGrainState 20 | { 21 | public List Greetings { get; private set; } = new(); 22 | } 23 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/HelloGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public class HelloGrain : Grain, IHello 6 | { 7 | public bool Deactivated { get; set; } 8 | 9 | public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken cancellationToken) 10 | { 11 | Deactivated = true; 12 | 13 | return base.OnDeactivateAsync(reason, cancellationToken); 14 | } 15 | 16 | public Task SayHello(string greeting) => Task.FromResult("You said: '" + greeting + "', I say: Hello!"); 17 | } 18 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/HelloGrainWithServiceDependency.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public class HelloGrainWithServiceDependency : Grain, IHello 6 | { 7 | private readonly IDateTimeService _dateTimeService; 8 | 9 | public HelloGrainWithServiceDependency(IDateTimeService dateTimeService) 10 | { 11 | if (dateTimeService == null) 12 | throw new ArgumentNullException(nameof(dateTimeService)); 13 | 14 | _dateTimeService = dateTimeService; 15 | } 16 | 17 | public async Task SayHello(string greeting) 18 | { 19 | var date = await _dateTimeService.GetCurrentDate(); 20 | 21 | return $"[{date.Date}]: You said: '" + greeting + "', I say: Hello!"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/HelloReminders.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Runtime; 2 | 3 | namespace TestGrains; 4 | 5 | public sealed class HelloReminders : Grain, IGrainWithIntegerKey, IRemindable 6 | { 7 | public List FiredReminders { get; } = new(); 8 | 9 | Task IRemindable.ReceiveReminder(string reminderName, TickStatus status) 10 | { 11 | FiredReminders.Add(reminderName); 12 | return Task.CompletedTask; 13 | } 14 | 15 | public Task RegisterReminder(string reminderName, TimeSpan dueTime, TimeSpan period) 16 | { 17 | return this.RegisterOrUpdateReminder(reminderName, dueTime, period); 18 | } 19 | 20 | public async Task UnregisterReminder(string reminderName) 21 | { 22 | var reminder = await this.GetReminder(reminderName); 23 | 24 | if (reminder != null) 25 | { 26 | await this.UnregisterReminder(reminder); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/IUnknownGrainResolver.cs: -------------------------------------------------------------------------------- 1 | namespace TestGrains; 2 | 3 | public interface IUnknownGrainResolver : IGrainWithStringKey 4 | { 5 | Task CreateAndPingMultiple(); 6 | 7 | Task> GetResolvedUnknownGrainIdsAsync(); 8 | } 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/LifecycleGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public sealed class LifecycleGrain : Grain, ILifecycleGrain 6 | { 7 | public int ActivateCount { get; set; } 8 | 9 | public int DeactivateCount { get; set; } 10 | 11 | public override Task OnActivateAsync(CancellationToken cancellationToken) 12 | { 13 | ActivateCount++; 14 | return base.OnActivateAsync(cancellationToken); 15 | } 16 | 17 | public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken cancellationToken) 18 | { 19 | DeactivateCount++; 20 | return base.OnDeactivateAsync(reason, cancellationToken); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/Listener.cs: -------------------------------------------------------------------------------- 1 | using Orleans.Streams; 2 | using TestInterfaces; 3 | 4 | namespace TestGrains; 5 | 6 | public class Listener : Grain, IListener 7 | { 8 | private int _receivedCount; 9 | 10 | public override Task OnActivateAsync(CancellationToken cancellationToken) 11 | { 12 | var stream = this.GetStreamProvider("Aevatar").GetStream(Guid.Empty); 13 | 14 | stream.SubscribeAsync((data, token) => 15 | { 16 | _receivedCount++; 17 | 18 | return Task.CompletedTask; 19 | }); 20 | 21 | return base.OnActivateAsync(cancellationToken); 22 | } 23 | 24 | public Task ReceivedCount() => Task.FromResult(_receivedCount); 25 | } 26 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/PingGrain.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public class PingGrain : Grain, IPing 6 | { 7 | public Task Ping() 8 | { 9 | var pong = GrainFactory.GetGrain(22); 10 | 11 | return pong.Pong(); 12 | } 13 | 14 | public Task PingCompound() 15 | { 16 | var pong = (IPongCompound)GrainFactory.GetGrain(typeof(IPongCompound),44, keyExtension: "Test"); 17 | 18 | return pong.Pong(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/StatefulActivationGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestGrains; 2 | 3 | public interface IStatefulActivationGrain : IGrainWithIntegerKey 4 | { 5 | Task GetActivationValue(); 6 | 7 | Task GetStateValue(); 8 | } 9 | 10 | public sealed class StatefulActivationGrain : Grain, IStatefulActivationGrain 11 | { 12 | private int _activationValue; 13 | 14 | public Task GetActivationValue() => Task.FromResult(_activationValue); 15 | 16 | public Task GetStateValue() => Task.FromResult(State.Value); 17 | 18 | public override Task OnActivateAsync(CancellationToken cancellationToken) 19 | { 20 | _activationValue = this.State.Value; 21 | return Task.CompletedTask; 22 | } 23 | } 24 | 25 | public sealed class StatefulActivationGrainState 26 | { 27 | public int Value { get; set; } = 123; 28 | } 29 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/StatefulNonNewActivationGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestGrains; 2 | 3 | public interface IStatefulUnsupportedActivationGrain : IGrainWithIntegerKey 4 | { 5 | Task GetActivationValue(); 6 | 7 | Task GetStateValue(); 8 | } 9 | 10 | public sealed class StatefulNonSupportedActivationGrainState 11 | { 12 | public StatefulNonSupportedActivationGrainState(int activationValue) 13 | { 14 | Value = activationValue; 15 | } 16 | 17 | public int Value { get; set; } 18 | } 19 | 20 | public sealed class StatefulUnsupportedActivationGrain : Grain, IStatefulUnsupportedActivationGrain 21 | { 22 | private int _activationValue; 23 | 24 | public Task GetActivationValue() => Task.FromResult(_activationValue); 25 | 26 | public Task GetStateValue() => Task.FromResult(State.Value); 27 | 28 | public override Task OnActivateAsync(CancellationToken cancellationToken) 29 | { 30 | _activationValue = this.State.Value; 31 | return Task.CompletedTask; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Grains/UnknownGrainResolver.cs: -------------------------------------------------------------------------------- 1 | using TestInterfaces; 2 | 3 | namespace TestGrains; 4 | 5 | public class UnknownGrainResolver : Grain, IUnknownGrainResolver 6 | { 7 | private List _resolvedIds = new List(); 8 | 9 | public async Task CreateAndPingMultiple() 10 | { 11 | var unknownGrainOne = GrainFactory.GetGrain("unknownGrainOne"); 12 | var unknownGrainTwo = GrainFactory.GetGrain("unknownGrainTwo"); 13 | 14 | _resolvedIds.Add(await unknownGrainOne.WhatsMyId()); 15 | _resolvedIds.Add(await unknownGrainTwo.WhatsMyId()); 16 | } 17 | 18 | public Task> GetResolvedUnknownGrainIdsAsync() => Task.FromResult(_resolvedIds); 19 | } 20 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/Color.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | /// A set of well-known colors. 4 | public enum Color 5 | { 6 | Unknown = 0, 7 | 8 | Red, 9 | 10 | Orange, 11 | 12 | Yellow, 13 | 14 | Green, 15 | 16 | Blue, 17 | 18 | Indigo, 19 | 20 | Violet, 21 | } 22 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IBasicGrains.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IGuidCompoundKeyGrain : IGrainWithGuidCompoundKey 4 | { 5 | Task<(Guid, string)> GetKey(); 6 | } 7 | 8 | public interface IGuidKeyGrain : IGrainWithGuidKey 9 | { 10 | Task GetKey(); 11 | } 12 | 13 | public interface IIntegerCompoundKeyGrain : IGrainWithIntegerCompoundKey 14 | { 15 | Task<(long, string)> GetKey(); 16 | } 17 | 18 | public interface IIntegerKeyGrain : IGrainWithIntegerKey 19 | { 20 | Task GetKey(); 21 | } 22 | 23 | public interface IStringKeyGrain : IGrainWithStringKey 24 | { 25 | Task GetKey(); 26 | } 27 | 28 | public interface IAliasGrain : IGrainWithIntegerKey 29 | { 30 | Task GetKey(); 31 | } 32 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IChatty.cs: -------------------------------------------------------------------------------- 1 | using TestGrains; 2 | 3 | namespace TestInterfaces; 4 | 5 | public interface IChatty : IGrainWithIntegerKey 6 | { 7 | Task GetMessage(); 8 | 9 | Task SendChat(string msg); 10 | 11 | Task SendChatBatch(string[] msgs); 12 | 13 | Task Subscribe(); 14 | Task SubscribeBatch(); 15 | } 16 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IColorGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | /// Represents a grain that tracks a color. 4 | public interface IColorGrain : IGrainWithGuidKey 5 | { 6 | /// Asynchronously gets the color. 7 | /// 8 | /// A task that represents the asynchronous operation. The task result returns the color or 9 | /// if the color has not yet been set. 10 | /// 11 | Task GetColor(); 12 | 13 | /// 14 | /// Asynchronously resets the color to . This has no effect if the color has not yet 15 | /// been set or if the color has already been reset. 16 | /// 17 | /// A task that represents the asynchronous operation. 18 | Task ResetColor(); 19 | 20 | /// Asynchronously sets the color. 21 | /// The new color. 22 | /// A task that represents the asynchronous operation. 23 | /// 24 | /// If is or an invalid value. 25 | /// 26 | Task SetColor(Color color); 27 | } 28 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IColorRankingGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IColorRankingGrain : IGrainWithGuidKey 4 | { 5 | Task GetFavouriteColor(); 6 | Task GetLeastFavouriteColor(); 7 | Task SetFavouriteColor(Color color); 8 | Task SetLeastFavouriteColor(Color color); 9 | } 10 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IDateTimeService.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IDateTimeService 4 | { 5 | Task GetCurrentDate(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IDeactivationGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IDeactivationGrain : IGrainWithIntegerKey 4 | { 5 | public Task DeactivateOnIdle(); 6 | 7 | public Task DelayDeactivation(TimeSpan timeSpan); 8 | } 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IDependencyGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IDependencyGrain : IGrainWithGuidKey 4 | { 5 | Task GetFirstKeyedServiceValue(); 6 | 7 | Task GetSecondKeyedServiceValue(); 8 | 9 | Task GetUnkeyedServiceValue(); 10 | } 11 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IDevice.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IDevice : IGrainWithStringKey 4 | { 5 | Task GetDeviceType(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IDeviceManager.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IDeviceManager : IGrainWithIntegerKey 4 | { 5 | Task GetDeviceGrain(string deviceType); 6 | 7 | Task GetDeviceType(string deviceType); 8 | } 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IGreetingArchiveGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | /// Represents an Orleans grain that stores a list of greetings. 4 | public interface IGreetingArchiveGrain : IGrainWithIntegerKey 5 | { 6 | Task AddGreeting(string greeting); 7 | 8 | Task> GetGreetings(); 9 | 10 | Task ResetGreetings(); 11 | } 12 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IHello.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IHello : IGrainWithIntegerKey 4 | { 5 | Task SayHello(string greeting); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/ILifecycleGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface ILifecycleGrain : IGrainWithIntegerKey 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IListener.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IListener : IGrainWithIntegerKey 4 | { 5 | Task ReceivedCount(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IPing.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IPing : IGrainWithIntegerKey 4 | { 5 | Task Ping(); 6 | 7 | Task PingCompound(); 8 | } 9 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IPong.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IPong : IGrainWithIntegerKey 4 | { 5 | Task Pong(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IPong2.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IPong2 : IGrainWithIntegerKey 4 | { 5 | Task Pong2(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IPongCompound.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IPongCompound : IGrainWithIntegerCompoundKey 4 | { 5 | Task Pong(); 6 | } 7 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Interfaces/IUnknownGrain.cs: -------------------------------------------------------------------------------- 1 | namespace TestInterfaces; 2 | 3 | public interface IUnknownGrain : IGrainWithStringKey 4 | { 5 | //this grain's identity is not known when it is resolved 6 | //for instance, if GrainA needs to create another NEW IUnknownGrain it would generate a new id and ask the GrainFactory for a reference 7 | //in this case a test of GrainA would not know the id in advance and would not be able to set up a probe using the id. 8 | 9 | Task WhatsMyId(); 10 | } 11 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/ActivationGrainTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TestGrains; 4 | using Xunit; 5 | 6 | namespace Aevatar.TestKit.Tests; 7 | 8 | public class ActivationGrainTests : DefaultTestKitBase 9 | { 10 | [Fact] 11 | public async Task ShouldActivateWithValidState() 12 | { 13 | // Arrange 14 | 15 | // Act 16 | var grain = await Silo.CreateGrainAsync(0); 17 | var value = await grain.GetActivationValue(); 18 | 19 | // Assert 20 | value.Should().Be(123); 21 | } 22 | 23 | /// 24 | /// This test demonstrates what happens when you try activating a grain with a state that does not have a a 25 | /// parameterless constructor. The exception should help the user to find out what the problem is. 26 | /// 27 | [Fact] 28 | public async Task ShouldThrowNotSupportedException() 29 | { 30 | // Arrange 31 | 32 | // Act and Assert 33 | await Assert.ThrowsAsync( 34 | async () => await Silo.CreateGrainAsync(0)); 35 | } 36 | 37 | [Fact] 38 | public async Task OnActivateAsyncShouldBeCalledOnceOnRemindable() 39 | { 40 | // Arrange 41 | 42 | // Act 43 | var grain = await Silo.CreateGrainAsync(1); 44 | var value = await grain.GetActivationCount(); 45 | 46 | Assert.Equal(1, value); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/ContextConstructorGrainTests.cs: -------------------------------------------------------------------------------- 1 | using OrleansTestKit.Tests.Grains; 2 | using Xunit; 3 | 4 | namespace Aevatar.TestKit.Tests; 5 | 6 | public class ContextConstructorGrainTests : DefaultTestKitBase 7 | { 8 | [Fact] 9 | public async Task CanAccess_GrainContext_InConstructorAsync() 10 | { 11 | await Silo.CreateGrainAsync(0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/DeactivationGrainTests.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using TestGrains; 3 | using Xunit; 4 | 5 | namespace Aevatar.TestKit.Tests; 6 | 7 | #pragma warning disable CS0618 // Type or member is obsolete 8 | public class DeactivationGrainTests : DefaultTestKitBase 9 | { 10 | [Fact] 11 | public async Task ShouldCallDeactivateOnIdle() 12 | { 13 | // Arrange 14 | var grain = await Silo.CreateGrainAsync(0); 15 | 16 | // Act 17 | await grain.DeactivateOnIdle(); 18 | 19 | var context = Silo.GetContextFromGrain(grain); 20 | 21 | // Assert 22 | Silo.VerifyRuntime(i => i.DeactivateOnIdle(context), Times.Once); 23 | } 24 | 25 | [Fact] 26 | public async Task ShouldCallDelayDeactivation() 27 | { 28 | // Arrange 29 | var grain = await Silo.CreateGrainAsync(0); 30 | var timeSpan = TimeSpan.FromSeconds(5); 31 | 32 | // Act 33 | await grain.DelayDeactivation(timeSpan); 34 | 35 | var context = Silo.GetContextFromGrain(grain); 36 | 37 | // Assert 38 | Silo.VerifyRuntime(i => i.DelayDeactivation(context, timeSpan), Times.Once); 39 | } 40 | } 41 | #pragma warning restore CS0618 // Type or member is obsolete 42 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/GrainIdTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Orleans.Runtime; 3 | using TestGrains; 4 | using Xunit; 5 | 6 | namespace Aevatar.TestKit.Tests; 7 | 8 | public class GrainIdTests : DefaultTestKitBase 9 | { 10 | [Fact] 11 | public async Task CreatedGrainShouldHaveCorrectGrainType() 12 | { 13 | var grain = await Silo.CreateGrainAsync(0); 14 | GrainId id = grain.GetGrainId(); 15 | 16 | id.Type.Should().Be(GrainType.Create("hello")); 17 | } 18 | 19 | [Fact] 20 | public async Task CreatedGrainWithAliasShouldHaveCorrectGrainType() 21 | { 22 | var grain = await Silo.CreateGrainAsync(0); 23 | GrainId id = grain.GetGrainId(); 24 | 25 | id.Type.Should().Be(GrainType.Create("special-alias")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/LoggerTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using TestGrains; 3 | using TestInterfaces; 4 | using Xunit; 5 | using Xunit.Abstractions; 6 | 7 | namespace Aevatar.TestKit.Tests; 8 | 9 | public class LoggerTests : DefaultTestKitBase 10 | { 11 | private readonly ITestOutputHelper _output; 12 | 13 | public LoggerTests(ITestOutputHelper output) 14 | { 15 | _output = output; 16 | } 17 | 18 | [Fact] 19 | public async Task ConsoleLog() 20 | { 21 | const string greeting = "Bonjour"; 22 | 23 | IHello grain = await Silo.CreateGrainAsync(1); 24 | 25 | await grain.Invoking(g => g.SayHello(greeting)).Should().NotThrowAsync(); 26 | } 27 | 28 | [Fact] 29 | public async Task XUnitLog() 30 | { 31 | const string greeting = "Bonjour"; 32 | 33 | IHello grain = await Silo.CreateGrainAsync(2); 34 | 35 | await grain.Invoking(g => g.SayHello(greeting)).Should().NotThrowAsync(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Aevatar.TestKit.Tests/Tests/StreamBatchTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using TestGrains; 3 | using Xunit; 4 | 5 | namespace Aevatar.TestKit.Tests; 6 | 7 | public class StreamBatchTests : DefaultTestKitBase 8 | { 9 | [Fact] 10 | public async Task AddNonReferenceTypeStreamProbe() 11 | { 12 | var stream = Silo.AddStreamProbe(Guid.Empty, null); 13 | 14 | var chatty = await Silo.CreateGrainAsync(4); 15 | await chatty.SubscribeBatch(); 16 | 17 | const string msg = "Hello Chat"; 18 | const int id = 2; 19 | await stream.OnNextBatchAsync(new ChattyMessage[] { new(msg, id) }); 20 | 21 | stream.Sends.Should().Be(1); 22 | stream.VerifySendBatch(); 23 | 24 | var message = await chatty.GetMessage(); 25 | Assert.NotEqual(default, message); 26 | Assert.Equal(msg, message.Message); 27 | Assert.Equal(id, message.Id); 28 | } 29 | 30 | [Fact] 31 | public async Task GrainGetAllSubscriptionHandles() 32 | { 33 | var stream = Silo.AddStreamProbe(Guid.Empty, null); 34 | 35 | var chatty = await Silo.CreateGrainAsync(4); 36 | await chatty.SubscribeBatch(); 37 | var handlers = await stream.GetAllSubscriptionHandles(); 38 | 39 | handlers.Count.Should().Be(1); 40 | stream.Subscribed.Should().Be(1); 41 | 42 | await handlers[0].UnsubscribeAsync(); 43 | 44 | handlers.Count.Should().Be(1); 45 | stream.Subscribed.Should().Be(0); 46 | } 47 | } 48 | --------------------------------------------------------------------------------